Skip to content

feat: add server validate subcommand and unknown key warnings#6307

Open
denysvitali-kaiko wants to merge 1 commit intorunatlantis:mainfrom
kaiko-ai:feat/server-validate
Open

feat: add server validate subcommand and unknown key warnings#6307
denysvitali-kaiko wants to merge 1 commit intorunatlantis:mainfrom
kaiko-ai:feat/server-validate

Conversation

@denysvitali-kaiko
Copy link

What changed?

Added a server validate subcommand and unknown-key warnings on startup.

Problem

Atlantis silently ignores unknown keys in server config files because
viper.Unmarshal() discards unrecognized keys by default. This means
typos and invalid formats go unnoticed:

  • allow-draft-pr instead of allow-draft-prs
  • parallel_apply instead of parallel-apply
  • tofu-version (nonexistent key)

There's currently no built-in way to validate a config file.

Solution

  1. atlantis server validate --config <file> — reads the config file
    via viper (same as preRun()), checks for unknown keys, and exits
    non-zero if any are found. Does NOT require VCS credentials or start the
    server.

  2. Warning on atlantis server startup — logs unknown keys at WARN
    level before proceeding. Non-breaking, doesn't block startup.

  3. Helper functions buildKnownKeys() and findUnknownKeys() that
    derive valid keys from server.UserConfig mapstructure tags
    reflectively, so they stay in sync automatically when new flags are
    added.

Example

$ atlantis server validate --config config.yaml
Error: unknown keys in config.yaml: allow-draft-pr, parallel_apply, tofu-version

Why?

We discovered that our production Atlantis config had 4-5 silently ignored
keys (typos and wrong formats) that had been there for months. This
feature makes it easy to catch these in CI before deployment.

Changes

File Change
cmd/server.go Add buildKnownKeys(), findUnknownKeys(), warning in run()
cmd/server_validate.go NEW — validate subcommand
cmd/server_validate_test.go NEW — tests for validate + unknown key detection
main.go Wire ServerValidateCmd into server command

How Has This Been Tested?

  • go test ./cmd/ — all existing tests pass, 9 new tests added
  • Smoke tested against real config files with known invalid keys
  • Verified exit code behavior (0 for valid, 1 for unknown keys)

Atlantis silently ignores unknown keys in server config files via
viper.Unmarshal(), which can lead to misconfiguration from typos
(e.g. "allow-draft-pr" vs "allow-draft-prs") or invalid formats
(e.g. "parallel_apply" vs "parallel-apply").

This adds:
- `atlantis server validate --config <file>` subcommand that checks
  for unknown keys and exits non-zero if any are found
- Warning on `atlantis server` startup when unknown keys are detected
  (non-breaking, doesn't block startup)
- Helper functions buildKnownKeys() and findUnknownKeys() that derive
  valid keys from server.UserConfig mapstructure tags

Signed-off-by: Denys Vitali <denys.vitali@kaiko.ai>
Copilot AI review requested due to automatic review settings March 12, 2026 09:06
@github-actions github-actions bot added the go Pull requests that update Go code label Mar 12, 2026
@dosubot dosubot bot added the feature New functionality/enhancement label Mar 12, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a server validate CLI workflow and runtime warnings to detect unknown/typo’d keys in Atlantis server config files (which are otherwise silently ignored by Viper), improving config correctness and deploy safety.

Changes:

  • Add atlantis server validate --config <file> subcommand to fail on unknown config keys.
  • Warn on atlantis server startup when unknown keys are present (non-blocking).
  • Add reflective helpers (buildKnownKeys, findUnknownKeys) plus unit tests around validation and warning behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
main.go Wires the new server validate subcommand into the CLI command tree.
cmd/server.go Adds known/unknown key detection helpers and logs unknown-key warnings during server startup.
cmd/server_validate.go Implements the server validate subcommand that reads config and errors on unknown keys.
cmd/server_validate_test.go Adds tests for validate behavior and for unknown-key detection/warnings.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +859 to +868
// findUnknownKeys returns config keys present in viper that do not correspond
// to any known Atlantis server flag. The result is sorted alphabetically.
func findUnknownKeys(v *viper.Viper) []string {
known := buildKnownKeys()
var unknown []string
for _, key := range v.AllKeys() {
if _, ok := known[key]; !ok {
unknown = append(unknown, key)
}
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

findUnknownKeys() iterates over viper.AllKeys(), which includes flattened nested keys (e.g. a valid webhooks: config will typically produce keys like webhooks.0.event, webhooks.0.kind, etc.). Since buildKnownKeys() only includes top-level UserConfig tags (like webhooks), valid nested config currently gets reported as unknown. Consider either validating only top-level keys (e.g. by iterating over viper.AllSettings() map keys or de-duping by the first segment before ".") or extending known-key generation to include nested key paths for structured fields like WebhookConfig.

Suggested change
// findUnknownKeys returns config keys present in viper that do not correspond
// to any known Atlantis server flag. The result is sorted alphabetically.
func findUnknownKeys(v *viper.Viper) []string {
known := buildKnownKeys()
var unknown []string
for _, key := range v.AllKeys() {
if _, ok := known[key]; !ok {
unknown = append(unknown, key)
}
}
// findUnknownKeys returns top-level config keys present in viper that do not
// correspond to any known Atlantis server flag. The result is sorted
// alphabetically.
func findUnknownKeys(v *viper.Viper) []string {
known := buildKnownKeys()
unknownSet := make(map[string]struct{})
for _, key := range v.AllKeys() {
// viper.AllKeys() returns flattened keys (e.g. "webhooks.0.event").
// We only want to validate the top-level key (e.g. "webhooks").
topKey := key
if parts := strings.SplitN(key, ".", 2); len(parts) > 0 {
topKey = parts[0]
}
if _, ok := known[topKey]; !ok {
unknownSet[topKey] = struct{}{}
}
}
unknown := make([]string, 0, len(unknownSet))
for key := range unknownSet {
unknown = append(unknown, key)
}

Copilot uses AI. Check for mistakes.
Equals(t, "allow-draft-pr", unknowns[0])
Equals(t, "tofu-version", unknowns[1])
}

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The unknown-key detection tests only cover flat keys. Since server config supports nested structures like webhooks (slice of objects), add a test case with a valid webhooks: config and assert it does not produce unknown keys; this would prevent regressions where viper's flattened nested keys are mistakenly treated as unknown.

Suggested change
func TestFindUnknownKeys_WithWebhooksNestedConfig(t *testing.T) {
t.Log("Should not report unknown keys for valid nested webhooks config.")
v := viper.New()
v.Set("repo-allowlist", "github.com/test/*")
v.Set("webhooks", []map[string]any{
{
"event": "apply",
"workspace-regex": ".*",
"kind": "slack",
"secret": "secret",
"url": "https://example.com/hook",
},
})
unknowns := findUnknownKeys(v)
Equals(t, 0, len(unknowns))
}

Copilot uses AI. Check for mistakes.
@denysvitali-kaiko denysvitali-kaiko changed the title feat: add 'server validate' subcommand and unknown key warnings feat: add server validate subcommand and unknown key warnings Mar 12, 2026
@GenPage GenPage added the waiting-on-review Waiting for a review from a maintainer label Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New functionality/enhancement go Pull requests that update Go code waiting-on-review Waiting for a review from a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants