Skip to content

feat(lint): add atmos lint stacks command with 10 built-in quality rules - #2203

Open
RB (nitrocode) with Copilot wants to merge 50 commits into
mainfrom
copilot/add-atmos-yaml-linter
Open

RB (nitrocode) with Copilot wants to merge 50 commits into
mainfrom
copilot/add-atmos-yaml-linter

Conversation

Copilot AI commented Mar 15, 2026 •

Copy link
Copy Markdown
Contributor
  • Phase 1+2: Scope L-07, zero-pad rule IDs, drop missing non-globs, parity test, empty L-08 File
  • Phase 3: pkg/lint/pathnorm, concernGroup [-_/], mergedLintConfig CohesionMaxGroups
  • Fix resolveNonGlobImport: add os.Stat check for explicit .yaml/.yml paths (prevents phantom edges)
  • normalizeRuleID broader patterns: handles 7, L7, l-7 → L-07; extracted as named helper
  • Tests: TestL03DepthIgnoresMissingExtImport, TestNormalizeRuleID (16 cases), extended TestLintRuleFilterNormalization
  • All tests pass: pkg/lint/..., internal/exec, cmd/lint

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@mergify mergify Bot added triage Needs triage wip Work in Progress: Not ready for final review or merge labels Mar 15, 2026
@nitrocode RB (nitrocode) added minor New features that do not break anything and removed triage Needs triage labels Mar 15, 2026
Copilot AI and others added 2 commits March 15, 2026 02:26
Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Copilot AI changed the title [WIP] Add Atmos Stack YAML Linter functionality feat(lint): add atmos lint stacks command with 10 built-in quality rules Mar 15, 2026
Copilot AI requested a review from RB (nitrocode) March 15, 2026 02:29
@mergify mergify Bot removed the wip Work in Progress: Not ready for final review or merge label Mar 15, 2026
@github-actions github-actions Bot added the size/l Large size PR label Mar 15, 2026
@github-actions

github-actions Bot commented Mar 15, 2026 •

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 0408ff6.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@nitrocode

This comment was marked as outdated.

…dding lint command

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>

This comment was marked as outdated.

@github-actions

This comment was marked as outdated.

@nitrocode

This comment was marked as outdated.

…os lint stacks

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
@coderabbitai

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

Copilot AI and others added 2 commits March 23, 2026 05:04
Item 1: Disambiguate stack name → file mapping
- buildStackNameToFileIndex now returns map[string][]string (basename → []files)
- Add buildStackStemToFileIndex returning map[string]string (full stem → file)
- LintContext gains StackNameToFileIndex ([]string) and StackStemToFile (string)
- L-08 uses new resolveFileForStack() that prefers StackStemToFile, then
  disambiguates basename collisions (prefers deploy/ dir), else omits File

Item 2: Normalize --rule filter input
- ExecuteLintStacksCmd now calls strings.ToUpper + TrimSpace on each rule ID
- Empty tokens after trimming are dropped
- Users can now pass "l-02" or "L-02" interchangeably

Item 3: Drop unmatched globs from results
- expandGlobImports no longer appends the literal pattern when a glob
  produces no file matches; it is silently dropped with a debug log
- buildImportGraph skips adding an entry when the expanded result is empty
- This prevents phantom references in L-07 and false depth inflation in L-03

Item 4: Standardize relative paths in L-03 findings
- L-03 now converts File to a path relative to ctx.StacksBasePath (mirrors
  L-07's displayPath convention so all findings are consistently formatted)

Item 5: Docs update in stacks.go
- Long description now documents case-insensitive --rule, and the fail-closed
  behavior of --stack when no manifest stem matches

Tests:
- TestBuildStackNameToFileIndexCollision: assert collision → []string + stem index
- TestLintRuleFilterNormalization: verify upper/lower/mixed/empty normalization
- TestGlobNoMatchDroppedFromL03Depth: assert unmatched glob is dropped
- TestL03RelativePathOutput: assert File field is relative to StacksBasePath
- Update TestBuildImportGraph "no matches" to expect empty (not literal)
- Update TestBuildStackNameToFileIndex for new []string type
- Update TestL08FileAttributionWithIndex for new StackStemToFile + []string fields

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Agent-Logs-Url: https://github.com/cloudposse/atmos/sessions/ebe8551a-7679-4d09-9b9f-bf357998b8de
…attern doc comment

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Agent-Logs-Url: https://github.com/cloudposse/atmos/sessions/ebe8551a-7679-4d09-9b9f-bf357998b8de
@nitrocode

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

Copilot AI and others added 2 commits March 23, 2026 05:33
…IDs, drop missing non-globs, parity test, empty L-08 File

Item 1: Scope L-07 when --stack has no imports
- Remove `len(importGraph) > 0` guard; always scope AllStackFiles when stackFilter != ""
- When importGraph is empty (root has no imports), set AllStackFiles = keys(rawStackConfigs)
- Test: TestScopeStackFilesNoImports

Item 2: Zero-pad single-digit rule IDs
- "l-7" → "L-07" via new isDigitOnly() helper + fmt.Sprintf("L-%02s", numPart)
- Update TestLintRuleFilterNormalization to test zero-padding
- New test: TestRuleIDZeroPadNormalization
- Update cmd/lint/stacks.go Long description

Item 3: Drop missing non-glob imports
- resolveNonGlobImport returns "" when no file on disk (was: best-effort fallback path)
- expandGlobImports skips "" returns
- Test: TestMissingNonGlobDroppedFromGraph

Item 4: Normalization parity test
- TestRulesRelNormParityWithL07: corpus test for exec's rulesRelNorm against golden values
- TestRelNormCorpusGolden: parity test in pkg/lint/rules using ExportedRelNorm

Item 5: L-08 empty File when unresolved
- stackNameToFile: return "" for bare stack name with empty basePath (was: returned stack name)
- Update TestStackNameToFile to match new behavior
- New test: TestL08EmptyFileWhenUnresolved

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Agent-Logs-Url: https://github.com/cloudposse/atmos/sessions/e3b42b29-53eb-476a-aa52-5f2f6a8e2c80
…DigitOnly comment, blank line style

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Agent-Logs-Url: https://github.com/cloudposse/atmos/sessions/e3b42b29-53eb-476a-aa52-5f2f6a8e2c80
@nitrocode

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@mergify

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

…t, mergedLintConfig CohesionMaxGroups

Item 4 (pathnorm package):
- Create pkg/lint/pathnorm with NormalizeRelNoExt(path, base) as single
  canonical path normalization shared between exec and L-07
- Refactor internal/exec/lint_stacks.go to use pathnorm instead of
  local rulesRelNorm (eliminates duplication)
- Refactor pkg/lint/rules/l07_orphaned_file.go: relNorm and
  normalizeForComparison now delegate to pathnorm.NormalizeRelNoExt
- Add parity test TestPathnormParityWithRelNorm in rules_test.go
- Add pathnorm_test.go with corpus golden tests

Item 5a (concernGroup [-_/]):
- Expand concernGroup in l05_cohesion.go to split on /, -, _ in
  priority order (path separator > first separator)
- "network/vpc" → "network", "vpc_endpoints" → "vpc"
- Add TestConcernGroupSeparators with 11 test cases

Item 5b (mergedLintConfig CohesionMaxGroups):
- Remove defaultCohesionThreshold const from l05_cohesion.go
- Add CohesionMaxGroups default (3) to mergedLintConfig in exec
- L-05 reads ctx.LintConfig.CohesionMaxGroups directly with safety guard
- Update TestMergedLintConfig to assert CohesionMaxGroups=3 default
- Add sub-test for CohesionMaxGroups not overridden when set

Also: fix TestResolveNonGlobImport to expect "" (not fallback path) for
missing non-glob imports (behavior change from phase 2)

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Agent-Logs-Url: https://github.com/cloudposse/atmos/sessions/57b2e063-b57d-444f-b355-db94975612a3
@nitrocode

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

Copilot AI and others added 2 commits March 24, 2026 03:18
…roader patterns

Item 1 (resolveNonGlobImport os.Stat for .yaml/.yml):
- When importPath already has .yaml/.yml extension, probe disk via os.Stat
  before returning the path to drop phantom edges from mistyped/missing
  explicit-extension imports
- Prevents L-03 depth inflation from e.g. "catalog/phantom.yaml" that doesn't exist
- Update test "relative import with .yaml extension" to create file on disk
- Add tests: missing .yaml → "", missing .yml → ""

Item 2 (normalizeRuleID broader patterns):
- Extract normalizeRuleID() helper from inline rule-parse logic
- Handles all forms: "7", "L7", "l-7", "L-7", "L-07" → canonical "L-07"
- Custom/unknown IDs pass through unchanged (upper-case)
- Update ExecuteLintStacksCmd to use normalizeRuleID helper

Item 3 (new tests):
- TestL03DepthIgnoresMissingExtImport: explicit .yaml import that doesn't exist
  is dropped from import graph (no phantom edge, no L-03 inflation)
- TestNormalizeRuleID: 16 cases covering all input forms
- Extend TestLintRuleFilterNormalization: "7, L7, l-7" sub-test via normalizeRuleID

Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com>
Agent-Logs-Url: https://github.com/cloudposse/atmos/sessions/d1f7d81d-4e63-4060-9c9b-63940d355d4d
@nitrocode

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@mergify

mergify Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

💥 This pull request now has conflicts. Could you fix it @Copilot? 🙏

1 similar comment
@mergify

mergify Bot commented May 14, 2026

Copy link
Copy Markdown
Contributor

💥 This pull request now has conflicts. Could you fix it @Copilot? 🙏

This branch was successfully deployed

1 active deployment
preview — 0408ff62 Deployed Mar 24, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflict This PR has conflicts minor New features that do not break anything size/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants