Skip to content

chore: cleanup unkey env and add goreleaser cfg#4575

Merged
chronark merged 5 commits intomainfrom
chore/release-unkey-env-img
Jan 9, 2026
Merged

chore: cleanup unkey env and add goreleaser cfg#4575
chronark merged 5 commits intomainfrom
chore/release-unkey-env-img

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Jan 8, 2026

What does this PR do?

Fixes #4561

Also renames unkey-env to inject. I hate naming things.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test A
  • Test B

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Ran make fmt on /go directory
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@vercel
Copy link

vercel bot commented Jan 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Review Updated (UTC)
dashboard Ignored Ignored Preview Jan 9, 2026 0:42am
engineering Ignored Ignored Preview Jan 9, 2026 0:42am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

The pull request refactors the secrets injection system by renaming the "unkey-env" binary to "inject". This includes creating a new Go implementation in cmd/inject, deleting the legacy cmd/unkey-env implementation, introducing build and release configurations, and updating all dependent configurations across the preflight service, Kubernetes manifests, and infrastructure code.

Changes

Cohort / File(s) Summary
Inject Binary Implementation
cmd/inject/command.go, cmd/inject/config.go, cmd/inject/main.go, cmd/inject/run.go
New Go CLI implementation for secrets injection: command definition with flags and validation, configuration model with validation logic, entry point, and runtime execution with secret fetching, environment sanitization, and target command execution. ~226 lines of new logic.
Build Configuration
cmd/inject/Dockerfile, cmd/inject/.goreleaser.yaml, .github/workflows/release_inject.yaml
New multi-stage Dockerfile for building a compact, statically-linked inject binary with upx compression and Alpine runtime. GoReleaser configuration for multi-arch Docker builds and manifest publishing. GitHub Actions workflow for automated release on inject/* tag pushes.
Unkey-env Removal
cmd/unkey-env/main.go, cmd/unkey-env/Dockerfile, Dockerfile.unkey-env
Complete deletion of legacy unkey-env implementation (169 lines CLI code, 24 + 10 lines Dockerfile). Entire command-line workflow, config validation, and execution logic removed.
Preflight Service Configuration
cmd/preflight/main.go, svc/preflight/config.go, svc/preflight/internal/services/mutator/config.go, svc/preflight/internal/services/mutator/mutator.go, svc/preflight/internal/services/mutator/patch.go, svc/preflight/run.go
Systematic renaming of UnkeyEnvImage/UnkeyEnvImagePullPolicy to InjectImage/InjectImagePullPolicy across struct fields and constants. Updated validation logic, init container patch generation, and mount configuration. CLI flags and environment variables renamed accordingly.
Secrets Provider Updates
pkg/secrets/provider/krane_vault.go, pkg/secrets/provider/provider.go
KraneVault provider refactored: removed endpoint field, added resolveToken helper, early return for empty encrypted blobs, environment_id validation for decryption. Updated comments to reference "inject".
Deployment & Infrastructure
Tiltfile, k8s/manifests/preflight.yaml, k8s/manifests/rbac.yaml
Tiltfile build targets renamed from unkey-compile/unkey-env to build-unkey/build-inject with updated dependencies and build commands. Kubernetes manifests updated with INJECT_IMAGE and INJECT_IMAGE_PULL_POLICY environment variables. RBAC comments updated.
Documentation Updates
svc/krane/proto/krane/v1/scheduler.proto, svc/krane/proto/krane/v1/secrets.proto
Proto file inline comments updated from "unkey-env" to "inject" for clarity; no protobuf field or service definition changes.
.gitignore
.gitignore
Ignore pattern changed from /unkey-env to /inject.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 3
❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description references the linked issue (#4561) but the 'How should this be tested' section contains unspecified placeholders (Test A, Test B) and the required checklist items are all unchecked. Complete the 'How should this be tested' section with concrete testing instructions and check off relevant items from the required checklist before merging.
Out of Scope Changes check ❓ Inconclusive Beyond the required goreleaser configuration, the PR includes a comprehensive refactoring to rename unkey-env to inject across the codebase, including restructured command implementation and configuration updates. Clarify whether the rename from unkey-env to inject is within scope of issue #4561 or should be handled separately, as the issue only mentions adding goreleaser configuration.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: cleanup of unkey-env and addition of goreleaser configuration.
Linked Issues check ✅ Passed The PR successfully implements the goreleaser configuration for the inject (formerly unkey-env) Docker image with proper multi-arch builds, manifests, and release workflow automation.

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

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

@vercel vercel bot temporarily deployed to Preview – engineering January 8, 2026 21:26 Inactive
@vercel vercel bot temporarily deployed to Preview – dashboard January 8, 2026 21:28 Inactive
@Flo4604
Copy link
Member Author

Flo4604 commented Jan 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In @cmd/inject/command.go:
- Around line 10-34: The validation currently allows both Token and TokenPath or
neither; change the validation used by the command (the method that currently
checks Token != "" || TokenPath != "") to require exactly one of Token XOR
TokenPath and return a clear error if both or neither are set. Also add a check
that EnvironmentID is non-empty when SecretsBlob (secrets-blob) is provided
(analogous to how DeploymentID and Endpoint are validated), since EnvironmentID
is passed into FetchSecrets(); return an error if missing. Finally, introduce
provider-specific validation by switching on the provider flag value (compare
against provider.KraneVault and other provider constants) and enforce any
provider-specific required flags (e.g., for KraneVault ensure Endpoint and
DeploymentID are present), returning descriptive errors for missing
provider-required fields. Ensure all errors are surfaced from the command
validation path so Action/run.go (FetchSecrets) receives only valid inputs.

In @cmd/inject/config.go:
- Around line 33-46: The validate() method on config currently skips checking
for an EnvironmentID when hasSecrets() is true; add a check to the assert.All
block to require EnvironmentID (e.g., assert.NotEmpty(c.EnvironmentID,
"environment-id is required when secrets-blob is provided")) so blob decryption
always has the keyring specified; update config.validate to include this new
assertion alongside Endpoint, DeploymentID, and token/token-path checks.

In @cmd/inject/main.go:
- Around line 1-14: Add the missing import for the package that defines cmd.Run:
update the import block to include the module path for the local command package
that exposes Run (import it as cmd if it's named differently), so main can call
cmd.Run without a compile error; ensure the import path matches your module and
the package actually declares Run(ctx context.Context, args []string) error.

In @cmd/inject/run.go:
- Around line 17-44: The run function currently calls execCommand(cfg.Args,
logger) without validating cfg.Args, which can cause a panic when cfg.Args is
empty; add a defensive check in run before calling execCommand: if len(cfg.Args)
== 0 return a clear error (or fmt.Errorf) indicating missing command/arguments
so the program fails gracefully; update any tests or callers expecting panic to
expect this error instead.

In @pkg/secrets/provider/krane_vault.go:
- Around line 73-87: When reading a token from a file in
resolveToken(FetchOptions), trim the token and validate it is non-empty before
returning; if the trimmed token is empty return an error (e.g., via
assert.NotEmpty or fmt.Errorf) stating the token is required. Specifically,
after os.ReadFile in resolveToken, call strings.TrimSpace on tokenBytes, check
that the result is not empty, and only then return it; otherwise return a clear
error instead of returning an empty string.

In @svc/preflight/config.go:
- Around line 5-13: Add validation for Config.InjectImagePullPolicy so invalid
strings can't propagate: ensure the field is non-empty and matches a kube-style
allowlist (e.g., "Always", "IfNotPresent", "Never") or your chosen set.
Implement this check in a Config.Validate() method (or in the loader that
constructs Config) and return an error when the value is empty or not in the
allowlist; reference the Config type and InjectImagePullPolicy field when
locating where to add the check. Ensure callers of Config.Validate() reject
invalid configs so manifests/mutator logic never receive bad pull-policy values.
🧹 Nitpick comments (5)
cmd/inject/Dockerfile (1)

16-17: Consider testing the upx-compressed binary.

The upx --best --lzma compression is very aggressive and can occasionally cause issues with Go binaries (runtime panics, binary corruption). Ensure the compressed binary is thoroughly tested before deployment.

svc/preflight/internal/services/mutator/mutator.go (1)

133-133: Consider preserving component specificity in the log message.

The message change from "injected unkey-env for deployment %s" to "injected secrets for deployment %s" makes the log less specific. If multiple injection mechanisms are introduced in the future, this generic message could make troubleshooting more difficult.

Consider: "injected secrets via inject for deployment %s" or simply "injected inject container for deployment %s" to maintain component traceability in logs.

cmd/preflight/main.go (1)

20-23: Consider compatibility aliases + revisit latest default vs pull policy

Removing the old unkey-env-* flags/env vars is a breaking CLI/config change; if you want smoother upgrades, consider adding hidden/deprecated aliases that map to the new keys.

Also, inject:latest combined with IfNotPresent is a common footgun (stale “latest” on nodes). If “latest” is the default, consider defaulting pull policy to Always, or default the image to a versioned/fully-qualified ref.

Proposed compatibility sketch (minimal)
 Flags: []cli.Flag{
@@
 		cli.String("inject-image", "Container image for inject binary",
 			cli.Default("inject:latest"), cli.EnvVar("INJECT_IMAGE")),
 		cli.String("inject-image-pull-policy", "Image pull policy (Always, IfNotPresent, Never)",
 			cli.Default("IfNotPresent"), cli.EnvVar("INJECT_IMAGE_PULL_POLICY")),
+		// Back-compat (optional): consider hiding/deprecating these if cli supports it
+		cli.String("unkey-env-image", "DEPRECATED: use --inject-image",
+			cli.EnvVar("UNKEY_ENV_IMAGE")),
+		cli.String("unkey-env-image-pull-policy", "DEPRECATED: use --inject-image-pull-policy",
+			cli.EnvVar("UNKEY_ENV_IMAGE_PULL_POLICY")),
cmd/inject/config.go (1)

8-15: Nit: consider map[string]struct{} for set semantics

Not required, but avoids storing redundant booleans and signals intent.

cmd/inject/run.go (1)

86-99: Remove unreachable code after syscall.Exec (optional).

On success, syscall.Exec never returns, so return nil is dead code.

Proposed tweak
 	if err := syscall.Exec(binary, args, os.Environ()); err != nil {
 		return fmt.Errorf("exec failed: %w", err)
 	}
-
-	return nil
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3222c1 and b50493a.

⛔ Files ignored due to path filters (1)
  • gen/proto/krane/v1/scheduler.pb.go is excluded by !**/*.pb.go, !**/gen/**
📒 Files selected for processing (24)
  • .github/workflows/release-inject.yaml
  • .gitignore
  • Dockerfile.unkey-env
  • Tiltfile
  • cmd/inject/.goreleaser.yaml
  • cmd/inject/Dockerfile
  • cmd/inject/command.go
  • cmd/inject/config.go
  • cmd/inject/main.go
  • cmd/inject/run.go
  • cmd/preflight/main.go
  • cmd/unkey-env/Dockerfile
  • cmd/unkey-env/main.go
  • k8s/manifests/preflight.yaml
  • k8s/manifests/rbac.yaml
  • pkg/secrets/provider/krane_vault.go
  • pkg/secrets/provider/provider.go
  • svc/krane/proto/krane/v1/scheduler.proto
  • svc/krane/proto/krane/v1/secrets.proto
  • svc/preflight/config.go
  • svc/preflight/internal/services/mutator/config.go
  • svc/preflight/internal/services/mutator/mutator.go
  • svc/preflight/internal/services/mutator/patch.go
  • svc/preflight/run.go
💤 Files with no reviewable changes (3)
  • Dockerfile.unkey-env
  • cmd/unkey-env/Dockerfile
  • cmd/unkey-env/main.go
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
📚 Learning: 2025-09-24T18:57:34.843Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 4010
File: QUICKSTART-DEPLOY.md:17-17
Timestamp: 2025-09-24T18:57:34.843Z
Learning: In the Unkey deployment platform, API key environment variables use component-specific naming but share the same secret value: UNKEY_API_KEY for the ctrl service (validator), API_KEY for the CLI client, and CTRL_API_KEY for the dashboard client. The ctrl service acts as the source of truth for validation.

Applied to files:

  • k8s/manifests/preflight.yaml
  • cmd/preflight/main.go
📚 Learning: 2025-09-16T19:08:44.174Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3980
File: go/k8s/manifests/dashboard.yaml:41-42
Timestamp: 2025-09-16T19:08:44.174Z
Learning: For local development Kubernetes manifests (typically indicated by paths containing "local" or environment variables like UNKEY_REGION: "local"), hardcoded generic credentials in environment variables are acceptable for convenience. Security recommendations about using Secrets should be reserved for production or staging environments.

Applied to files:

  • k8s/manifests/preflight.yaml
📚 Learning: 2025-09-11T14:12:30.570Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3944
File: go/apps/ctrl/run.go:292-299
Timestamp: 2025-09-11T14:12:30.570Z
Learning: In go/apps/ctrl/config.go, the Validate() method already includes validation for Cloudflare API token using assert.NotEmpty() when both c.Acme.Enabled and c.Acme.Cloudflare.Enabled are true, with the error message "cloudflare API token is required when cloudflare is enabled".

Applied to files:

  • cmd/inject/config.go
  • svc/preflight/config.go
📚 Learning: 2025-10-15T10:12:40.810Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4098
File: go/proto/ctrl/v1/deployment.proto:33-36
Timestamp: 2025-10-15T10:12:40.810Z
Learning: In the Unkey codebase proto files (ctrl/v1/build.proto, ctrl/v1/deployment.proto, hydra/v1/deployment.proto), use `dockerfile_path` (not `docker_file_path`) for consistency in generated Go field names.

Applied to files:

  • svc/krane/proto/krane/v1/scheduler.proto
  • svc/preflight/internal/services/mutator/mutator.go
  • svc/preflight/internal/services/mutator/config.go
  • svc/preflight/internal/services/mutator/patch.go
  • cmd/preflight/main.go
📚 Learning: 2025-08-08T16:07:48.307Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:37-40
Timestamp: 2025-08-08T16:07:48.307Z
Learning: Repo unkeyed/unkey — pnpm immutable installs are enforced by setting the CI environment variable; any truthy value (e.g., "1" or "true") is acceptable. Do not require the literal string "true". Applies to .github/actions/setup-node/action.yaml and all workflows using pnpm install.

Applied to files:

  • .gitignore
📚 Learning: 2025-08-08T15:37:14.734Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/build.yaml:14-17
Timestamp: 2025-08-08T15:37:14.734Z
Learning: Repo: unkeyed/unkey — CI behavior: We rely on CI=true to make pnpm install behave as --frozen-lockfile. Don’t suggest adding --frozen-lockfile in .github/actions/setup-node/action.yaml or workflows like .github/workflows/build.yaml.

Applied to files:

  • .gitignore
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, oapi-codegen doesn’t support OpenAPI 3.1 union nullability; overlay.yaml must be applied before codegen. The overlay key in oapi-codegen config isn’t supported—use a pre-step (programmatic or CLI) to merge overlay into the bundled spec, then run oapi-codegen.

Applied to files:

  • Tiltfile
📚 Learning: 2025-07-15T14:45:18.920Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3564
File: go/cmd/cli/commands/deploy/flags.go:17-20
Timestamp: 2025-07-15T14:45:18.920Z
Learning: In the go/cmd/cli/commands/deploy/ directory, ogzhanolguncu prefers to keep potentially temporary features (like UNKEY_DOCKER_REGISTRY environment variable) undocumented in help text if they might be deleted in the future, to avoid documentation churn.

Applied to files:

  • cmd/preflight/main.go
🧬 Code graph analysis (4)
cmd/inject/config.go (1)
pkg/secrets/provider/provider.go (2)
  • Provider (19-26)
  • Type (11-11)
cmd/inject/command.go (1)
pkg/secrets/provider/provider.go (3)
  • KraneVault (15-15)
  • Provider (19-26)
  • Type (11-11)
svc/preflight/internal/services/mutator/patch.go (1)
svc/preflight/internal/services/mutator/mutator.go (1)
  • Mutator (28-36)
pkg/secrets/provider/krane_vault.go (1)
pkg/secrets/provider/provider.go (1)
  • FetchOptions (29-46)
🪛 actionlint (1.7.10)
.github/workflows/release-inject.yaml

17-17: label "depot-ubuntu-24.04-4" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🔇 Additional comments (26)
svc/krane/proto/krane/v1/secrets.proto (1)

8-8: Documentation update aligns with "unkey-env" → "inject" refactor.

The comment clearly identifies the new component name and its deployment context. No structural or functional changes to the service definition.

svc/krane/proto/krane/v1/scheduler.proto (1)

152-154: Documentation accurately reflects the "inject" runtime secret decryption flow.

The updated comments clearly explain how encrypted secrets are handled: the blob is set as an environment variable, and the inject binary calls DecryptSecretsBlob at runtime. No changes to the field definition or proto contract.

.gitignore (1)

36-39: Verify completeness of the rename and pattern name consistency.

There appears to be an inconsistency between the AI summary and the annotated code:

  • The summary states: "Replaced ignore pattern /unkey-env with /inject"
  • The annotated code shows: /unkey (line 36, unmarked) followed by /inject (line 37, marked with ~)

Please clarify:

  1. Was the original pattern /unkey or /unkey-env? (The code shows /unkey but the summary mentions /unkey-env)
  2. Should the /unkey entry at line 36 also be removed or replaced as part of this rename, or is it a separate, unrelated pattern?
  3. If both should be changed, why is line 36 not marked with ~ in the diff?

The goreleaser additions (dist/) at lines 38–39 look appropriate.

pkg/secrets/provider/provider.go (1)

2-2: LGTM! Documentation updated to reflect the rename.

The comment update aligns with the PR's objective to rename "unkey-env" to "inject" throughout the codebase.

svc/preflight/internal/services/mutator/config.go (2)

13-15: LGTM! Constants renamed consistently.

The internal constants have been renamed from unkeyEnv* to inject* to align with the new inject workflow.


31-32: All references to renamed fields have been successfully updated.

The search confirmed that no references to the old field names (UnkeyEnvImage, UnkeyEnvImagePullPolicy) exist in the codebase, and the new field names (InjectImage, InjectImagePullPolicy) are consistently used across all relevant files: svc/preflight/config.go, svc/preflight/internal/services/mutator/config.go, svc/preflight/internal/services/mutator/mutator.go, svc/preflight/run.go, and cmd/preflight/main.go.

cmd/inject/Dockerfile (2)

22-22: LGTM! CA certificates are properly included.

Adding CA certificates is essential for the inject binary to make HTTPS calls to secrets providers.


2-2: No action required.

Go 1.25 is the latest stable version as of January 2026 (specifically 1.25.5, released December 2, 2025). The Dockerfile is using an appropriate, current Go version.

k8s/manifests/rbac.yaml (1)

14-14: LGTM! Comment updated to reflect the rename.

The comment has been updated from "unkey-env" to "inject" to align with the new naming convention.

k8s/manifests/preflight.yaml (1)

74-77: LGTM! Clean environment variable rename.

The environment variable names have been updated from UNKEY_ENV_IMAGE and UNKEY_ENV_IMAGE_PULL_POLICY to INJECT_IMAGE and INJECT_IMAGE_PULL_POLICY, aligning with the component rename throughout the PR. The values are appropriate for local development.

svc/preflight/internal/services/mutator/mutator.go (1)

33-34: LGTM! Field renames align with component renaming.

The struct fields and constructor initialization have been correctly updated to use injectImage and injectImagePullPolicy, maintaining consistency with the broader inject naming convention.

Also applies to: 44-45

cmd/inject/command.go (1)

36-54: LGTM! Clean action handler with proper error handling.

The action function correctly:

  • Builds configuration from CLI flags
  • Validates before execution
  • Returns appropriate exit codes on validation failure
  • Delegates execution to the run() function
.github/workflows/release-inject.yaml (2)

17-17: Static analysis warning about runner label is expected.

The depot-ubuntu-24.04-4 runner is flagged as unknown by actionlint because it's a custom runner used by the Unkey organization (likely provided by Depot, as evidenced by Depot integration elsewhere in the codebase). This is acceptable for organization-specific infrastructure.

If this causes confusion for contributors, consider adding a comment in the workflow file explaining the custom runner.

Based on learnings, if this review concern warrants tracking but can be deferred, would you like me to create a GitHub issue with detailed sections (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan)?


1-51: LGTM! Well-structured release workflow with security best practices.

The workflow correctly:

  • Uses semantic version tag triggers for automated releases
  • Pins actions to specific commit SHAs for supply chain security
  • Sets appropriate permissions (contents, packages)
  • Configures concurrency control to prevent parallel releases
  • Authenticates with GHCR for Docker image publishing
  • Extracts version from tags properly
  • Uses goreleaser-pro with dedicated config
svc/preflight/run.go (1)

76-77: LGTM! Configuration field updates align with the rename.

The mutator configuration initialization has been correctly updated to use InjectImage and InjectImagePullPolicy, maintaining consistency with the field renames in the mutator package.

cmd/preflight/main.go (1)

32-41: Config wiring to Inject looks consistent*

The command → config mapping is straightforward and matches the renamed fields.

cmd/inject/.goreleaser.yaml (1)

1-57: Tag format is correct—no action needed on .Version normalization

GoReleaser v2 automatically strips the leading v from git tags. If your tag is v1.2.3, {{ .Version }} produces 1.2.3, resulting in clean GHCR tags like ghcr.io/unkeyed/inject:1.2.3-amd64 (standard Docker convention). No explicit trimming or normalization is needed.

Regarding the changelog filter: if your team uses many chore: commits, the exclusion of ^chore: may result in sparse release notes. Review your commit conventions to confirm this won't leave releases undocumented.

svc/preflight/internal/services/mutator/patch.go (3)

25-34: Volume rename to injectVolumeName looks consistent.


48-121: Ensure injectBinary points into the mounted volume path.

You set container command to injectBinary (Line 112) and mount the EmptyDir at injectMountPath (Line 50). If injectBinary is not located under injectMountPath, the exec will fail in the mutated container.

This is especially important given the init-container copy step.


10-22: No action required. The code is correct and does not have a mount shadowing bug.

The init container copies from /inject (image filesystem) to /inject-bin/inject (destination in the shared EmptyDir volume). These are different paths—/inject is not masked by the volume mount at /inject-bin. The pattern is sound: the source path on the image and the destination path in the shared volume are properly separated.

Likely an incorrect or invalid review comment.

cmd/inject/run.go (2)

46-75: Timeout + error wrapping look good for secret fetch.


77-84: Env sanitization approach is reasonable; ensure allowlist is intentional.

Tiltfile (4)

21-23: Suppressing inject:latest warnings matches the new injected image name.


285-292: build-inject local_resource wiring looks consistent with inject:latest.


307-309: Preflight deps include build-inject (good).


151-160: No stale references found after renaming to build-unkey.

The codebase contains no references to unkey-compile or unkey-env, confirming the rename is clean.

@Flo4604
Copy link
Member Author

Flo4604 commented Jan 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @svc/preflight/config.go:
- Line 26: In Validate(), treat an empty InjectImagePullPolicy as a valid
default (mirror HttpPort behavior) by assigning a sensible default value before
checking the validImagePullPolicies map (e.g., set c.InjectImagePullPolicy =
"IfNotPresent" when it is ""), and also add an explicit check that c.InjectImage
is non-empty (return assert.True/False with a clear message) so callers that
don't go through the CLI still pass validation; update the check using the
validImagePullPolicies[c.InjectImagePullPolicy] lookup after the defaulting.
Ensure you modify the Validate method that references validImagePullPolicies and
the fields InjectImagePullPolicy and InjectImage.
- Around line 12-19: Validate() currently doesn't ensure TLSCertFile and
TLSKeyFile are set even though preflight.Run calls tls.NewFromFiles() and
cmd/preflight/main.go uses cmd.RequireString(); update the Validate() method to
check TLSCertFile and TLSKeyFile are non-empty and return a descriptive error
(or aggregate error) if either is missing so misconfiguration is caught early
before preflight.Run executes.
🧹 Nitpick comments (5)
svc/preflight/config.go (1)

5-9: Treat validImagePullPolicies as an immutable set (avoid bool-map footguns).
map[string]bool works, but it’s easy to accidentally “validate” unknown keys by setting them elsewhere. Consider using map[string]struct{} and _, ok := ... in Validate.

Proposed refactor
-var validImagePullPolicies = map[string]bool{
-	"Always":       true,
-	"IfNotPresent": true,
-	"Never":        true,
-}
+var validImagePullPolicies = map[string]struct{}{
+	"Always":       {},
+	"IfNotPresent": {},
+	"Never":        {},
+}
cmd/preflight/main.go (1)

20-23: Breaking change: flag/env rename—consider a compatibility alias (even if hidden) for one release.
If existing users are on UNKEY_ENV_* / unkey-env-*, this will break upgrades unless all manifests/helm/terraform are updated in lockstep. If you want a soft migration, you can accept both and prefer the new value when both are set.

pkg/secrets/provider/krane_vault.go (1)

44-46: Consider returning nil for consistency.

Returning nil instead of an empty map would be more idiomatic when no secrets are present, though the current approach is also valid and prevents potential nil pointer issues downstream.

♻️ Optional refactor
 if len(opts.Encrypted) == 0 {
-  return make(map[string]string), nil
+  return nil, nil
 }
cmd/inject/run.go (2)

18-20: Redundant validation check.

This check is redundant since config.validate() already ensures Args is non-empty. Consider removing this for cleaner code.

♻️ Optional refactor
 func run(ctx context.Context, cfg config) error {
-  if len(cfg.Args) == 0 {
-    return fmt.Errorf("no command specified")
-  }
-
   logger := logging.New()

90-103: Unreachable return statement.

Line 102's return nil is unreachable because syscall.Exec replaces the current process on success and only returns on failure (which is handled on line 99). While harmless, removing it would make the control flow clearer.

♻️ Optional refactor
   if err := syscall.Exec(binary, args, os.Environ()); err != nil {
     return fmt.Errorf("exec failed: %w", err)
   }
-
-  return nil
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b50493a and 6b7ae26.

📒 Files selected for processing (6)
  • .github/workflows/release_inject.yaml
  • cmd/inject/config.go
  • cmd/inject/run.go
  • cmd/preflight/main.go
  • pkg/secrets/provider/krane_vault.go
  • svc/preflight/config.go
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.
📚 Learning: 2025-10-15T10:12:40.810Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4098
File: go/proto/ctrl/v1/deployment.proto:33-36
Timestamp: 2025-10-15T10:12:40.810Z
Learning: In the Unkey codebase proto files (ctrl/v1/build.proto, ctrl/v1/deployment.proto, hydra/v1/deployment.proto), use `dockerfile_path` (not `docker_file_path`) for consistency in generated Go field names.

Applied to files:

  • cmd/preflight/main.go
📚 Learning: 2025-09-24T18:57:34.843Z
Learnt from: mcstepp
Repo: unkeyed/unkey PR: 4010
File: QUICKSTART-DEPLOY.md:17-17
Timestamp: 2025-09-24T18:57:34.843Z
Learning: In the Unkey deployment platform, API key environment variables use component-specific naming but share the same secret value: UNKEY_API_KEY for the ctrl service (validator), API_KEY for the CLI client, and CTRL_API_KEY for the dashboard client. The ctrl service acts as the source of truth for validation.

Applied to files:

  • cmd/preflight/main.go
📚 Learning: 2025-07-15T14:45:18.920Z
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3564
File: go/cmd/cli/commands/deploy/flags.go:17-20
Timestamp: 2025-07-15T14:45:18.920Z
Learning: In the go/cmd/cli/commands/deploy/ directory, ogzhanolguncu prefers to keep potentially temporary features (like UNKEY_DOCKER_REGISTRY environment variable) undocumented in help text if they might be deleted in the future, to avoid documentation churn.

Applied to files:

  • cmd/preflight/main.go
📚 Learning: 2025-09-11T14:12:30.570Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3944
File: go/apps/ctrl/run.go:292-299
Timestamp: 2025-09-11T14:12:30.570Z
Learning: In go/apps/ctrl/config.go, the Validate() method already includes validation for Cloudflare API token using assert.NotEmpty() when both c.Acme.Enabled and c.Acme.Cloudflare.Enabled are true, with the error message "cloudflare API token is required when cloudflare is enabled".

Applied to files:

  • pkg/secrets/provider/krane_vault.go
  • svc/preflight/config.go
  • cmd/inject/config.go
📚 Learning: 2025-09-11T14:12:30.570Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3944
File: go/apps/ctrl/run.go:292-299
Timestamp: 2025-09-11T14:12:30.570Z
Learning: In go/apps/ctrl/config.go, the Validate() method already includes validation for Cloudflare API token when ACME and Cloudflare are both enabled, checking that cfg.Acme.Cloudflare.ApiToken is not empty.

Applied to files:

  • pkg/secrets/provider/krane_vault.go
  • svc/preflight/config.go
📚 Learning: 2025-08-08T14:59:52.283Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.

Applied to files:

  • pkg/secrets/provider/krane_vault.go
📚 Learning: 2025-08-08T15:10:46.436Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: For repo unkeyed/unkey and PR review workflows: When imeyer comments "issue" on a thread, automatically create a thorough GitHub issue (sections: Summary, Impact, Where, Repro/Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and the specific comment, and assign the issue to imeyer.

Applied to files:

  • pkg/secrets/provider/krane_vault.go
📚 Learning: 2025-08-08T15:10:46.436Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.

Applied to files:

  • pkg/secrets/provider/krane_vault.go
📚 Learning: 2025-08-08T16:10:00.224Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.

Applied to files:

  • pkg/secrets/provider/krane_vault.go
🧬 Code graph analysis (3)
pkg/secrets/provider/krane_vault.go (1)
pkg/secrets/provider/provider.go (1)
  • FetchOptions (29-46)
cmd/inject/run.go (1)
pkg/secrets/provider/provider.go (5)
  • New (61-68)
  • Config (49-55)
  • Type (11-11)
  • Provider (19-26)
  • FetchOptions (29-46)
cmd/inject/config.go (1)
pkg/secrets/provider/provider.go (3)
  • Provider (19-26)
  • Type (11-11)
  • KraneVault (15-15)
🪛 actionlint (1.7.10)
.github/workflows/release_inject.yaml

17-17: label "depot-ubuntu-24.04-4" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🔇 Additional comments (13)
cmd/preflight/main.go (1)

34-41: Config wiring is consistent; consider using RequireString for inject-image if you ever drop the default.
With a default, cmd.String(...) is fine. If the image must always be explicit in prod, making it required at the CLI layer would catch misconfig earlier.

.github/workflows/release_inject.yaml (2)

1-51: LGTM - Well-structured release workflow.

The workflow follows GoReleaser best practices with appropriate permissions, concurrency control, and Docker image building setup. The tag pattern and version extraction logic align with the inject command structure.


17-17: No action needed. The runner label depot-ubuntu-24.04-4 is correctly configured. It is consistently used across 19 workflow files throughout the project, including critical test and deployment jobs, confirming the project has access to these Depot.dev runners.

pkg/secrets/provider/krane_vault.go (4)

19-35: LGTM - Cleaner struct design.

Removing the endpoint field from the struct is appropriate since it's only needed during client initialization. The constructor still validates the endpoint is provided before creating the client.


48-51: LGTM - Clean token resolution.

Extracting token resolution into a separate helper improves testability and makes the code easier to maintain.


53-71: LGTM - Streamlined decryption flow.

The updated validation correctly requires EnvironmentID for blob decryption, and the simplified flow that always uses DecryptSecretsBlob is clearer than the previous conditional logic.


73-91: LGTM - Robust token resolution.

The helper properly handles both token sources with appropriate validation, including trimming whitespace and ensuring the token file is not empty. Error messages are clear and actionable.

cmd/inject/config.go (3)

10-29: LGTM - Well-designed config structure.

The allowedUnkeyVars whitelist appropriately preserves deployment and environment context variables while clearing sensitive authentication data. The config struct fields align well with the provider's requirements.


31-33: LGTM - Clean helper method.

Simple and clear helper that improves code readability.


35-55: LGTM - Comprehensive validation logic.

The validation correctly enforces all requirements:

  • Command arguments are mandatory
  • XOR logic ensures exactly one of Token or TokenPath is provided
  • Provider-specific requirements are properly validated
  • Clear error messages guide users to fix configuration issues
cmd/inject/run.go (3)

22-48: LGTM - Well-structured execution flow.

The function correctly orders operations: clears sensitive environment variables before injecting secrets, provides per-variable error handling, and includes helpful debug logging. The comment on lines 24-26 clearly explains the rationale for the clearing order.


50-79: LGTM - Robust secret fetching.

The function properly initializes the provider, uses a reasonable timeout, handles base64 decoding, and passes all required options to the provider. Error messages provide helpful context.


81-88: LGTM - Safe environment cleanup.

The function correctly identifies and removes sensitive UNKEY_* variables while preserving those in the allowlist. The use of strings.Cut is appropriate and efficient.

@Flo4604 Flo4604 marked this pull request as ready for review January 9, 2026 13:00
@chronark chronark merged commit 751c852 into main Jan 9, 2026
36 of 41 checks passed
@chronark chronark deleted the chore/release-unkey-env-img branch January 9, 2026 13:41
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.

Add goreleaser config for unkey-env Docker image

3 participants