Skip to content

chore: modernize tooling (Go 1.26.4, deps, renovate, golangci-lint)#42

Merged
pilat merged 1 commit into
mainfrom
chore/modernize-ci-lint-go-1.26
Jun 23, 2026
Merged

chore: modernize tooling (Go 1.26.4, deps, renovate, golangci-lint)#42
pilat merged 1 commit into
mainfrom
chore/modernize-ci-lint-go-1.26

Conversation

@pilat

@pilat pilat commented Jun 23, 2026

Copy link
Copy Markdown
Owner

What

Modernizes the dev tooling, bringing it in line with our current standard:

  • Go 1.26.4 + dependency refresh. moby/moby is intentionally held at the version docker/compose v5.1.4 supports — newer adds an APIClient method the vendored compose client doesn't implement.
  • Renovate replaces Dependabot (weekly, grouped non-major, gomodTidy).
  • golangci-lint expanded to the full generic linter set (wrapcheck, revive, gosec, gocritic, perfsprint, …) plus gofumpt/gci/golines, with all resulting findings fixed. Clean on both linux and darwin.
  • CI action bumps (checkout v7, setup-go v6 via go-version-file, goreleaser v7) plus a darwin lint pass so the platform-specific cert code gets linted too.
  • Docs: note the macOS Gatekeeper quarantine step for downloaded binaries.

Homebrew stays on the deprecated brews for now (still functional); migrating to homebrew_casks is a separate change.

Testing

Unit (-race), e2e, and lint all pass.

Summary by CodeRabbit

  • Chores

    • Upgraded Go toolchain to 1.26.4 and updated dependencies.
    • Migrated dependency management from Dependabot to Renovate.
    • Updated GitHub Actions and enhanced linter suite.
    • Added build and formatting targets to Makefile.
  • Documentation

    • Added macOS Gatekeeper quarantine removal instructions for binary installations.
  • Refactor

    • Improved error messages with contextual wrapping throughout CLI commands for better debugging.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@pilat, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 22 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 18976107-eda3-408e-93ef-1814d8efe497

📥 Commits

Reviewing files that changed from the base of the PR and between c825532 and d9604b0.

