Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## UNRELEASED

IMPROVEMENTS:
* cli: Improved error message when pack lacks `.nomad.tpl` files to clearly explain naming requirements, show template naming convention, and list found template files [[GH-510](https://github.com/hashicorp/nomad-pack/pull/831)]
* cli: Add registry now honors default main/master branch [[GH-843](https://github.com/hashicorp/nomad-pack/pull/843)]

## 0.4.2 (March 16, 2026)
Expand Down
25 changes: 25 additions & 0 deletions internal/cli/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cli
import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/hashicorp/nomad/api"
Expand Down Expand Up @@ -557,3 +558,27 @@ func limit(s string, length int) string {

return s[:length]
}

// addNoParentTemplatesContext adds error details for missing parent templates
// to an existing error context. It lists any .tpl files discovered and provides
// naming guidance.
func addNoParentTemplatesContext(errorContext *errors.UIErrorContext, packPath string) {
errorContext.Add(errors.UIContextErrorDetail, "No parent templates (*.nomad.tpl files) were found in the pack")
errorContext.Add(errors.UIContextErrorSuggestion, "Parent templates must end with .nomad.tpl (e.g., app.nomad.tpl). Helper templates should start with _ (e.g., _helpers.tpl)")

// list found template files
templatesPath := filepath.Join(packPath, "templates")
if entries, err := os.ReadDir(templatesPath); err == nil {
var templateFiles []string
for _, entry := range entries {
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".tpl") {
templateFiles = append(templateFiles, entry.Name())
}
}
if len(templateFiles) > 0 {
errorContext.Add("Found Templates: ", strings.Join(templateFiles, ", "))
} else {
errorContext.Add("Found Templates: ", "none")
}
}
}
37 changes: 37 additions & 0 deletions internal/cli/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import (
"encoding/json"
"os"
"path"
"path/filepath"
"strings"
"testing"

"github.com/posener/complete"
"github.com/shoenig/test/must"

"github.com/hashicorp/nomad-pack/internal/pkg/caching"
"github.com/hashicorp/nomad-pack/internal/pkg/errors"
"github.com/hashicorp/nomad-pack/internal/pkg/helper/filesystem"
"github.com/hashicorp/nomad-pack/internal/pkg/logging"
"github.com/hashicorp/nomad-pack/internal/pkg/testfixture"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestExtractFlagValue(t *testing.T) {
Expand Down Expand Up @@ -131,3 +136,35 @@ func assertPredictorResults(t *testing.T, got, expected []string) {
must.Eq(t, len(expected), len(got))
must.SliceContainsAll(t, expected, got)
}

func TestAddNoParentTemplatesContext(t *testing.T) {
// create a temporary test directory with template files
tmpDir := t.TempDir()
templatesDir := filepath.Join(tmpDir, "templates")
err := os.MkdirAll(templatesDir, 0755)
require.NoError(t, err)

//create test template files
testFiles := []string{"test.tpl", "_helpers.tpl", "config.tpl"}
for _, file := range testFiles {
err := os.WriteFile(filepath.Join(templatesDir, file), []byte("test"), 0644)
require.NoError(t, err)
}

// build error context
ctx := errors.NewUIErrorContext()
addNoParentTemplatesContext(ctx, tmpDir)
require.NotNil(t, ctx)

// get all context entries
entries := ctx.GetAll()
require.NotEmpty(t, entries)

//verify expected content
contextStr := strings.Join(entries, " ")
assert.Contains(t, contextStr, "No parent templates")
assert.Contains(t, contextStr, "*.nomad.tpl")
assert.Contains(t, contextStr, "test.tpl")
assert.Contains(t, contextStr, "_helpers.tpl")
assert.Contains(t, contextStr, "config.tpl")
}
5 changes: 2 additions & 3 deletions internal/cli/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ func (c *PlanCommand) Run(args []string) int {
return c.exitCodeError
}

// Commands that render templates are required to render at least one
// parent template.
if r.LenParentRenders() < 1 {
c.ui.ErrorWithContext(errors.ErrNoTemplatesRendered, "no templates rendered", errorContext.GetAll()...)
addNoParentTemplatesContext(errorContext, c.packConfig.Path)
c.ui.ErrorWithContext(errors.ErrNoTemplatesRendered, "no parent templates found", errorContext.GetAll()...)
return c.exitCodeError
}

Expand Down
6 changes: 6 additions & 0 deletions internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ func (c *RunCommand) run() int {
return 255
}

if r.LenParentRenders() < 1 {
addNoParentTemplatesContext(errorContext, c.packConfig.Path)
c.ui.ErrorWithContext(errors.ErrNoTemplatesRendered, "no parent templates found", errorContext.GetAll()...)
return 1
}

renderedParents := r.ParentRenders()
renderedDeps := r.DependentRenders()

Expand Down
Loading