Files
helmfile/pkg/app/config.go
T
yxxheroandcopilot-swe-agent[bot] 902c5ced17 feat: add 'create' subcommand to scaffold helmfile deployment projects (#2574)
* feat: add 'create' subcommand to scaffold helmfile deployment projects

Add 'helmfile create [NAME]' command that generates a best-practice
helmfile project structure with:
- helmfile.yaml with commented examples (helmDefaults, repositories,
  environments, releases)
- environments/default.yaml for environment-specific values
- values/.gitkeep placeholder for release values

Supports --output-dir/-o for custom output path and --force to
overwrite existing files. Validates project name to prevent path
traversal.

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: add overwrite protection for all scaffold files and unit tests for create command

- pkg/app/create.go: extract writeFileIfNotExists helper that respects the
  --force flag; all three scaffold files (helmfile.yaml,
  environments/default.yaml, values/.gitkeep) now refuse to overwrite
  without --force
- pkg/config/create.go: ValidateConfig now checks all three scaffold paths
  and reports every already-existing file in a single error before
  proceeding, instead of only checking helmfile.yaml
- pkg/app/create_test.go: add unit tests covering new directory, current
  directory, per-file overwrite rejection without --force, and full
  overwrite with --force

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/eb6d9e4b-0f72-4e26-b841-e1e39a2b2e83

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: remove redundant absDir from ValidateConfig error message

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/eb6d9e4b-0f72-4e26-b841-e1e39a2b2e83

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: address create command review feedback

- cmd/create.go: add config.NewCLIConfigImpl() call for consistency with other
  subcommands; update --force flag help text to list all overwritten files
- pkg/config/create.go: delegate to c.GlobalImpl.ValidateConfig() at end of
  ValidateConfig() for global option validation (--color/--no-color)
- pkg/config/create_test.go: add unit tests for CreateImpl.ValidateConfig()
  covering path separator rejection, '..' rejection, existing-file detection
  per-file and with --force, and global color conflict delegation

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/6327d657-5888-4b94-85fb-def80c0a193f

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: clarify test helper name and comment in create_test.go

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/6327d657-5888-4b94-85fb-def80c0a193f

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: atomic preflight check in App.Create before any writes

Refactor Create to collect all conflicting scaffold paths up front
before writing anything. When --force is not set and any scaffold file
already exists, the command returns a single error listing all
conflicts without touching the filesystem.

Also removes the now-unnecessary writeFileIfNotExists helper and adds a
test (TestCreate_PreflightAtomicOnLaterConflict) verifying that a
conflict on a later file (e.g. environments/default.yaml) prevents even
the first file (helmfile.yaml) from being created.

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/aae6f2e6-7f9e-42b8-afa3-78edd3215127

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: handle non-IsNotExist Stat errors in preflight check; add whitespace name test; fix gci formatting

- pkg/app/create.go: treat os.Stat errors that are NOT os.IsNotExist as
  hard errors in the preflight scan, surfacing permission/IO issues before
  any writes happen; remove trailing blank line that caused gci failure
- pkg/config/create.go: same non-IsNotExist error handling in ValidateConfig
- pkg/config/create_test.go: add TestCreateImpl_ValidateConfig_WhitespaceOnlyName
  covering the "   " (whitespace-only) name rejection branch

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d6574f56-f46d-46f7-99d9-e0b0b897b3b5

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* refactor: eliminate duplicated scaffold existence check; use O_EXCL for TOCTOU protection

- pkg/config/create.go: remove file-existence check from ValidateConfig
  (duplicate of App.Create's preflight); ValidateConfig now only validates
  the project name and delegates to GlobalImpl.ValidateConfig. Remove unused
  os/path/filepath imports.
- pkg/app/create.go: add writeScaffoldFile helper that uses O_CREATE|O_EXCL
  when force=false, so a file appearing between the preflight check and the
  actual write is caught rather than silently overwritten (TOCTOU protection).
- pkg/config/create_test.go: remove four file-existence tests that tested the
  now-deleted ValidateConfig logic; file-conflict coverage remains in
  pkg/app/create_test.go. Simplify ValidName and GlobalColorConflict tests.

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/82f82e72-934f-416c-8662-5060e92284fa

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: wrap O_EXCL error with --force hint; add writeScaffoldFile unit tests

- pkg/app/create.go: wrap os.IsExist error from writeScaffoldFile with a
  message that names the conflicting file and suggests --force, so the user
  gets actionable output even in the TOCTOU case
- pkg/app/create_test.go: add TestWriteScaffoldFile_CreatesNewFile,
  TestWriteScaffoldFile_ExistingFileNoForce, and
  TestWriteScaffoldFile_ExistingFileWithForce to cover the helper directly

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/82f82e72-934f-416c-8662-5060e92284fa

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: wrap App.Create errors in *app.Error; reject '.' as project name; add '.' name test

- pkg/app/create.go: wrap all App.Create fmt.Errorf returns with appError("", ...)
  so toCLIError produces a clean user-friendly message instead of
  "unexpected error: *fmt.wrapError: ..."
- pkg/config/create.go: reject "." as a NAME alongside ".." to prevent
  accidentally scaffolding into the current directory via a named argument
- pkg/config/create_test.go: add TestCreateImpl_ValidateConfig_NameDot

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/6d64508e-2d66-47e9-a02a-7669a2f481b7

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: drop unused outputDir param from test helper to fix unparam lint error

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/11cd65e9-c5ef-4195-9375-bc929169616b

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: drop unused force param from test helper to fix unparam lint error

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/0e1bdac5-708f-4615-ae6d-e22fc1e921f2

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-03 19:03:11 +08:00

346 lines
5.4 KiB
Go

package app
import "go.uber.org/zap"
type ConfigProvider interface {
Args() string
HelmBinary() string
KustomizeBinary() string
EnableLiveOutput() bool
StripArgsValuesOnExitError() bool
DisableForceUpdate() bool
EnforcePluginVerification() bool
HelmOCIPlainHTTP() bool
SkipDeps() bool
SkipRefresh() bool
SequentialHelmfiles() bool
FileOrDir() string
KubeContext() string
Namespace() string
Chart() string
Selectors() []string
StateValuesSet() map[string]any
StateValuesFiles() []string
Kubeconfig() string
Env() string
loggingConfig
}
type DepsConfigProvider interface {
Args() string
SkipRepos() bool
IncludeTransitiveNeeds() bool
concurrencyConfig
}
type ReposConfigProvider interface {
Args() string
IncludeTransitiveNeeds() bool
}
type ApplyConfigProvider interface {
Args() string
PostRenderer() string
PostRendererArgs() []string
SkipSchemaValidation() bool
Cascade() string
HideNotes() bool
TakeOwnership() bool
SuppressOutputLineRegex() []string
Values() []string
Set() []string
SkipCRDs() bool
SkipDeps() bool
SkipRefresh() bool
Wait() bool
WaitRetries() int
WaitForJobs() bool
Timeout() int
IncludeTests() bool
Suppress() []string
SuppressSecrets() bool
ShowSecrets() bool
NoHooks() bool
SuppressDiff() bool
DetailedExitcode() bool
StripTrailingCR() bool
Color() bool
NoColor() bool
Context() int
DiffOutput() string
Validate() bool
SkipCleanup() bool
SkipDiffOnInstall() bool
DiffArgs() string
SyncArgs() string
SyncReleaseLabels() bool
DAGConfig
TrackMode() string
TrackTimeout() int
TrackLogs() bool
Description() string
concurrencyConfig
interactive
loggingConfig
valuesControlMode
}
type SyncConfigProvider interface {
Args() string
PostRenderer() string
SkipSchemaValidation() bool
PostRendererArgs() []string
HideNotes() bool
TakeOwnership() bool
Cascade() string
Values() []string
Set() []string
SkipCRDs() bool
SkipDeps() bool
SkipRefresh() bool
Wait() bool
WaitRetries() int
WaitForJobs() bool
Timeout() int
SyncArgs() string
Validate() bool
SkipNeeds() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
SyncReleaseLabels() bool
TrackMode() string
TrackTimeout() int
TrackLogs() bool
Description() string
DAGConfig
concurrencyConfig
interactive
loggingConfig
valuesControlMode
}
type DiffConfigProvider interface {
Args() string
PostRenderer() string
PostRendererArgs() []string
SkipSchemaValidation() bool
SuppressOutputLineRegex() []string
Values() []string
Set() []string
Validate() bool
SkipCRDs() bool
SkipDeps() bool
SkipRefresh() bool
IncludeTests() bool
Suppress() []string
SuppressSecrets() bool
ShowSecrets() bool
NoHooks() bool
SuppressDiff() bool
SkipDiffOnInstall() bool
DiffArgs() string
DAGConfig
DetailedExitcode() bool
StripTrailingCR() bool
Color() bool
NoColor() bool
Context() int
DiffOutput() string
TakeOwnership() bool
concurrencyConfig
valuesControlMode
}
type DestroyConfigProvider interface {
Args() string
Cascade() string
SkipDeps() bool
SkipRefresh() bool
SkipCharts() bool
DeleteWait() bool
DeleteTimeout() int
interactive
loggingConfig
concurrencyConfig
}
type TestConfigProvider interface {
Args() string
SkipDeps() bool
SkipRefresh() bool
Timeout() int
Cleanup() bool
Logs() bool
concurrencyConfig
}
type LintConfigProvider interface {
Args() string
Values() []string
Set() []string
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
DAGConfig
concurrencyConfig
}
type UnittestConfigProvider interface {
Args() string
Values() []string
Set() []string
FailFast() bool
Color() bool
DebugPlugin() bool
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
DAGConfig
concurrencyConfig
}
type FetchConfigProvider interface {
SkipDeps() bool
SkipRefresh() bool
OutputDir() string
OutputDirTemplate() string
WriteOutput() bool
concurrencyConfig
}
type TemplateConfigProvider interface {
Args() string
PostRenderer() string
PostRendererArgs() []string
SkipSchemaValidation() bool
Values() []string
Set() []string
OutputDirTemplate() string
Validate() bool
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
SkipTests() bool
OutputDir() string
IncludeCRDs() bool
NoHooks() bool
KubeVersion() string
ShowOnly() []string
DAGConfig
concurrencyConfig
}
type DAGConfig interface {
SkipNeeds() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
EnforceNeedsAreInstalled() bool
}
type WriteValuesConfigProvider interface {
Values() []string
Set() []string
OutputFileTemplate() string
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
IncludeTransitiveNeeds() bool
concurrencyConfig
}
type StatusesConfigProvider interface {
Args() string
concurrencyConfig
}
type StateConfigProvider interface {
EmbedValues() bool
}
type DAGConfigProvider any
type concurrencyConfig interface {
Concurrency() int
}
type loggingConfig interface {
Logger() *zap.SugaredLogger
}
type interactive interface {
Interactive() bool
}
type ListConfigProvider interface {
Output() string
SkipCharts() bool
}
type CacheConfigProvider any
type InitConfigProvider interface {
Force() bool
}
type CreateConfigProvider interface {
Name() string
OutputDir() string
Force() bool
loggingConfig
}
type PrintEnvConfigProvider interface {
Output() string
}
// reset/reuse values helm cli flags handling for apply/sync/diff
type valuesControlMode interface {
ReuseValues() bool
ResetValues() bool
}