⛔ Files ignored due to path filters (5)
  • go.sum is excluded by !**/*.sum
  • internal/git/git_test.go is excluded by !**/*_test.go
  • internal/hosts/hosts_test.go is excluded by !**/*_test.go
  • internal/manager/manager_test.go is excluded by !**/*_test.go
  • tests/e2e/e2e_test.go is excluded by !**/*_test.go
📒 Files selected for processing (41)
  • .github/dependabot.yml
  • .github/renovate.json
  • .github/workflows/build-and-release.yaml
  • .github/workflows/ci.yaml
  • .golangci.yml
  • Makefile
  • README.md
  • cmd/devbox/destroy.go
  • cmd/devbox/devbox.go
  • cmd/devbox/down.go
  • cmd/devbox/env.go
  • cmd/devbox/info.go
  • cmd/devbox/init.go
  • cmd/devbox/install_ca.go
  • cmd/devbox/list.go
  • cmd/devbox/logs.go
  • cmd/devbox/mount.go
  • cmd/devbox/ps.go
  • cmd/devbox/restart.go
  • cmd/devbox/run.go
  • cmd/devbox/shell.go
  • cmd/devbox/umount.go
  • cmd/devbox/up.go
  • cmd/devbox/update.go
  • cmd/devbox/update_hosts.go
  • docs/installation.md
  • go.mod
  • internal/app/const.go
  • internal/cert/cert.go
  • internal/cert/cert_darwin.go
  • internal/cert/cert_linux.go
  • internal/git/git.go
  • internal/git/normalizer.go
  • internal/hosts/hosts.go
  • internal/manager/manager.go
  • internal/pkg/fs/fs.go
  • internal/project/config.go
  • internal/project/export.go
  • internal/project/project.go
  • internal/table/export.go
  • internal/table/table.go

Walkthrough

Replaces Dependabot with Renovate for dependency management, upgrades GitHub Actions and Go toolchain versions, significantly expands the golangci-lint linter and formatter configuration, adds Makefile build/fmt/clean targets, wraps all AutodetectProject errors with context across CLI commands, replaces unsafe type assertions in project extension parsing with checked forms, adds path-contextual error wrapping in filesystem operations, and normalizes octal permission literals throughout.

Changes

Linter and CI Infrastructure

Layer / File(s) Summary
Renovate, Actions, and Go toolchain upgrades
.github/renovate.json, .github/workflows/build-and-release.yaml, .github/workflows/ci.yaml, go.mod
Removes .github/dependabot.yml, adds Renovate with weekly schedule and gomodTidy. Upgrades checkout/setup-go/goreleaser/golangci-lint action versions, switches all jobs to go-version-file: go.mod, adds a Darwin lint step. Bumps go.mod to Go 1.26.4 and upgrades several direct and indirect dependencies.
golangci-lint linter suite and formatter config
.golangci.yml
Expands enabled linters from a small set to a comprehensive suite including wrapcheck, revive, gocritic, gosec, perfsprint, nilnil, etc. Adds per-linter settings blocks. Broadens issue exclusion rules for test files. Switches formatters from gofmt/goimports to gofumpt/gci/golines with section configuration.
Makefile build, fmt, and clean targets
Makefile
Adds build (compiles devbox binary), fmt (golangci-lint fmt), and clean (rm -f devbox) phony targets; changes lint to pass ./... explicitly.

Error Wrapping, Safe Type Assertions, and Code Correctness

Layer / File(s) Summary
CLI binName constant and root command wiring
cmd/devbox/devbox.go, cmd/devbox/up.go
Introduces binName/nameFlag constants, uses binName for root.Use, wraps root.Execute() errors. Fixes up.go sudo invocations for install-ca and update-hosts to use binName/nameFlag instead of hardcoded string literals.
AutodetectProject error wrapping across all CLI commands
cmd/devbox/destroy.go, cmd/devbox/down.go, cmd/devbox/env.go, cmd/devbox/info.go, cmd/devbox/init.go, cmd/devbox/install_ca.go, cmd/devbox/list.go, cmd/devbox/logs.go, cmd/devbox/mount.go, cmd/devbox/ps.go, cmd/devbox/restart.go, cmd/devbox/run.go, cmd/devbox/shell.go, cmd/devbox/umount.go, cmd/devbox/update.go, cmd/devbox/update_hosts.go
All RunE handlers now wrap mgr.AutodetectProject failures with "failed to detect project: %w". restart.go extracts a standalone suggestRunningServices completion function. shell.go switches to errors.New for static errors and named return values in containerExec. run.go preallocates the commands slice. Import blocks and ValidArgsFunction bodies are reformatted throughout.
Manager autodetect and source detection error handling
internal/manager/manager.go
Switches static error messages from fmt.Errorf to errors.New across AutodetectProject, detectExplicitSource, and detectSourceByGitRemote. isSourceInWrongState unmount branch now uses filepath.Rel to normalize paths before checking proj.LocalMounts. Manager.load wraps project.New failures. Init uses 0o644 octal literal.
Project extension type assertions and state serialization
internal/project/project.go, internal/project/config.go, internal/project/export.go
applySources, applyScenarios, applyHosts, applyCert, and setupGracePeriod use checked type assertions, returning "unexpected type %T" errors on mismatch. SaveState uses 0o644. Config and export type/var declarations grouped into blocks.
Certificate generation error wrapping and octal literals
internal/cert/cert.go, internal/cert/cert_linux.go, internal/cert/cert_darwin.go
generateCertificate uses named returns and wraps key/certificate creation errors with fmt.Errorf. decodePEM switches static failures to errors.New. File writes throughout use 0o644/0o755 octal literals. Linux unsupported-distro error switches to errors.New.
Filesystem error wrapping, git path construction, and hosts errors
internal/pkg/fs/fs.go, internal/git/git.go, internal/git/normalizer.go, internal/hosts/hosts.go, internal/app/const.go, internal/table/export.go, internal/table/table.go
OSFileSystem methods return fmt.Errorf-wrapped errors with path context. git.go uses filepath.Join for .git/info/exclude and index lock paths. hosts.go switches to errors.New. Miscellaneous declaration grouping and formatting.
macOS Gatekeeper installation docs
README.md, docs/installation.md
Adds a note explaining Gatekeeper quarantine for unsigned binaries and the xattr -dr com.apple.quarantine ./devbox command.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • pilat/devbox#24: CLI commands now wrap mgr.AutodetectProject failures with contextual fmt.Errorf, directly building on the AutodetectProject logic refactored in that PR.
  • pilat/devbox#41: cmd/devbox/shell.go's filterLabels string concatenation change overlaps with the Docker Compose v5 migration work touching the same function in that PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main modernization efforts: Go version bump, dependency updates, Renovate migration, and golangci-lint expansion.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/modernize-ci-lint-go-1.26

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pilat pilat self-assigned this Jun 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/devbox/shell.go (1)

121-154: 🩺 Stability & Availability | 🟠 Major

Fix error propagation and goroutine leak in containerExec.

The function has two bugs:

  1. Dropped error from StdCopy: The case <-done: branch receives the error from the goroutine but discards it, always returning nil instead of propagating failures from copying output streams.

  2. Goroutine leak on context cancellation: The unbuffered done channel causes the goroutine to block indefinitely when ctx.Done() fires first—the goroutine will attempt to send on done with no receiver, blocking until the process terminates.

Both issues prevent proper error handling and can cause resource leaks. The fix makes done buffered, captures the copy error, and propagates it correctly:

Proposed fix
 func containerExec(ctx context.Context, containerID string, cmd []string) (stdoutBytes, stderrBytes []byte, err error) {
@@
-	done := make(chan error)
+	done := make(chan error, 1)
 
 	go func() {
-		_, err = stdcopy.StdCopy(&stdout, &stderr, execAttachResp.Reader)
-		done <- err
+		_, copyErr := stdcopy.StdCopy(&stdout, &stderr, execAttachResp.Reader)
+		done <- copyErr
 	}()
 
 	select {
-	case <-done:
-		break
+	case copyErr := <-done:
+		if copyErr != nil {
+			return nil, nil, fmt.Errorf("failed to read exec output: %w", copyErr)
+		}
 	case <-ctx.Done():
-		return nil, nil, errors.New("context cancelled")
+		return nil, nil, fmt.Errorf("context cancelled: %w", ctx.Err())
 	}
 
 	return stdout.Bytes(), stderr.Bytes(), nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/devbox/shell.go` around lines 121 - 154, The containerExec function has
two bugs that need fixing. First, the unbuffered done channel causes a goroutine
leak when ctx.Done() fires before the goroutine finishes—change the done channel
declaration to be buffered with capacity 1. Second, the case <-done: branch in
the select statement receives the error from the goroutine but discards it and
always returns nil—capture the error value from the done channel and return it
properly instead of returning nil for the error value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-and-release.yaml:
- Around line 20-29: Replace the mutable version tags with immutable full commit
SHAs for all three GitHub Actions in the release workflow. Change
actions/checkout@v7 to pin to its specific commit SHA, actions/setup-go@v6 to
its commit SHA, and goreleaser/goreleaser-action@v7 to its commit SHA. This
prevents supply-chain attacks where tag reassignment could inject malicious code
into your release pipeline that has elevated permissions and access to
production secrets. Obtain the exact commit SHAs from the respective GitHub
repositories and use them in the uses field instead of the version tags.

In @.github/workflows/ci.yaml:
- Line 18: The checkout action steps are not disabling credential persistence,
which keeps the GITHUB_TOKEN in git config across later steps unnecessarily. Add
the `persist-credentials: false` parameter to both checkout action steps (at
line 18 and line 44 where `actions/checkout@v7` is used) to prevent the GitHub
token from being persisted in the git configuration across subsequent workflow
steps.
- Around line 18-20: Replace all mutable version tags for GitHub Actions with
immutable commit SHAs in the CI workflow file. Update the uses statements for
actions/checkout, actions/setup-go, and golangci/golangci-lint-action to use
their specific commit hashes instead of version tags (like `@v7`, `@v6`, `@v9`). Each
action reference should be changed from the format "action@vX" to
"action@<commit-sha>" with a comment noting the version it corresponds to for
reference.

In @.golangci.yml:
- Around line 81-87: Add explanatory comments to the gosec rule exclusions in
the excludes section to document the rationale for disabling each security
check. For each excluded rule (G104, G204, G301, G302, G304, G306), include a
comment explaining why it is excluded, such as whether it's due to false
positive rate, intentional audit-only concerns, or project-specific permission
models. This preserves security oversight and clarifies the intent behind these
exclusions to future reviewers.

In `@internal/cert/cert.go`:
- Around line 136-137: Private key files are being written with overly
permissive file mode 0o644, which allows non-owner users to read sensitive key
material on multi-user systems. Change the file permission mode from 0o644 to
0o600 for both occurrences where private key files are written: the os.WriteFile
call for clientKeyPEM and the similar write operation at line 202 for the other
private key file. This restricts read and write access to the owner only, as
required for secure handling of cryptographic key material.

In `@internal/manager/manager.go`:
- Around line 388-399: The string prefix matching in the condition
`strings.HasPrefix(servicePath, sourcePrefix)` at line 388 lacks path-boundary
awareness and can incorrectly match sibling paths (for example, `/sources/api`
would match `/sources/api-gateway`). Replace this raw string prefix check with
path-boundary-aware matching by verifying that after the prefix match, the next
character is a path separator (or the strings are equal), ensuring only actual
sub-paths under the source directory are matched rather than paths that merely
start with the same string. Use the filepath package or add an explicit boundary
check after the prefix match.

In `@internal/project/project.go`:
- Around line 235-239: The code parses an IP address using net.ParseIP(item.IP)
but fails to check if the result is nil before calling ip.String() on line 238,
allowing invalid IPs to be converted to the string "<nil>" and propagated into
the ipToHosts map. Add a nil check after the net.ParseIP call and handle the
invalid IP case appropriately by either returning an error to fail fast or
skipping the invalid entry, ensuring that only valid parsed IPs are normalized
and assigned to item.IP.

---

Outside diff comments:
In `@cmd/devbox/shell.go`:
- Around line 121-154: The containerExec function has two bugs that need fixing.
First, the unbuffered done channel causes a goroutine leak when ctx.Done() fires
before the goroutine finishes—change the done channel declaration to be buffered
with capacity 1. Second, the case <-done: branch in the select statement
receives the error from the goroutine but discards it and always returns
nil—capture the error value from the done channel and return it properly instead
of returning nil for the error value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4bd14500-9ac9-4aec-af03-8c458628d689

📥 Commits

Reviewing files that changed from the base of the PR and between a2cad8e and c825532.

⛔ Files ignored due to path filters (5)
  • go.sum is excluded by !**/*.sum
  • internal/git/git_test.go is excluded by !**/*_test.go
  • internal/hosts/hosts_test.go is excluded by !**/*_test.go
  • internal/manager/manager_test.go is excluded by !**/*_test.go
  • tests/e2e/e2e_test.go is excluded by !**/*_test.go
📒 Files selected for processing (41)
  • .github/dependabot.yml
  • .github/renovate.json
  • .github/workflows/build-and-release.yaml
  • .github/workflows/ci.yaml
  • .golangci.yml
  • Makefile
  • README.md
  • cmd/devbox/destroy.go
  • cmd/devbox/devbox.go
  • cmd/devbox/down.go
  • cmd/devbox/env.go
  • cmd/devbox/info.go
  • cmd/devbox/init.go
  • cmd/devbox/install_ca.go
  • cmd/devbox/list.go
  • cmd/devbox/logs.go
  • cmd/devbox/mount.go
  • cmd/devbox/ps.go
  • cmd/devbox/restart.go
  • cmd/devbox/run.go
  • cmd/devbox/shell.go
  • cmd/devbox/umount.go
  • cmd/devbox/up.go
  • cmd/devbox/update.go
  • cmd/devbox/update_hosts.go
  • docs/installation.md
  • go.mod
  • internal/app/const.go
  • internal/cert/cert.go
  • internal/cert/cert_darwin.go
  • internal/cert/cert_linux.go
  • internal/git/git.go
  • internal/git/normalizer.go
  • internal/hosts/hosts.go
  • internal/manager/manager.go
  • internal/pkg/fs/fs.go
  • internal/project/config.go
  • internal/project/export.go
  • internal/project/project.go
  • internal/table/export.go
  • internal/table/table.go
💤 Files with no reviewable changes (2)
  • .github/dependabot.yml
  • internal/table/table.go

Comment thread .github/workflows/build-and-release.yaml
Comment thread .github/workflows/ci.yaml
Comment thread .github/workflows/ci.yaml
Comment thread .golangci.yml
Comment thread internal/cert/cert.go
Comment thread internal/manager/manager.go
Comment thread internal/project/project.go
@pilat pilat force-pushed the chore/modernize-ci-lint-go-1.26 branch from c825532 to d9604b0 Compare June 23, 2026 01:31
@pilat pilat merged commit 3c382e1 into main Jun 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant