Skip to content

Commit 24623a4

Browse files
authored
Merge branch 'master' into codex/plan-lets-system-config-support
2 parents 22a5eed + 7e4e9ac commit 24623a4

19 files changed

Lines changed: 864 additions & 164 deletions

docs/docs/changelog.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ title: Changelog
1111
* `[Added]` Expose `LETS_OS` and `LETS_ARCH` environment variables at command runtime.
1212
* `[Removed]` Drop deprecated `eval_env` directive. Use `env` with `sh` execution mode instead.
1313
* `[Added]` When a command or its `depends` chain fails, print an indented tree to stderr showing the full chain with the failing command highlighted
14-
* `[Changed]` Format command failure output as a `lets:`-prefixed tree plus a separate final status line such as `lets: exit status 1`.
14+
* `[Changed]` Format command failure output as a logger-prefixed `command failed:` block followed by the dependency tree, while preserving the final status line such as `lets: exit status 1`.
1515
* `[Added]` Support `env_file` in global config and commands. File names are expanded after `env` is resolved, and values loaded from env files override values from `env`.
1616
* `[Changed]` Migrate the LSP YAML parser from the CGO-based tree-sitter bindings to pure-Go [`gotreesitter`](https://github.com/odvcencio/gotreesitter), removing the C toolchain requirement from normal builds and release packaging.
1717
* `[Refactoring]` Move CLI startup flow from `cmd/lets/main.go` into `internal/cli/cli.go`, keeping `main.go` as a thin launcher.
1818
* `[Added]` Add `lets self doc` command to open the online documentation in a browser.
1919
* `[Added]` Show background update notifications for interactive sessions, with Homebrew-aware guidance and `LETS_CHECK_UPDATE` opt-out.
2020
* `[Changed]` Centralize the `lets:` log prefix in the formatter and render debug messages in blue.
2121
* `[Added]` Add user settings in `~/.config/lets/config.yaml` for lets behavior such as `no_color` and `upgrade_notify`, with env variables still taking precedence.
22+
* `[Fixed]` Resolve `go to definition` from YAML merge aliases such as `<<: *test` to the referenced command in `lets self lsp`.
23+
* `[Added]` Load local mixin files into LSP storage and command index so mixin commands are available for navigation.
2224

2325
## [0.0.59](https://github.com/lets-cli/lets/releases/tag/v0.0.59)
2426

examples/python/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
aiohttp==3.7.4
1+
aiohttp==3.13.4
22

33
ipython==8.10.0

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ require (
99
github.com/coreos/go-semver v0.3.1
1010
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
1111
github.com/fatih/color v1.16.0
12-
github.com/odvcencio/gotreesitter v0.9.2
12+
github.com/mattn/go-isatty v0.0.20
13+
github.com/odvcencio/gotreesitter v0.12.1
1314
github.com/pkg/errors v0.9.1
1415
github.com/sirupsen/logrus v1.9.3
1516
github.com/spf13/cobra v1.10.2
@@ -24,7 +25,6 @@ require (
2425
github.com/iancoleman/strcase v0.3.0 // indirect
2526
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
2627
github.com/mattn/go-colorable v0.1.13 // indirect
27-
github.com/mattn/go-isatty v0.0.20 // indirect
2828
github.com/mattn/go-runewidth v0.0.14 // indirect
2929
github.com/muesli/termenv v0.15.2 // indirect
3030
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1n
7979
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
8080
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
8181
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
82-
github.com/odvcencio/gotreesitter v0.9.2 h1:ZROpRS+bTcC1mwofBp53l66Jv00FH0ccViSwGVmaBBM=
83-
github.com/odvcencio/gotreesitter v0.9.2/go.mod h1:Sx+iYJBfw5xSWkSttLSuFvguJctlH+ma1BTxZ0MPCqo=
82+
github.com/odvcencio/gotreesitter v0.12.1 h1:bJI2ZH6K/A8Vdqqa+ilA6+V82JQ3wyCRlHsFEIKTSIY=
83+
github.com/odvcencio/gotreesitter v0.12.1/go.mod h1:Sx+iYJBfw5xSWkSttLSuFvguJctlH+ma1BTxZ0MPCqo=
8484
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
8585
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
8686
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

internal/cli/cli.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ func Main(version string, buildDate string) int {
143143
if err := rootCmd.ExecuteContext(ctx); err != nil {
144144
var depErr *executor.DependencyError
145145
if errors.As(err, &depErr) {
146-
executor.PrintDependencyTree(depErr, os.Stderr)
146+
log.Errorf("%s", depErr.TreeMessage())
147147
log.Errorf("%s", depErr.FailureMessage())
148+
148149
return getExitCode(err, 1)
149150
}
150151

internal/executor/dependency_error.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ import (
99
"github.com/fatih/color"
1010
)
1111

12+
const dependencyTreeIndent = " "
13+
const dependencyTreeHeader = "command failed:"
14+
const dependencyTreeJoint = "└─ "
15+
1216
// DependencyError carries the full dependency chain when a command fails.
1317
// Chain is outermost-first (e.g., ["deploy", "build", "lint"]).
1418
type DependencyError struct {
1519
Chain []string
1620
Err error
1721
}
1822

19-
const treePrefix = "lets: "
20-
2123
func (e *DependencyError) Error() string { return e.Err.Error() }
2224
func (e *DependencyError) Unwrap() error { return e.Err }
2325

@@ -40,6 +42,28 @@ func (e *DependencyError) FailureMessage() string {
4042
return e.Err.Error()
4143
}
4244

45+
func (e *DependencyError) TreeMessage() string {
46+
red := color.New(color.FgRed).SprintFunc()
47+
48+
var builder strings.Builder
49+
50+
builder.WriteString(dependencyTreeHeader)
51+
52+
for i, name := range e.Chain {
53+
builder.WriteByte('\n')
54+
builder.WriteString(strings.Repeat(dependencyTreeIndent, i+1))
55+
builder.WriteString(dependencyTreeJoint)
56+
builder.WriteString(name)
57+
58+
if i == len(e.Chain)-1 {
59+
builder.WriteString(dependencyTreeIndent)
60+
builder.WriteString(red("<-- failed here"))
61+
}
62+
}
63+
64+
return builder.String()
65+
}
66+
4367
// prependToChain prepends name to the chain in err if err is already a *DependencyError,
4468
// otherwise wraps err in a new single-element DependencyError.
4569
func prependToChain(name string, err error) error {
@@ -55,19 +79,5 @@ func prependToChain(name string, err error) error {
5579
// The failing node (last in chain) is annotated in red.
5680
// Respects NO_COLOR automatically via fatih/color.
5781
func PrintDependencyTree(e *DependencyError, w io.Writer) {
58-
red := color.New(color.FgRed).SprintFunc()
59-
treeIndent := strings.Repeat(" ", len(treePrefix))
60-
61-
for i, name := range e.Chain {
62-
indent := treeIndent + strings.Repeat(" ", i+1)
63-
if i == 0 {
64-
indent = treePrefix
65-
}
66-
67-
if i == len(e.Chain)-1 {
68-
fmt.Fprintf(w, "%s%s %s\n", indent, name, red("<-- failed here"))
69-
} else {
70-
fmt.Fprintf(w, "%s%s\n", indent, name)
71-
}
72-
}
82+
fmt.Fprintln(w, e.TreeMessage())
7383
}

internal/executor/dependency_error_test.go

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,18 @@ func TestPrintDependencyTree(t *testing.T) {
150150
PrintDependencyTree(depErr, &buf)
151151
out := buf.String()
152152
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
153-
if len(lines) != 1 {
154-
t.Fatalf("expected 1 line, got %d: %v", len(lines), lines)
153+
if len(lines) != 2 {
154+
t.Fatalf("expected 2 lines, got %d: %v", len(lines), lines)
155155
}
156-
if !strings.HasPrefix(lines[0], "lets: lint") {
157-
t.Errorf("expected line to start with 'lets: lint', got: %q", lines[0])
156+
want := []string{
157+
dependencyTreeHeader,
158+
dependencyTreeIndent + dependencyTreeJoint + "lint" + dependencyTreeIndent + "<-- failed here",
158159
}
159-
if !strings.Contains(out, "failed here") {
160-
t.Errorf("expected 'failed here' annotation on lint line, got: %q", out)
160+
161+
for i := range want {
162+
if lines[i] != want[i] {
163+
t.Errorf("line %d: want %q, got %q", i, want[i], lines[i])
164+
}
161165
}
162166
})
163167

@@ -170,28 +174,20 @@ func TestPrintDependencyTree(t *testing.T) {
170174
PrintDependencyTree(depErr, &buf)
171175
lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n")
172176

173-
if len(lines) != 3 {
174-
t.Fatalf("expected 3 lines, got %d: %v", len(lines), lines)
175-
}
176-
// index 0 = 2 spaces, index 1 = 4 spaces, index 2 = 6 spaces (outermost first)
177-
checks := []struct {
178-
prefix string
179-
name string
180-
hasFailed bool
181-
}{
182-
{"lets: ", "deploy", false},
183-
{" ", "build", false},
184-
{" ", "lint", true},
185-
}
186-
for i, c := range checks {
187-
if !strings.HasPrefix(lines[i], c.prefix+c.name) {
188-
t.Errorf("line %d: want prefix %q + name %q, got %q", i, c.prefix, c.name, lines[i])
189-
}
190-
if c.hasFailed && !strings.Contains(lines[i], "failed here") {
191-
t.Errorf("line %d: expected 'failed here' annotation, got %q", i, lines[i])
192-
}
193-
if !c.hasFailed && strings.Contains(lines[i], "failed here") {
194-
t.Errorf("line %d: unexpected 'failed here' annotation on non-failing node, got %q", i, lines[i])
177+
if len(lines) != 4 {
178+
t.Fatalf("expected 4 lines, got %d: %v", len(lines), lines)
179+
}
180+
want := []string{
181+
dependencyTreeHeader,
182+
dependencyTreeIndent + dependencyTreeJoint + "deploy",
183+
strings.Repeat(dependencyTreeIndent, 2) + dependencyTreeJoint + "build",
184+
strings.Repeat(dependencyTreeIndent, 3) + dependencyTreeJoint + "lint" +
185+
dependencyTreeIndent + "<-- failed here",
186+
}
187+
188+
for i := range want {
189+
if lines[i] != want[i] {
190+
t.Errorf("line %d: want %q, got %q", i, want[i], lines[i])
195191
}
196192
}
197193
})

0 commit comments

Comments
 (0)