Repository navigation
Typo - #265
Merged
Merged
Typo#265
Conversation
stoned
requested review from
Andy Roth (RothAndrew) and
Cody Moore (dotCipher)
November 26, 2022 19:17
Erik Osterman (Cloud Posse) (osterman)
added a commit
that referenced
this pull request
Aug 7, 2026
Bump pnpm.overrides for transitively-pulled packages to their patched versions, all within the semver-major bump the dependabot.yml ignore policy blocks: - js-yaml 3.15.0 -> 3.15.1, 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, GHSA for the 4.x line): quadratic CPU consumption in !!omap resolution, fixes #269, #268. - mermaid 11.16.0 -> 11.16.1: fixes #267 (radar diagram DoS), #266 (config API prototype pollution), #265 (CSS injection), #264 (Architecture diagram prototype pollution), #263 (XY Chart infinite-loop DoS). No open CodeQL alerts. Verified with `atmos lint --changed` (0 issues) and `npm run build` in website/ (succeeds, same pre-existing unrelated broken-anchor warning as before this change). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman)
added a commit
that referenced
this pull request
Aug 7, 2026
Bump pnpm.overrides floors for transitive npm dependencies flagged by Dependabot (all patch/minor bumps within the same major, not blocked by dependabot.yml's major-version ignore policy): - js-yaml 3.15.0 -> 3.15.1 (#269, GHSA-5p4m-2wfm-xmqj: quadratic CPU consumption in !!omap resolution, high) - js-yaml 4.3.0 -> 4.3.1 (#268, same advisory, 4.x line, high) - mermaid 11.16.0 -> 11.16.1 (#267, #266, #265, #264, #263: five advisories ranging low-medium) NOTICE unchanged (no license changes). Verified: pnpm install regenerated website/pnpm-lock.yaml with the bumped versions resolved, atmos fix lint (patch-scoped, no Go files touched) is a no-op, and cd website && npm run build succeeds with no new broken links. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman)
added a commit
that referenced
this pull request
Aug 7, 2026
Bump the pnpm overrides pinning js-yaml and mermaid to their patched versions -- all within the same major version, so no dependabot.yml ignore-policy exception is needed: - js-yaml@^3: 3.15.0 -> 3.15.1 (GHSA-5p4m-2wfm-xmqj, alert #269) - js-yaml@^4: 4.2.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, alert #268) - mermaid@^11: 11.15.0 -> 11.16.1 (GHSA-rhh3-jpg6-66xh #267, GHSA-c4c3-pg64-4m4v #266, GHSA-6x64-9x62-f2gx #265, GHSA-3rrr-jr9j-h3q3 #264, GHSA-2v8p-3f2j-5mp7 #263) Verified: pnpm install regenerates the lockfile at the patched versions, `npm run build` succeeds, and NOTICE is unchanged (no license drift). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman)
added a commit
that referenced
this pull request
Aug 7, 2026
Bump pnpm overrides to patched versions, all within the major-version ignore policy in .github/dependabot.yml: - js-yaml 3.15.0 -> 3.15.1, 4.3.0 -> 4.3.1: quadratic CPU consumption in !!omap resolution (GHSA-5p4m-2wfm-xmqj / GHSA advisories, alerts #269, #268, high severity) - mermaid 11.16.0 -> 11.16.1: radar-diagram DoS, prototype pollution (config APIs and Architecture diagrams), CSS injection, XY-chart infinite-loop DoS (alerts #267, #266, #265, #264, #263, medium/low) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Erik Osterman (Cloud Posse) (osterman)
added a commit
that referenced
this pull request
Aug 7, 2026
Bumps three pnpm.overrides pins to their patched releases, all within the major-version line dependabot.yml's ignore policy allows: - js-yaml 3.15.0 -> 3.15.1 (GHSA-5p4m-2wfm-xmqj, alert #269) - js-yaml 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, alert #268) - mermaid 11.16.0 -> 11.16.1 (GHSA-rhh3-jpg6-66xh #267, GHSA-c4c3-pg64-4m4v #266, GHSA-6x64-9x62-f2gx #265, GHSA-3rrr-jr9j-h3q3 #264, GHSA-2v8p-3f2j-5mp7 #263) Verified via `pnpm run build` in website/; NOTICE unchanged (no license changes from these patch bumps). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Andriy Knysh (aknysh)
added a commit
that referenced
this pull request
Aug 8, 2026
* feat(ai): add browsable /ai/skills directory and skill categories Adds a searchable, category-grouped /ai/skills page (reusing the existing file-browser plugin behind /examples and /gists) with a per-skill detail page for each SKILL.md, replacing the hand-maintained "Available Skills" list in the docs that had drifted to half the real count (25 vs 52). Adds `metadata.category` to every bundled SKILL.md, threads it through `pkg/ai/skills/marketplace`, and exposes it via a new `--format` flag (table/json/yaml/csv/tsv) on `atmos ai skill list`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(ai): polish agent skills directory nav, homepage count, and changelog Adds the changelog post and roadmap entry for the browsable Agent Skills Directory shipped in the prior commit, plus follow-up polish: a SkillCount component that renders the live skill count at build time (used on the homepage AI section instead of a hardcoded number), file-browser plugin card icon/CTA options, sidebar nav restructuring under Atmos AI, and doc updates for the new `--format` flag. Also fixes pre-existing EditorConfig indentation violations (3-space markdown list continuations) in several SKILL.md files, surfaced by the affected-file validator once those files were touched by this branch's earlier commit. * feat(ai): add copy-as-markdown button to skill pages Adds a "Copy as Markdown" button to a skill's root page that concatenates its SKILL.md and every nested reference file into one clipboard-ready Markdown document, so the full context can be grabbed without installing the skill. Ships as an opt-in `enableCopyMarkdown` file-browser plugin option (enabled for the /ai/skills instance only; /examples and /gists render unchanged). * fix: address CodeRabbit review feedback on skills directory browser PR Fixes six issues flagged on PR #2881: - cmd/ai/skill/list.go: dispatch structured --format output (json/yaml/csv/tsv) before the "No skills installed" empty-message check, so --installed with zero results stays machine-readable instead of returning prose. - cmd/ai/skill/list.go: validate the Viper-resolved --format value (covers ATMOS_AI_SKILL_FORMAT env/config, not just the CLI flag) before it reaches the renderer. - pkg/ai/skills/marketplace/catalog_test.go: add the missing Category field to the AvailableSkill compile-time sentinel so a future field rename or drop is caught at compile time. - cmd/ai/skill/markdown/atmos_ai_skill_list_usage.md: fix MD040/MD014 lint on the new --format=json example (shell fence, no unshown $ prompt). - agent-skills/skills/atmos-templates/SKILL.md: add missing trailing period. - website/src/components/FileBrowser/styles.module.css: move the viewport max-height constraint from .sidebar onto .sidebarInner so a long file tree scrolls inside the sidebar instead of growing it (shared by /examples, /gists, and /ai/skills). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(ai): use natural link text for the Agent Skills Directory Two spots linked to /ai/skills using the raw path as the visible link text ("browsable at /ai/skills") instead of natural language, which read poorly next to the "Agent Skills Directory" phrasing used everywhere else this page is linked. Match the established convention. * feat(ai): per-page skill markdown, code-styled titles, fix AWS compliance category - file-browser plugin: new enablePerPageMarkdown option writes a raw <name>.md per item at build time (SKILL.md + nested reference files concatenated), extending the sitewide "append .md for raw Markdown" convention to skill pages, which docusaurus-plugin-llms-txt can't see since they're custom routes, not docs/blog content. - new titleAsCode option renders each item's title as a code-formatted `/name` on the index cards and sidebar header, signaling how a skill is invoked. Enabled for the skills instance only. - atmos-aws-compliance: recategorize from "security" to "aws" so it groups with the other AWS integrations instead of Auth/Secrets. * feat(ai): hover copy button on skill cards, drop code-title background - IndexPage cards get a "Copy as Markdown" icon button in the corner, revealed on hover/focus, so a skill's full context (including nested reference files) can be copied straight from the grid without opening it. Gated by enableCopyMarkdown, so /examples and /gists are unaffected. CopyMarkdownButton gained an iconOnly mode. - Code-formatted titles (titleAsCode) now drop Infima's default inline-code background chip/border, keeping just the monospace font. * fix(ai): match card copy-button style to the site's pill buttons The icon-only copy button used a solid circular background with a border and drop shadow, inconsistent with the translucent rounded- rect pill language used everywhere else (.copyMarkdownButton, .githubButton, .filterButton). Same rgba background/hover, 6px radius, no shadow. * docs(ci): note that atmos CLI replaces the old github-action-* Actions * fix: JSON empty-array bug in list renderer, backtick-fence collision - pkg/list/renderer: formatJSON built its result with a nil slice var, so json.MarshalIndent emitted "null" instead of "[]" for zero rows. Every --format=json list command was affected, not just skills. Strengthened the regression test (cmd/ai/skill/list_test.go) to require a non-nil slice, which is what caught this. - file-browser plugin + client utils: collectMarkdownContext wrapped non-Markdown file content in a fixed ``` fence. A nested file whose own content contains a ``` run would close the fence early and corrupt the generated/copied Markdown. Both twins now pick a fence longer than any backtick run already in the content. * fix(ai): disable pointer-events on hidden card copy button opacity: 0 doesn't remove an element from hit testing. The invisible button sat in front of the card link (top-right), so an early click or touch there hit the button instead of navigating - its handler stops propagation, silently swallowing the click. * ci: give windows acceptance-test job timeout real headroom A run hit the 75-minute job-level ceiling to the exact second while every individual step, including the full go test suite, had already completed successfully per the logs - the "Acceptance tests" step itself finished in 54.6m, under its own 60m budget, but the job-level timeout left almost no slack once summed with setup/toolchain steps. Same CI-to-CI variance already documented on the step-level timeout below; widen the job-level ceiling to match. * docs(website): reorganize Atmos AI sidebar and consolidate agent skills docs Nest MCP under the Atmos AI category alongside a flat "Agent Skills" link (previously a top-level sibling behind a nested "Skills" category), and consolidate the redundant agent-skills.mdx and skill-marketplace.mdx pages into the atmos ai skill CLI command reference, updating all cross-references and adding redirects for the removed URLs. Also fixes a duplicate "scaffold" sidebar entry: scaffold.mdx now lives inside the scaffold/ folder as usage.mdx with a _category_.json, matching every other multi-subcommand command (auth, pro, validate, ai, toolchain). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(website): correct EditorConfig indentation in scaffold blog post CI's EditorConfig validation requires left-padding in multiples of 2; these list items under a numbered entry used 3 spaces. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(website): collapse multi-line PrimaryCTA/SecondaryCTA to single line MDX wraps a JSX component's text in a <p> when it sits on its own line inside a block-level context, which overrides the button's centered white/bold styling with muted paragraph styling. Single-line usage (the pattern already used on working pages like auth/usage.mdx) keeps the label as plain inline text. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(ci): clarify actions/checkout + atmos is only a baseline setup The intro note and workflow overview implied actions/checkout plus an atmos command was universally sufficient. Status checks, check runs, PR comments, OIDC, SBOM uploads, and github/artifacts planfile storage need additional permissions or the github-runtime action, already documented in the Permissions section below - cross-reference it instead of overclaiming. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(ai): keep copy confirmation visible for its full duration Repeated clicks scheduled overlapping setTimeout calls to reset the "Copied!" state; an earlier click's timeout could fire and hide the confirmation before the latest click's 2s window elapsed. Track the timeout in a ref, clear it before scheduling a new one, and clean it up on unmount. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(blog): fix inaccurate claim and rewrite agent-skills-directory post The post claimed atmos ai skill list was a hand-maintained doc that drifted out of sync with the real catalog. It never was -- the listing is generated at runtime from the embedded skill catalog. The actual gap was the missing full-content browse/search/copy/fetch experience, which is what the post now leads with. Also rewritten in short, active, single-idea sentences (ASD-STE100 style). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * ci: give the acceptance-test job real timeout headroom on Linux timeout-minutes: 90 matched Linux's timed-step sum exactly (25m registry cache + 60m coverage tests + 5m coverage upload), leaving zero slack for checkout, Go/Atmos setup, and toolchain installs before the job gets force-cancelled. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(blog): use a shell-safe curl example in the skills directory post The unquoted <skill-name> placeholder is parsed as shell redirection syntax, so a reader who copies the command as written fails before curl runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(website): remove invalid font-family quotes in CommandBox Stylelint's font-family-name-quotes rule rejects quotes on single- word font names like Monaco and Menlo; Courier New keeps its quotes since it contains a space. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(website): handle clipboard write failures in CommandBox navigator.clipboard.writeText can reject when the browser denies clipboard access, and the rejection was left unhandled. Catch it and surface a "Copy failed" state instead of letting the click silently do nothing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(website): keep CommandBox copy result states mutually exclusive Independent copied/failed booleans let a fast retry leave the button showing a checkmark with a "Copy failed" tooltip (or vice versa) until the stale timeout caught up. Replace them with a single CopyStatus value so the two states can't coexist, and extract the clipboard-write outcome into a pure performCopy() function (following the CastPlayer convention: sibling .mjs logic module + node:test .test.mjs) so the success/failure/reset behavior has regression coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 7 Dependabot alerts (js-yaml, mermaid) Bump pnpm overrides to patched versions, all within the major-version ignore policy in .github/dependabot.yml: - js-yaml 3.15.0 -> 3.15.1, 4.3.0 -> 4.3.1: quadratic CPU consumption in !!omap resolution (GHSA-5p4m-2wfm-xmqj / GHSA advisories, alerts #269, #268, high severity) - mermaid 11.16.0 -> 11.16.1: radar-diagram DoS, prototype pollution (config APIs and Architecture diagrams), CSS injection, XY-chart infinite-loop DoS (alerts #267, #266, #265, #264, #263, medium/low) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>
zack-is-cool
pushed a commit
to zack-is-cool/atmos
that referenced
this pull request
Aug 10, 2026
…udposse#2880) * fix(steps): resolve relative paths against step.WorkingDirectory The archive, file, workdir, junit, and container build step handlers resolved relative source/destination/path/glob/context fields via template substitution only, then let filesystem calls resolve them against the Atmos process's own cwd instead of step.WorkingDirectory. This surfaced most visibly for `type: archive` hooks, since the hooks engine correctly defaults working_directory to the component path but the handler never read it back. Add a shared BaseHandler.ResolveInWorkingDirectory helper that anchors a relative resolved value to step.WorkingDirectory (falling back to process cwd when unset, matching prior behavior), and apply it across the five affected handlers. container_build.go additionally anchors Dockerfile to the resolved Context rather than WorkingDirectory directly, matching Docker's own convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): add fix record for step WorkingDirectory bug Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(runner/step): close coverage gaps in ResolveInWorkingDirectory and loadReport Codecov flagged handler_base.go and junit.go below the 85% patch-coverage threshold. Add a case that exercises the relative (non-template) WorkingDirectory branch in resolveWorkingDirectory, and a junit test that triggers a WorkingDirectory template-resolution error from inside loadReport's per-pattern loop, distinct from the already-covered `files` template error path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(steps): anchor container bake/cache paths and preserve workflow error context A field test of the WorkingDirectory fix found the container_build.go handler was only partially fixed: build.bake.file/bake.files and cache.from/cache.to type: local src/dest still resolved against the Atmos process's own cwd instead of step.WorkingDirectory, reproduced live as a silent build against the wrong bake file. Both now route through ResolveInWorkingDirectory like context/dockerfile already do. Also root-caused and fixed a separate defect surfaced while verifying error output: buildWorkflowStepError dual-wrapped step errors with fmt.Errorf before building the final error, and cockroachdb/errors treats that Go 1.20 multi-error shape as an opaque leaf node, silently dropping any hints/context a handler attached deeper in the chain. Switched to WithCause, which extracts them eagerly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(steps): anchor container bake/cache paths and preserve workflow error context Address CodeRabbit findings on PR cloudposse#2880: - Remove the host-specific /tmp/atmos-field-test/ path from the fix doc, replacing it with a worktree-relative fixture reference. - Split TestBuildWorkflowStepError and TestBuildWorkflowStepErrorPreservesInnerHintsAndContext out of workflow_utils_test.go into a focused workflow_step_error_test.go, keeping step-error tests co-located and out of the oversized (pre-existing) workflow_utils_test.go file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(runner/step): close container_build.go coverage gaps from bake/cache anchoring Codecov flagged container_build.go's patch coverage below 85% after the bake/cache anchoring follow-up. Add regression tests for the three previously-uncovered error branches: resolveBakeFiles propagating a per-entry template failure, anchorCacheLocalPaths' own resolve failure, and that failure propagating out through resolveBuildCache. handler_base.go's remaining gap (os.Getwd/filepath.Abs failing inside resolveWorkingDirectory) is left uncovered deliberately -- it requires corrupting the process's own working directory to reach, has no DI seam, and matches this repo's existing untested pattern for the identical os.Getwd/filepath.Abs fallback in container_run.go. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(docs): exclude reproducible-builds.org from link checking (CI connection refused) CI's Check Markdown Links job failed on docs/prd/archive-step.md's citation of the SOURCE_DATE_EPOCH origin -- the CI runner's outbound request was refused while the page returns 200 OK outside CI. Follows the repo's existing precedent for excluding CI-hostile hosts (docs.docker.com, otelic.com, taskfile.dev, etc.) in lychee.toml. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(hooks): anchor bare-relative working_directory to the component An explicit working_directory: on a kind: step/kind: steps hook was always resolved against the Atmos process's own cwd, regardless of shape. Following docs/prd/base-path-resolution-semantics.md's existing Dot/Bare convention: a dot-prefixed value (., .., ./x, ../x) keeps resolving against the process cwd; a bare relative value (x, x/y) now resolves against the component's own working directory instead -- the same directory ComponentPath(ctx) already computes for the unset-default case, so this stays compatible with provisioned working directories and metadata.component aliasing for free. Workflows and custom commands are unaffected -- they have no "current component" concept, so bare-relative values there still resolve against the process cwd exactly as before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 7 open Dependabot alerts Bump pnpm.overrides floors for transitive npm dependencies flagged by Dependabot (all patch/minor bumps within the same major, not blocked by dependabot.yml's major-version ignore policy): - js-yaml 3.15.0 -> 3.15.1 (cloudposse#269, GHSA-5p4m-2wfm-xmqj: quadratic CPU consumption in !!omap resolution, high) - js-yaml 4.3.0 -> 4.3.1 (cloudposse#268, same advisory, 4.x line, high) - mermaid 11.16.0 -> 11.16.1 (cloudposse#267, cloudposse#266, cloudposse#265, cloudposse#264, cloudposse#263: five advisories ranging low-medium) NOTICE unchanged (no license changes). Verified: pnpm install regenerated website/pnpm-lock.yaml with the bumped versions resolved, atmos fix lint (patch-scoped, no Go files touched) is a no-op, and cd website && npm run build succeeds with no new broken links. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): fix markdownlint MD018 in working-directory fix record Address CodeRabbit finding on PR cloudposse#2880: a paragraph wrap left "cloudposse#2880):" at the start of a line, which markdownlint's atx-heading rule (MD018) flags as a heading missing a space after the hash. Rewrap so the line doesn't start with a bare hash-prefixed token. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(runner/step): close remaining working_directory anchoring gaps Found by field-testing the working_directory fix against real hooks, workflows, and a live docker build instead of just unit tests: - workdir step: `source` (local relative path) now anchors to `working_directory` via a new `sourceprov.WithBaseDir` option, matching `path`'s existing anchoring instead of silently falling back to the process cwd. - workflow-level `working_directory:` default now reaches extended step types (archive/file/junit/workdir/container), not just shell/exec/atmos steps, which silently ignored it before. - step-level `working_directory` now expands a leading `~`, matching the tilde expansion --chdir already gets. - Corrected agent-skills/atmos-steps and the workflow/hooks docs, which claimed a single blanket CWD-vs-base_path rule that doesn't match actual per-surface behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(steps): document handler-field and unset-hook working_directory contracts Addresses CodeRabbit feedback on PR cloudposse#2880: state that relative handler fields (source, destination, path, files, context) resolve against the already-resolved working_directory, that a container Dockerfile resolves relative to context rather than working_directory directly, and that an unset kind: step hook working_directory (not just a bare value) anchors to the component's own working directory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(runner/step): isolate CI base-resolution test from ambient GITHUB_EVENT_PATH TestSetDescribeAffectedFlagValueInCliArgs_BaseResolution's merge_group subtest only overrode GITHUB_ACTIONS/GITHUB_EVENT_NAME/GITHUB_BASE_REF, so it depended on $GITHUB_EVENT_PATH being unset in the ambient environment. That's true for local/regular CI runs, but not when the test itself runs inside a real GitHub Actions merge_group job (i.e. the merge queue): the real event payload's merge_group.base_sha then takes precedence over the simulated GITHUB_BASE_REF, resolving to a SHA instead of the "refs/remotes/origin/main" ref the test asserts. This was failing the merge queue for this PR and, independently, for cloudposse#2900 and cloudposse#2903 as well -- confirmed pre-existing and unrelated to any of their changes. Fixed by explicitly forcing GITHUB_EVENT_PATH="" so the subtest deterministically exercises the fallback path it's meant to test, regardless of the real job's event context. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 5 of 7 open Dependabot alerts - github.com/go-git/go-git/v5: v5.19.1 -> v5.19.2, fixing GHSA-hc8v-wwc9-vgxm (symlink traversal in worktree operations) and the companion malicious reference-name advisory (cloudposse#270, cloudposse#271). Patch-level bump, no API changes. - website: dompurify pnpm override ^3.4.12 -> ^3.4.13, fixing an IN_PLACE hook removal XSS (cloudposse#272). - website: nanoid pnpm overrides bumped to ^3.3.17 (added a second selector, nanoid@^3.3.16, to also catch postcss's own nanoid range, which the existing nanoid@3.3.3 selector didn't match), fixing two indefinite-loop DoS advisories (cloudposse#273, cloudposse#274). The nanoid 5.x line was already patched by the existing override. - NOTICE regenerated for the go-git/dompurify/nanoid version bumps. Not fixed: image-size (cloudposse#275, cloudposse#276, both DoS via infinite loop) has no patched release yet as of this commit -- GitHub's advisories list "<= 2.0.2" as vulnerable with no first_patched_version. Left as-is pending an upstream fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>
Marko Petrovic (gitbluf)
pushed a commit
to gitbluf/atmos
that referenced
this pull request
Aug 11, 2026
…custom commands and workflows (cloudposse#2882) * feat(workflows): make custom commands and workflows a complete task-runner replacement Closes the remaining gaps that kept teams running go-task alongside Atmos: - Named cross-unit dependencies: dependencies.commands/dependencies.workflows on custom commands and workflows, with automatic dedup of identical invocations, parameterized invocations as distinct graph nodes, and concurrent-by-default execution via the existing scheduler. - Freshness-based step skipping: inputs.sources/artifacts.paths skip a step when nothing has changed since its last successful run (implicit when: checksum.changed); precondition.tools skips a step when a required tool is already on PATH (implicit when: "!precondition.success"). Exposes checksum.changed/timestamp.changed/ precondition.success as when: CEL facts, plus structured per-file records for custom comparisons. - continue: always step field, mirroring GitHub Actions' continue-on-error: a step's own failure is forgiven, later steps still run, overall exit status unaffected. - Fixed type: parallel/type: matrix steps silently failing in custom commands (only workflows supported them) -- the exact recipe the go-task migration guide recommended for concurrent dependents. - platforms via when: CEL facts (os/arch/platform), native per-command aliases:/internal:, and values: constraint on flags/arguments with an interactive picker. Relocates cmd/custom_command_dependency_adapter.go and cmd/custom_command_values.go into pkg/taskgraph/adapters and pkg/flags respectively, so this logic is unit-testable in isolation instead of coupled to cmd's live command registry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(roadmap): link taskfile-convergence milestones to PR cloudposse#2882 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(ci): fix Windows shell-escape path corruption and CodeQL alloc overflow Windows Acceptance Tests: unquoted backslash paths embedded in shell Command strings get corrupted by mvdan/sh (pkg/utils/shell_utils.go parses commands with bash syntax, which consumes unquoted backslashes as escapes). Apply filepath.ToSlash() to every path used inside a shell Command string across the freshness/dependency/precondition test suites; forward slashes are valid path separators on Windows too. Also give freshness state Save() a uniquely named temp file per write (os.CreateTemp) since pkg/cache.FileLock is a documented no-op on Windows, so a fixed temp filename let concurrent writers collide. CodeQL: pkg/taskgraph.RefsFromDependencies allocated with len(a)+len(b), which go/allocation-size-overflow flags as a potentially overflowing sum; size the capacity hint to a single len() instead. * fix(ci): tolerate mvdan/sh CRLF+trailing-space on Windows in test assertions TestCustomCommandIntegration_ParallelStepWithNeeds failed on Windows CI with an exact-match assertion against shell-redirected file content: mvdan/sh's `echo`+`>>` produced "first \r\nsecond \r\n" there instead of "first\nsecond\n". Reproduced locally that the redirect itself correctly isolates fd1 from the live-display writer (no leaked/duplicated output), so this is a shell/OS text formatting difference Atmos doesn't control, not a functional bug. Strengthen the shared splitNonEmptyLines test helper to trim each line (handles \r and trailing whitespace) and switch this test's assertion to use it, matching how sibling dependency tests already tolerate line content. * fix(workflows): fix 4 concurrency/control-flow bugs in command dependencies and freshness checking Found via field-testing the task-runner dependency/freshness feature; each is fixed with a failing-first regression test: - pkg/taskgraph/adapters/cobra_command.go: same-name dependency dispatches (e.g. the same command depended on twice with different flags) resolve to one shared *cobra.Command and now serialize per-target instead of racing on its mutable flags/context, and reset every non-overridden flag to its declared default before each dispatch instead of silently inheriting a prior dispatch's leftover value. - cmd/cmd_utils.go + cobra_command.go: a step failure inside a dependency's own execution no longer hard-exits the whole process before taskgraph.Run's fail: mode handling (wait_all/fail_fast/best_effort) can see it -- failures now report through a dependency error sink instead. - cmd/cmd_utils.go + internal/exec/workflow_utils.go: a step's freshness-referencing `when:` (timestamp.changed, structured sources/artifacts) no longer gets silently evaluated against an empty pre-check context, which always read false and skipped the whole command/workflow. - internal/exec/workflow_dependency_adapter.go: workflow-depends-on-command subprocess dispatch now resolves its own binary path via os.Executable() instead of the bare "atmos" (PATH lookup), which could silently run an unrelated installed version instead of the active build. examples/task-runner-dependencies/ is a new, durable fixture exercising all of the above end to end. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(ci): make LinePrefixWriter flush multi-line writes as one atomic burst writeLine locked/unlocked the shared writeMu per individual line rather than per flush. When one Write() resolved into multiple lines (e.g. a \r-separated progress update followed later by its completion), releasing the lock between them let a concurrently writing sibling node's entire output interleave in the gap. Write/Flush now hold writeMu across every line one call flushes. Fixes the CI failure in TestExecuteTerraformConcurrentHooksUseNodeWriters (pkg/scheduler/adapters/terraform_test.go), the only real failure in the macOS acceptance-test job's log. Also fixes a numbered-list indentation lint finding in the previous commit's fix doc. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workflows): address CodeRabbit review findings on PR cloudposse#2882 - cmd/cmd_utils.go: propagate cmd.Context() to taskgraph.Run so Cobra cancellation reaches dependency execution instead of using context.Background(); generalize the dependency-error-sink helper and route every error path in executeCustomCommand (~28 sites: argument processing, dependency/tool resolution, working-directory resolution, validation, component_config, ENV var resolution, per-step auth) through it, not just step-execution failures. - internal/exec/workflow_utils.go + workflow_dependency_adapter.go: fix workflow-depends-on-workflow redundantly re-resolving and re-running its own dependency graph (a diamond dependency shared by two parents ran 3x instead of once) by adding a dependencies-resolved marker for nested ExecuteWorkflow calls, mirroring the existing command-side mechanism. - pkg/hashfile/hashfile.go: fix two real hash collisions (path/content concatenation ambiguity, and losing directory identity by hashing only the basename) with length-prefixed records and full-path hashing; stream file reads via os.Open + io.Copy instead of loading whole files into memory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(ci): regenerate JSON schema for UnitDependencies string shorthand The generated atmos.yaml schema rejected the plain-string shorthand form of dependencies.commands/.workflows entries, even though schema.UnitDependencies' UnmarshalYAML has always accepted it. Add the missing applyPolymorphicOverrides entry (mirroring the existing Tasks entry) and regenerate the schema, fixing TestGeneratedSchemaAcceptsRealConfigs failures on the Linux and macOS acceptance-test CI jobs. Also fix an EditorConfig list-continuation indentation issue in a previously committed fix doc, caught by the pre-commit hook while committing this change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(cmd): use Go-native marker writer instead of shell echo/redirect Replace the echo/>> shell redirect in the values: constraint tests with the package's existing customCommandWriteHelperCommand os.Executable() helper, matching the pattern already used throughout custom_command_integration_test.go for marker-file writes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: retrigger CI Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workflows): address CodeRabbit review findings on PR cloudposse#2882 (round 2) Fixes: - pkg/runner/freshness/checker.go: RecordSuccess early-returned when inputs == nil, so an artifacts-only step never persisted state and reran forever even once its artifact existed and was unchanged. Widen the caller gates and hash an empty source list instead of skipping the record entirely. - pkg/taskgraph/adapters/cobra_command.go: a dependency dispatch permanently left its dependencies-resolved marker and drained error sink on the target *cobra.Command's context. Restore the original context via defer so a later top-level invocation of the same command object (long-lived processes, test suites reusing RootCmd) doesn't inherit stale dispatch state. - pkg/taskgraph/taskgraph.go: a `fail: best_effort` entry silences the whole run's failures, including siblings that declared no fail: at all -- documented as explicitly run-wide (not per-entry) on UnitDependency.Fail, and the swallowed error is now logged at warn level so it isn't invisible. - pkg/datafetcher/schema/atmos/manifest/1.0.json + pkg/datafetcher/schema/stacks/stack-config/1.0.json: the shared "dependencies" definition only modeled tools/components/files/folders, so a documented dependencies.commands/dependencies.workflows declaration was rejected by the manifest schema (additionalProperties: false) and had no typed shape in the stack-config schema. Also removes a duplicate "dependencies" key in the manifest's workflow_manifest object (Biome noDuplicateObjectKeys). - pkg/schema/command.go: FindCommandByName resolved a bare name to the first depth-first match, so a config with duplicate global/nested command names could route a dependencies.commands reference to the wrong target. Now reports ambiguous=true instead of guessing; CommandLookup surfaces this as a clear error during graph-building, before any dispatch happens. Investigated, not applied: - internal/exec/workflow_utils.go's stepExecutorState global can race when taskgraph.Run dispatches multiple sibling dependencies.workflows entries concurrently. Documented the failure mode and why a quick mutex is unsafe here (deadlocks on multi-level dependency chains, or leaves a stale-pointer race window) -- a real fix means threading a *stepPkg.StepExecutor through ExecuteWorkflow instead of reaching for the package-level var, which is a larger, separately-scoped refactor. Verified as correct behavior, not bugs (added regression tests either way): - cmd/cmd_utils.go's unforgiven-failure step loop deliberately doesn't break -- it matches the pre-existing workflow executor's identical pattern and GHA's own if:success()/if:failure() semantics, so a when:failure handler step still runs after an unforgiven failure. - internal/exec/custom_command_control_adapter.go's TemplateData callback ignoring its matrix argument matches the workflow control adapter's identical, already-correct pattern -- pkg/workflow/control.go's controlTemplateData injects matrix independently of the callback. Replaces the remaining shell echo/redirect/exit test fixtures in cmd/custom_command_control_test.go with Go-native os.Executable() helpers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 7 Dependabot alerts (js-yaml, mermaid) Bump the pnpm overrides pinning js-yaml and mermaid to their patched versions -- all within the same major version, so no dependabot.yml ignore-policy exception is needed: - js-yaml@^3: 3.15.0 -> 3.15.1 (GHSA-5p4m-2wfm-xmqj, alert cloudposse#269) - js-yaml@^4: 4.2.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, alert cloudposse#268) - mermaid@^11: 11.15.0 -> 11.16.1 (GHSA-rhh3-jpg6-66xh cloudposse#267, GHSA-c4c3-pg64-4m4v cloudposse#266, GHSA-6x64-9x62-f2gx cloudposse#265, GHSA-3rrr-jr9j-h3q3 cloudposse#264, GHSA-2v8p-3f2j-5mp7 cloudposse#263) Verified: pnpm install regenerates the lockfile at the patched versions, `npm run build` succeeds, and NOTICE is unchanged (no license drift). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workflows): unit_dependency requires name, raise patch coverage toward 85% Schema fix (CodeRabbit): - pkg/datafetcher/schema/atmos/manifest/1.0.json + stacks/stack-config/1.0.json: unit_dependency accepted an entry with no `name` (e.g. `{}` or `{flags: {env: dev}}`), producing a reference with an empty name that only failed later during graph construction instead of failing fast at schema validation. Add "required": ["name"] to both copies. Coverage (patch coverage was 79.12% against an 85% target; 224 lines missing): adds real, behavior-asserting tests across cmd, internal/exec, pkg/condition, pkg/datafetcher, pkg/flags, pkg/hashfile, pkg/runner/freshness, pkg/schema, pkg/taskgraph, and pkg/workflow -- error paths, edge cases, and previously uncovered branches added by this PR's dependencies/freshness/continue/matrix work. Notably: pkg/flags/constrained.go gained isInteractiveFn/promptForValueFn DI seams (mirroring the existing pattern in cmd/secret/deps.go) so the interactive-prompt branches of ValidateConstrainedFields are testable without a real TTY. Genuinely untestable lines (defensive/unreachable code, no injection seam, TTY-only, or requiring real network/toolchain access) are left uncovered with the reasoning documented at each call site rather than padded with tautological tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workflows): quote test-helper exe paths for real host shell on Windows customCommandWriteHelperCommand and its siblings used Go's %q to quote os.Executable()'s path in the shell command string. %q applies Go-syntax escaping, doubling every backslash in a Windows path. Steps nested inside a `type: parallel`/`type: matrix` group run through pkg/workflow/control_executor.go's controlShellInvocationForOS, which (absent a wired ShellRunner) shells out to the real host shell -- cmd.exe /C on Windows -- instead of the in-process mvdan/sh interpreter used by top-level shell steps. Native cmd.exe doesn't collapse a doubled backslash back to one, so %q corrupted the executable path there, failing TestCustomCommandIntegration_ParallelStepWithNeeds on the Acceptance Tests (windows) CI job. Switched to a plain double-quote wrap, valid unescaped syntax in both POSIX shell and cmd.exe for a path with no embedded quotes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(process): use verbatim CmdLine for cmd.exe /C on Windows The previous fix (3dec2e9) stopped double-quote-formatting from doubling backslashes in a Windows executable path, but TestCustomCommandIntegration_ ParallelStepWithNeeds still failed CI: a `type: shell` step nested in a `type: parallel`/`type: matrix` group routes through pkg/workflow/control_executor.go's controlShellInvocationForOS, which -- absent a wired ShellRunner -- runs the raw command through the real host shell (`cmd.exe /C <command>` on Windows) via process.DefaultRunner's plain os/exec.CommandContext(Command, Args...). Go's default Windows arg escaping re-quotes and backslash-escapes that whole command string (it contains spaces), corrupting the quote characters already wrapping the executable path before cmd.exe ever parses it -- the same class of bug pkg/process/shell_command_windows.go's NewShellCommand already solved for session-attached commands, by bypassing Args-based escaping entirely with a verbatim SysProcAttr.CmdLine ("<shell>" /S /C "<command>"). Extend that same fix to process.DefaultRunner.Run: applyWindowsCmdExeQuoting detects the `cmd.exe /C <command>` shape controlShellInvocationForOS produces and rewrites the *exec.Cmd to use the verbatim CmdLine, exactly like NewShellCommand, instead of the default per-argument escaping. No-op everywhere else (POSIX exec.Cmd passes Args to execve verbatim; other Program/Args shapes on Windows keep normal escaping). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): record windows parallel/matrix shell child quoting fix Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * refactor(workflows): rename step field precondition to preconditions Matches the pluralization convention already used by inputs/artifacts/ dependencies for a single YAML block that can hold multiple check kinds (currently tools; more may follow). Renames the Go type/field, the CEL fact (precondition.success -> preconditions.success), the hand-maintained manifest/stack-config JSON schemas, and all docs/examples. Safe now since this field hasn't shipped yet (PR cloudposse#2882 is still open). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate nanoid infinite-loop DoS Dependabot alert Bumps the pnpm.overrides target for nanoid's 3.x line from ^3.3.15 to ^3.3.17 (patched: GHSA-2v37-7h3g-55p8 / CVE-2026-67213), and adds an override for postcss's own `^3.3.16` request range so it resolves to the same patched version. The 4.x/5.x override already resolved to 5.1.16, past the patched 5.1.6 cutoff, so it needed no change. image-size's two DoS alerts (GHSA-w3rx-r6r6-pgpr, GHSA-5p2g-fcmc-qvqq) are not fixed here: both report first_patched_version=null and npm has no image-size release past 2.0.2 yet, so there is nothing to bump to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(workflows): note GOBIN/PATH requirement in preconditions example Addresses CodeRabbit review comment on PR cloudposse#2882: `go install` writes to GOBIN (or GOPATH/bin) rather than updating PATH directly, so the install-stringer example needs to say that directory must already be on PATH for preconditions.tools to find the binary on a later run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workflows): address CodeRabbit review findings on PR cloudposse#2882 (round 3) Verified each finding against current code, fixing what was still valid and skipping what was already resolved or working as intended (documented inline where relevant). Fixed: - cmd_utils.go: hoist argumentsData/flagsData construction and values: validation above the per-step loop, so interactive prompts fire once per command invocation instead of once per step. - pkg/taskgraph: new ErrMissingRefMetadata sentinel instead of reusing ErrUnknownDependencyKind for a missing-graph-metadata condition. - internal/exec: wrap raw file-read/YAML-parse/executable-resolution errors with static sentinels (ErrReadFile, ErrInvalidWorkflowManifest, new ErrResolveExecutablePath) for errors.Is() classification. - pkg/flags: wrap PersistentFlags().Set failures with errUtils.ErrSetFlag; add a ValueKind (flag vs argument) parameter to ValidateValue so a values:-constrained positional argument is never reported as an invalid flag. - pkg/hashfile: wrap file-operation errors with static sentinels; replace a hardcoded Unix path in a test with a cross-platform t.TempDir() path. - pkg/runner/freshness: wire the previously-dead ErrGlobInvalid sentinel into Glob()'s real error path; harden recordPath against a path-traversing state key; clean up stale Save() *.tmp files left by a killed process. - pkg/process: document that NewShellCommand requires trusted config input. - pkg/schema/task.go: fix a doc-comment merge bug where Task's doc comment had been swallowed into Inputs' (no blank line between adjacent type declarations), leaving Task undocumented and Inputs mis-described in the generated JSON schema. Regenerated pkg/datafetcher/schema/atmos/config/1.0.json. - Docs: add a language tag to a fenced code block in a fix log. - Tests: doc comments on 6 exported test functions; migrate 3 test files' echo-based shell fixtures to existing Go-native helpers; strengthen the same-file/cross-file dependency tests to assert execution ORDER via a shared log, not just that both steps ran; switch the preconditions.tools regression test off the "go" binary onto the running test binary's own os.Executable() path. Skipped as already fixed/working as intended (verified against current code): custom-command fail-stop behavior (contradicted by an existing test documenting the no-break loop as deliberate), matrix TemplateData's ignored matrix arg (by design -- pkg/workflow/control.go injects .matrix after the callback), FindCommandByName ambiguous-name handling, cobra_command.go context restoration, taskgraph FailBestEffort logging + run-wide Fail docs, RecordSuccess artifacts-only handling, schema unit_dependency required: ["name"], preconditions.mdx GOBIN/PATH note, and a claimed duplicate `dependencies` schema property (schema has since regenerated/drifted). The ci.cache.includes doc finding was factually wrong (the real field is ci.cache.paths per pkg/schema/schema.go) and was left unchanged. Skipped as real but out of scope for a minimal pass: the stepExecutorState global-state race across concurrently-dispatched sibling workflows and splitting the ~700-line ExecuteWorkflow into helpers (both already documented in-code as known/deferred); two pure test-organization nitpicks (table-driven consolidation, splitting checker_test.go into separate files); one shell-fixture migration that couldn't be confidently line-matched after drift. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com>
Michael Pursifull (arcaven)
pushed a commit
to arcaven/atmos
that referenced
this pull request
Aug 19, 2026
…cloudposse#2879) * test(container): cover combined buildx driver/cache/tags/context args Strengthens pkg/container's pure arg-building test with a case combining engine, driver, cache, custom dockerfile/context, and tags in a single config, closing the one remaining gap versus per-field-only coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(git): tolerate config errors for CI git-clone bootstrap pre-Cobra atmos git clone in a fresh CI workspace (no atmos.yaml yet, e.g. a profile referenced by CI config) failed with "profile not found" before ever attempting the clone, and ATMOS_CI=true had no effect. Execute() runs an initial cfg.InitCliConfig before Cobra resolves any command; only the second, PersistentPreRun-scoped InitCliConfig call knew how to tolerate the CI bootstrap clone's expected missing config (applyCIGitCloneBootstrap), so the first call's error aborted the process before that check could run. Add isCIGitCloneBootstrapArgs, an os.Args-based equivalent of the existing cmd-aware bootstrap check, so the pre-Cobra handler recognizes the same no-argument `atmos git clone` shape and defers to the same ATMOS_CI/CI-provider resolution (via the new exported CIGitCloneModeRequestedFromEnv) before Cobra ever parses the command. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): record CI git-clone bootstrap profile fix Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(git): parse CI bootstrap flags with real pflag arity, not a heuristic The pre-Cobra CI git-clone bootstrap check (added in the prior commit) disqualified the bootstrap on any bare, non-"-"-prefixed token, including a space-separated flag value like the "0" in `--depth 0`. That misread a value-taking flag's argument as a positional repo name/URI, so the exact reported reproduction (`atmos git clone --ci --depth 0` in a fresh CI workspace) still failed on "profile not found". Replace the heuristic with CIGitCloneBootstrapRequestedFromRawArgs, which parses the clone-specific args against a throwaway command carrying the real clone flag set (a fresh newCloneParser() instance, never the shared singleton) via actual pflag parsing, then defers to the existing CICloneBootstrapRequested. This also lets an explicit --ci/--ci=false in the raw args be honored before Cobra resolves the command, which the removed env-only CIGitCloneModeRequestedFromEnv could not do. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): update CI git-clone bootstrap fix record for pflag rewrite Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(schema): decode with: into Build/Run/Push/Inspect for custom commands Fixes cloudposse#2876. A custom command's `type: container` step with a `with:` block (engine, driver, cache, tags, etc.) silently dropped everything, falling back to a bare `docker build -f Dockerfile .`, when loaded from a commands.yaml merged into atmos.yaml's Viper config tree. Root cause: `with:` is polymorphic -- decoded into Build/Run/Push/Inspect for `type: container` steps, or the generic With map otherwise -- but that promotion lives entirely in Task.UnmarshalYAML/WorkflowStep.UnmarshalYAML (go-yaml's yaml.Unmarshaler interface), invoked only when something calls yaml.Node.Decode directly (e.g. standalone workflows/*.yaml files via pkg/utils.UnmarshalYAMLFromFile). Custom commands merged into atmos.yaml decode via Viper's mapstructure pipeline (TasksDecodeHook -> decodeTaskFromMap), which never invokes yaml.Unmarshaler and had no equivalent promotion, so `with:` only ever reached the raw generic map. decodeTaskFromMap now pulls `with:` out before the mapstructure decode and replays the same polymorphic decode via decodeStepWith, round-tripping the value through YAML so both code paths share one implementation and can't drift apart. Reproduced through the real production paths per the bug report's request: config loaded via InitCliConfig (pkg/config), and the full custom command executed via RootCmd through a fake logging docker executable (cmd/) -- not by manually constructing schema.Task/WorkflowStep/ContainerBuildStep literals, which would have bypassed the actual decode bug. Added a complementary test proving workflow-file and custom-command steps decode with: identically, per the report's public-contract requirement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir): sanitize nested component names in BuildPath A component name containing "/" (e.g. a nested layout like ecs/cluster) made workdir.BuildPath produce a real extra subdirectory instead of a single path segment, since the name was interpolated into "<stack>-<name>" without escaping and then filepath.Join'd. That put the nested component's workdir one level deeper than a flat component's at the same stack. Any path computed relative to the workdir -- most visibly a relative `backend.local.path` template like `../../../.context/tfstate/...` -- therefore climbed to a different real ancestor for the nested component than for the flat one, silently writing state under a different root (<repo>/.workdir/.context/... instead of <repo>/.context/...) even though both components used the identical backend config. Sanitize the component name the same way internal/exec/terraform_generate_ backends.go already does for backend template context: replace "/" with "-" before building the workdir directory name. BuildPath is the single formula reused by the source provisioner and by internal/terraform_backend's JIT-workdir state lookup, so both pick up the fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): record custom-command with: and workdir path-depth fixes Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(terraform/output): retarget containment-guard test at stack traversal; surface cached output lookups BuildPath now sanitizes "/" out of component names, so the containment guard test's traversal-via-component vector no longer escapes BasePath. Retarget it at the stack argument, which isn't sanitized the same way and still needs the guard. Also make cache-hit output lookups emit the same visible "Fetching ..." notification a real fetch would, instead of only a Debug-level log, so a second output lookup on an already-cached component isn't silently invisible. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(terraform/output): strip ANSI before asserting cache-hit visibility; correct fix-log formatter name CI forces color output (CI=true), which makes the markdown-based UI renderer split "Fetching vpc_id ..." into multiple ANSI-styled runs right at the literal underscore, without dropping or reordering any visible characters. Strip ANSI before the assert.Contains checks, matching the ansi.Strip convention already used elsewhere in the test suite. Also correct the fix-log's "gofmt" validation bullet to "gofumpt", the formatter this repo actually mandates and runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore(claude): deny gofmt in Claude Code permissions Repo mandates gofumpt, not gofmt (CLAUDE.md, .golangci.yml). Denying the raw command prevents Claude Code from running gofmt directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test: close Codecov patch-coverage gaps on PR cloudposse#2879 Adds behavior-focused tests for the 5 lines Codecov flagged as uncovered on this branch's added code: isCIGitCloneBootstrapArgs's len(args) < 1 guard, decodeTaskFromMap/decodeStepWithFromMapValue's three error-wrap branches (invalid container action, yaml.Marshal failure via a yaml.Marshaler that errors, yaml.Unmarshal failure via a dangling YAML alias), and resolveOutputFromCache's cache-miss and getOutputVariable- error branches. No production code changes; no assertions weakened. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): record terraform/output CI fixes; correct gofmt->gofumpt typo Two fixes from this branch (cache-hit output lookups now visible; containment-guard test retargeted at the still-open stack-traversal vector after the workdir fix closed the component-name one) had no docs/fixes/ record. Also corrects a stale "gofmt" mention in the git-clone-ci-bootstrap doc to "gofumpt", matching the correction already applied to the container with-block doc. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(registry): widen timing margin in provider-mirror concurrency test Fixes a flaky Windows Acceptance Tests failure: resolving 10 platforms took 784ms against a 750ms threshold, even though that's nowhere near the 1.5s serial floor the test guards against. Raises the bound to 4/5 of the serial floor (1200ms) for headroom against normal CI timing variance, Windows runners especially. No production code changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir): sanitize nested component names in createWorkdirDirectory createWorkdirDirectory duplicated the unsanitized stack-componentName formula that BuildPath was already fixed to sanitize, so a local (non-source) component with provision.workdir.enabled: true and a nested name still got a workdir one level deeper than a flat sibling, silently shifting where relative backend.local.path state resolves. Now delegates to BuildPath so both formulas can't drift apart again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(provisioner): guard path traversal in source-vendoring fallback DetermineTargetDirectory's non-workdir fallback (the default vendoring path when provision.workdir.enabled is unset) joined the component base path with the raw component name with no containment check, so a component named with ../ segments could vendor outside components/terraform/. Adds the same absolutize-and-prefix containment guard already used by the two other BuildPath-derived callers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cli): decode and execute custom-command step-level container: overrides Sibling gap to the with: block fix (docs/fixes/2026-08-05-custom- command-container-with-block-dropped.md): a custom command step's container: override went through three independent failures. The bare boolean opt-out (container: false) broke InitCliConfig for the whole atmos.yaml because decodeTaskFromMap never round-tripped container: through YAML the way with: now does. The mapping form decoded fine but was never consulted at execution time -- the custom-command step loop always ran type: shell steps on the host. And once both of those were fixed, container: false still ran the step inside a container because cloneCommand's JSON round-trip silently dropped WorkflowContainer.Enabled (json:"-"), inverting the opt-out. Fixes all three: decodeTaskContainerFromMapValue mirrors the with: fix's round-trip for container:, cmd/cmd_utils.go's step loop now reuses the same pkg/workflow/container.go session logic the workflow-file path already uses, and WorkflowContainer gained MarshalJSON/UnmarshalJSON so Enabled survives a JSON round-trip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(container): pass restart/healthcheck through to ephemeral run steps ContainerRunStep.Restart/.HealthCheck decoded fine but EphemeralConfig (the runtime config for type: container, action: run steps) had no such fields, and buildRunConfig never populated them -- unlike the persistent-component path, which already wires the same settings. Adds the fields to EphemeralConfig and populates them via the existing (previously unused for this path) RestartPolicyFromStep/ HealthCheckFromStep helpers, so --restart/--health-* flags now reach the real docker/podman invocation for step-based container runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(config): surface swallowed import errors and hide-nothing validation Two DX gaps where already-computed diagnostic detail never reached the default log level: a YAML syntax error in an import:-loaded commands file was silently swallowed (Debug-only), leaving only a generic "Unknown command" with no hint a config file failed to parse; and container-step validation (missing required field, invalid pull: value) already computed the field/step/type and the bad value but only exposed them via --verbose or dropped them entirely. LocalAdapter now pairs its existing log.Debug with a ui.Warning naming the file and parse error. ValidateRequired's default message now names the field; invalidContainerField now echoes the actual invalid value typed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 7 Dependabot alerts in website dependencies Bumps three pnpm.overrides pins to their patched releases, all within the major-version line dependabot.yml's ignore policy allows: - js-yaml 3.15.0 -> 3.15.1 (GHSA-5p4m-2wfm-xmqj, alert cloudposse#269) - js-yaml 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, alert cloudposse#268) - mermaid 11.16.0 -> 11.16.1 (GHSA-rhh3-jpg6-66xh cloudposse#267, GHSA-c4c3-pg64-4m4v cloudposse#266, GHSA-6x64-9x62-f2gx cloudposse#265, GHSA-3rrr-jr9j-h3q3 cloudposse#264, GHSA-2v8p-3f2j-5mp7 cloudposse#263) Verified via `pnpm run build` in website/; NOTICE unchanged (no license changes from these patch bumps). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(schema): reject unknown fields in container step with:/driver: blocks The JSON Schema for a container step's with: block already documented it as "validated by the step handler at run time," but nothing fulfilled that promise for unknown keys -- yaml.Node.Decode (used by both the workflow-file and custom-command loading paths) has no strict/KnownFields mode, so a typo'd field like `platforms:` was silently dropped with no error. This masked a real pre-existing test bug: TestWorkflowStep_DecodeWith's push-action case used `tag: v1` instead of the actual `tags: []string` field and passed anyway. decodeYAMLInto and ContainerDriverConfig.UnmarshalYAML now decode through a stream-level yaml.Decoder with KnownFields(true) (the only place go-yaml exposes strict decoding) instead of plain node.Decode, scoped narrowly to the typed container structs -- the generic with: map[string]any fallback other step types use is untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 5 Dependabot alerts, 2 unpatched and deferred - github.com/go-git/go-git/v5 5.19.1 -> 5.19.2 (GHSA-hc8v-wwc9-vgxm cloudposse#270, GHSA-qgq7-7hm3-q39j cloudposse#271), pulling in patch bumps to golang.org/x/{mod,net,text,tools} via go mod tidy - nanoid pnpm override widened from a pinned 3.3.3->^3.3.15 mapping to a ^3->^3.3.17 range so every 3.x requester resolves past both vulnerable versions (GHSA-28wg-ghj8-5hjv cloudposse#274, GHSA-2v37-7h3g-55p8 cloudposse#273); previously two different 3.x versions were resolving simultaneously because the override only matched exact-version requests - dompurify pnpm override 3.4.12 -> 3.4.13 (GHSA-55q2-fjhq-7xh7 cloudposse#272) image-size alerts cloudposse#275/cloudposse#276 (GHSA-5p2g-fcmc-qvqq, GHSA-w3rx-r6r6-pgpr) have no first_patched_version yet in any release line -- left open, nothing to bump to. Verified via `go build ./...`, targeted go-git-consumer package tests, and `pnpm run build`; NOTICE regenerated (version-string changes only, no license changes). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd): update stale path assertions in container build argv test TestCustomCommandContainerBuildPassesWithBlockToDocker asserted the buildx argv contained bare "Dockerfile" and "app" strings. Since main's cloudposse#2880 (resolve relative paths against step.WorkingDirectory), context: and dockerfile: values in a with: block are correctly resolved to absolute paths before reaching docker, so the argv now contains the full resolved paths instead of the bare relative strings. Docker still receives the same file -- update the assertions to check for the resolved absolute paths. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(provisioner): pass OutputWriters in nested-workdir Provision test Merging origin/main's fix(terraform): prevent concurrent output corruption (cloudposse#2898) added a fourth provisioner.OutputWriters parameter to (*Service).Provision. Every call site main's own history knew about was updated by that commit, but this branch's own TestServiceProvision_NestedComponentName_SanitizesLikeBuildPath (added by an earlier, unrelated fix on this branch) didn't exist in main's history, so git's auto-merge had nothing to reconcile it against and left the stale three-argument call in place -- a compile failure only visible once this branch actually merges main, which is exactly what GitHub Actions' implicit PR-merge checkout does on every CI run regardless of whether this branch has locally merged yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(cmd): assert cache/driver flag values, not just presence TestCustomCommandContainerBuildPassesWithBlockToDocker only checked that --cache-from/--cache-to/--builder appeared somewhere in the argv, not that they carried the configured registry ref, mode=max, or driver. The driver: block also provisions a real Buildx builder via a separate `docker buildx create` invocation (pkg/container/docker.go's ensureBuilder) that the fake runtime already recorded but the test never inspected. Adds table-driven flag-value assertions covering both the create and build invocations. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd): normalize marker path for cross-platform shell command markerPath comes from t.TempDir(), which contains backslashes on Windows. The step command string goes through Atmos's mvdan/sh interpreter, which treats \ as an escape character, so the redirection target would resolve to a mangled path. Convert to slashes and quote the path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(provisioner): sanitize backslash in workdir component names BuildPath sanitized "/" in a component name to prevent it from adding a real directory level, but not "\", which is Windows' actual path separator. A crafted or copy-pasted component name containing "\" (e.g. "..\\..\\evil") would let filepath.Join/Clean treat it as real ".."-traversal segments on that platform, escaping the intended workdir root. Sanitize "\" identically and unconditionally on every platform, matching the existing "/" handling, so a given component name's workdir path also stays identical across OSes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(runner): make step-name assertion independent, cover empty type The default-message test used step name "run" and field "run.image", so the step-name assertion could pass even if ValidateRequired dropped the step name entirely, since "run.image" also contains "run". Converted to a table with a distinct step name/field per case and no shared substrings, and added a step.Type == "" case to cover the previously-untested branch that omits the "(type ...)" clause. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(schema): reject unknown fields in container: override blocks WorkflowContainer.UnmarshalYAML's mapping branch used plain value.Decode, so a typo'd field (e.g. `imgae` instead of `image`) in a workflow-level or step-level container: block was silently discarded rather than rejected -- the same class of gap already fixed for with: blocks in decodeYAMLInto. Use the existing decodeYAMLKnownFields helper instead, and add regression coverage for both the workflow-file (yaml.Unmarshal) and custom-command (mapstructure + TasksDecodeHook) decode paths. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixtures): sync workdir-nested fixture with path-safety fixes The README still documented the pre-fix behavior for the app/local-nested "known bug" scenario and the ../escape-test-nowd path-traversal probe, which are both now fixed on this branch. Re-verified both scenarios for real (atmos terraform apply/source pull against the fixture) and updated the manual-testing steps and expected output to match: the nested local component now sanitizes to a sibling workdir, and the unguarded probe now fails with ErrPathTraversal instead of vendoring outside components/terraform/. Updated the .gitignore comment on the now-defensive-only escape-test-nowd entry accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): correct formatter name, markdown syntax, typos, spelling - gofmt -> gofumpt (the repository-required formatter) - double-backtick delimiter for a code span containing a literal backtick, which single backticks can't escape in Markdown - add `text` language identifiers to unlabeled fenced output blocks - "on a already-parsed" -> "on an already-parsed" - "macos" -> "macOS" in a CI platform list Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): correct macOS spelling in CI platform list Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(provisioner): use filepath.Rel for component base path containment The naive absBase+separator prefix check breaks when componentBasePath resolves to a filesystem root ("/" on Unix, "C:\" on Windows): absBase already ends in the separator there, so the literal absBase+sep prefix ("//" or "C:\\") never matches any real descendant, rejecting every valid target with ErrPathTraversal. filepath.Rel doesn't have this edge case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(schema): wrap WorkflowContainer JSON decode error UnmarshalJSON returned the raw json.Unmarshal error directly instead of wrapping it with a static error from errors/errors.go, so callers couldn't classify a WorkflowContainer JSON decode failure the way they already can for its YAML counterpart. Wrap with the existing ErrInvalidWorkflowContainer sentinel, matching UnmarshalYAML. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixtures): sync stack-manifest comments with path-safety fixes The comments on the three path-traversal probes in this fixture's dev.yaml still described pre-fix behavior (determineSourceTargetDirectory having no containment guard, DetermineTargetDirectory's non-workdir fallback having no sanitization, createWorkdirDirectory reimplementing an unsanitized formula) even though all three are now fixed on this branch. Updated to describe the current, correct expected behavior, matching the README sync in the prior commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(provisioner,cmd): close symlink containment gap and route script steps through container overrides - pkg/provisioner/source: validateWithinComponentBasePath now resolves symlinks in the existing portion of target/base paths before checking containment, closing a bypass where a symlink under componentBasePath pointing outside it passed the old lexical-only check. - cmd/cmd_utils: type: script custom-command steps now route through StepContainerOverride/RunStepContainerOverride like type: shell steps already do, instead of silently ignoring a step-level container: override. - tests/fixtures: drop the POSIX-only /dev/stderr log destination from the source-provisioner-workdir-nested fixture. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd): propagate Cobra cancellation to custom-command step execution executor.Execute and both RunStepContainerOverride call sites in executeCustomCommand used context.Background() instead of cmd.Context(), so a Ctrl-C on the top-level invocation couldn't cancel an extended step handler or a container runtime operation. Derive one executionCtx from cmd.Context() (falling back to context.Background() for direct-test invocations), mirroring the existing depCtx pattern used for dependency graph execution in the same function. Addresses CodeRabbit review comment on PR cloudposse#2879. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): add missing comma after "e.g." in fix-log doc Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd,schema): address CodeRabbit findings on PR cloudposse#2879 Completes Cobra-cancellation propagation to the remaining context.Background() call sites in executeCustomCommand, wraps with:/container: decode failures with their static sentinel errors so errors.Is works regardless of which decode step fails, and extends cmd.NewTestKit(t) to restore RootCmd.Commands() between tests so custom commands registered by one test no longer leak into the next. Also fixes comment/doc-accuracy nits (godot periods, exported test-double doc comments, and two docs/fixes/*.md wording corrections). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(workdir): document BuildPath's separator sanitization in its doc comment CodeRabbit nitpick on PR cloudposse#2879: the doc comment didn't mention that both "/" and "\" are replaced with "-", only the inline comment did. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(cmd): add regression test for RootCmd.Commands() restoration Addresses CodeRabbit findings on PR cloudposse#2879: a dedicated table-driven test was missing for restoreRootCmdCommands (confirmed failing pre-fix, passing post-fix), plus a comment period and an inconsistent path-traversal probe description in a test fixture README. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir,schema): close BuildPath collision/traversal gaps, stop mutating caller task maps Addresses CodeRabbit findings on PR cloudposse#2879: - workdir.BuildPath encoded "/" and "-" identically, so components named e.g. "app/local" and "app-local" collided on the same workdir, sharing files, metadata, and Terraform state. Encode "/" and "\" as "--" instead. - BuildPath validated the component name but never the stack name, both of which come from user-controlled YAML; a crafted stack value could escape BasePath. Move a shared containment check into BuildPath itself so all six call sites get it, instead of the two ad hoc copies that existed before (and the four call sites that had none). - decodeTaskFromMap deleted "with"/"container" keys in place, which could mutate the caller's own map (e.g. Viper's live config tree) when earlier normalization steps returned it unchanged instead of a copy. Also converts a hard-coded JIT-workdir test case to table-driven per a separate nitpick on the same PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(fixes): fix EditorConfig indentation in workdir fix-log doc The numbered list's continuation lines used 3-space indentation (aligned under the "1. " marker), which fails the repo's indent_size=2 EditorConfig rule (want multiple of 2). CI's "Run pre-commit hooks" job caught this. Switched to the 2-space continuation indentation already used elsewhere in docs/fixes/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir,cmd): make BuildPath's encoding fully injective, restore removed RootCmd commands Addresses a second CodeRabbit review pass on PR cloudposse#2879: - The prior "/" -> "--" fix for workdir.BuildPath's component-name collision was still not injective: a component literally named "app--local" collided with "app/local" (both encode to "app--local"). Replaced it with a fully injective scheme -- escape the literal hyphen ("-" -> "-h") before encoding separators ("/" or "\" -> "-s") -- so "-" never appears unescaped in the output and no two distinct component names can produce the same encoded path segment. This changes the on-disk workdir directory name for existing hyphenated components; updated every hardcoded expected-path assertion this touched across six packages, several switched to compute the expected path via the real BuildPath instead of a hand-rolled formula so they can't go stale the same way again. - restoreRootCmdCommands (added in an earlier pass on this PR) only removed commands added to RootCmd after a NewTestKit snapshot; a command present in the snapshot but removed mid-test (via RootCmd.RemoveCommand) stayed gone for every later test. It now also re-adds any snapshot command whose Parent() is no longer RootCmd. Also fixes a godot comment-period nit and stale sanitized-name comments in a fixture referencing the earlier "--" encoding. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test: update hardcoded workdir names for BuildPath's injective encoding Fixes CI failures on all three platforms (linux/windows/macos) from the previous commit: several tests hardcoded the pre-encoding workdir directory name (e.g. "dev-vpc-remote-workdir", "dev-null-label-exports", "test-producer-from-source") instead of the new "-h"/"-s" escaped form BuildPath now produces for hyphenated component names. These packages weren't covered by the test sweep before that commit landed: pkg/ci/plugins/terraform and the tests/ acceptance suite's JIT-source and source-provisioner-workdir tests. The pkg/git TestDefaultBranchAndGitHubRepository failure on the Windows run is unrelated -- a transient runner permission error on C:/Users/runneradmin/.gitconfig, not a code issue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir): give backslash its own escape token, route CleanWorkdir through BuildPath Addresses a third CodeRabbit review pass on PR cloudposse#2879: - escapeComponentNameForPath aliased "/" and "\" to the same "-s" token, so "ecs/cluster" and `ecs\cluster` still collided. The aliasing was meant to keep a component name's encoding OS-independent, but that's already guaranteed by the encoding being pure Go string processing (never delegated to path/filepath) -- so backslash now gets its own token ("-b") at no cost to that property. - CleanWorkdir had its own separate, unsanitized stack+"-"+component formula, never routed through BuildPath. It already couldn't find workdirs for "/"-containing components; the encoding fixes on this PR widened that to ordinary hyphenated components too. Now delegates to BuildPath like every other consumer. - TestCustomCommandStepContainerFalseOptOutRunsOnHost only proved the host command ran, not that docker was never invoked. Now installs the fake container runtime and asserts its argument log was never created. - Two other fix-log docs and this PR's own fix-log doc still described earlier, now-superseded encoding examples; updated for consistency. - Replaced a slash-delimited path literal with filepath.Join in a test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir): handle filesystem-root basePath, de-tautologize workdir_path_test oracles containWithinBase's absBase+separator prefix check rejected every legitimate workdir path when basePath resolved to a filesystem root (absBase already ends in the separator there, so absBase+sep doubled up). Switch to filepath.Rel, mirroring pkg/provisioner/source/source.go's isWithinBase. Also replace pkg/component/workdir_path_test.go's BuildPath-derived expected paths with independent hand-computed literals: BuildAndResolveWorkdirPath calls the same BuildPath internally, so a setup+assertion pair that both called BuildPath would silently agree on a wrong path if the encoding ever regressed again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test: cover BuildPath's error-propagation branches across workdir consumers Adds behavioral tests for the (string, error) BuildPath signature change: stack-name path-traversal now returns errUtils.ErrPathTraversal instead of silently resolving, and every caller's new `if err != nil` branch needs its own test to prove it actually forwards/wraps that error rather than swallowing it. Also covers resolveExistingSymlinks's non-ENOENT propagation and buildWorkdirPath's empty-BasePath default, both left untested by the original patch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir,cmd,tests): contain stack traversal to component-type root, fix bootstrap flag inheritance, fix mock fixture marker pkg/provisioner/workdir/types.go: BuildPath validated the derived path against basePath only, but stack (unlike component) was never escaped before being folded into workdirName -- a stack like "../../components" resolves inside basePath while still escaping .workdir/<componentType>. Now also validates against the canonical per-component-type workdir root. cmd/git/bootstrap.go: CIGitCloneBootstrapRequestedFromRawArgs's throwaway Cobra tree only registered clone-specific flags, so an inherited global flag like --config made clone.ParseFlags reject the args and silently report no CI-bootstrap request. Now registers the real global persistent flags before parsing. tests/fixtures/.../components/terraform/mock/main.tf: was byte-identical to the vendored source-modules/mock/main.tf, including its "vendored" header -- the local component is never vendored, so its component_type marker couldn't distinguish which module actually produced a given state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir,cmd/git): address PR cloudposse#2879 CodeRabbit round and fix local-backend state loss on re-provision CodeRabbit review round on PR cloudposse#2879 (verified against current code, not just the diff it saw): - pkg/provisioner/source/source.go: attach underlying filesystem errors to symlink-resolution failures instead of discarding them. - pkg/provisioner/workdir/types.go: BuildPath rejects a stack name containing "/" or "\" instead of only checking containment after the fact, closing a workdir-collision gap (e.g. stack "team/../prod" aliasing stack "prod"). - pkg/provisioner/workdir/clean.go, cmd/terraform/workdir/workdir_helpers.go: CleanWorkdir/GetWorkdirInfo/DescribeWorkdir now honor atmos_component overrides via BuildPath. The CLI-wired DefaultWorkdirManager had its own separate, never-updated path formula that couldn't find any hyphenated component's real workdir at all -- fixed too. - pkg/provisioner/workdir/workdir.go: best-effort migration of a workdir found at the pre-escaping path onto the new encoded one, so upgrading doesn't orphan existing local state. - cmd/git/bootstrap.go: a malformed `atmos git clone --depth not-a-number` no longer gets masked by an unrelated config/profile error; Cobra's own flag-parsing error now surfaces as intended. Also fixes a real, separate bug found while testing the above: workdir sync was deleting local-backend Terraform state (terraform.tfstate) on every re-provision, since only provider lock files and the workspace-specific terraform.tfstate.d/ were protected from the sync's delete-orphaned-files pass. A local-backend component's state was silently gone after the second run. shouldSkipSyncFile now also protects terraform.tfstate, terraform.tfstate.backup, and .terraform.tfstate.lock.info. See docs/fixes/2026-08-17-pr2879-coderabbit-round-workdir-and-bootstrap-fixes.md and docs/fixes/2026-08-17-workdir-sync-deletes-local-backend-state.md for full details, and website/blog/2026-08-17-container-config-validation-and-workdir-path-encoding.mdx for the user-facing changelog. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(workdir): reject only '.'/'..' segments in stack, not every '/' CI caught a real regression from the previous commit's validateStackForPath: rejecting any stack value containing "/" broke cmd/terraform/migrate's own test fixtures (stack "deploy/test") and, transitively, three tests/cli_workdir_test.go fixtures for hyphenated component names. Only a literal "." or ".." path segment is an actual collision/traversal risk (filepath.Join's implicit Clean() can fold it away, aliasing e.g. stack "team/../prod" onto stack "prod"). A plain "/" without such a segment, like "deploy/test", is a real, already-supported nesting convention with no traversal risk -- it just becomes a real subdirectory, exactly as it always has. Narrowed the check accordingly. Also fixed tests/cli_workdir_test.go's testWorkdirShow/testWorkdirDescribe/ testWorkdirCleanSpecific fixtures, which hand-rolled a pre-escaping workdir path for a hyphenated component name instead of computing it via BuildPath -- the same class of drift the prior commit's CleanWorkdir fix addressed. testWorkdirShow/testWorkdirDescribe had been silently masking their own breakage via a weak assert.Contains check that passed on error output too; tightened to require.NoError. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * refactor(cmd): extract shared container-override step helper The "shell" and "script" custom-command step cases each built an identical workflowPkg.ContainerStepParams and called RunStepContainerOverride, differing only in the workflowStep and the display command. Extracted into a shared runContainerOverrideStep closure. No behavior change: TestCustomCommandStepContainerOverrideRunsInsideContainer (shell), its _ScriptType variant, and TestCustomCommandStepContainerFalseOptOutRunsOnHost all still pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd,workdir,terraform): address PR cloudposse#2879 CodeRabbit round 3 findings Propagate executionCtx to non-TTY shell steps and the atmos step type so Ctrl-C/prompt cancellation actually stops an in-flight custom-command step instead of letting it run to completion. Make migrateLegacyWorkdir fail closed on a rename error instead of silently creating a fresh empty workdir over an orphaned legacy directory that may hold real Terraform state. Make ExtractComponentPath propagate a BuildPath rejection instead of falling back to the source component directory, which could point Terraform at the wrong workdir on a rejected (traversal/invalid) stack. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd): make workdir clean/describe/show honor atmos_component overrides resolveComponentConfig passed the caller's already-loaded (processStacks=false) AtmosConfiguration into ExecuteDescribeComponent, which only does its own full stack-processing init when passed nil. That branch never ran, so component resolution always failed silently and every clean/describe/show call fell back to treating the component as its own instance name -- the exact failure mode atmos_component-override support exists to prevent. It now builds its own fully-processed config from the same CLI flag overrides (base-path, config, config-path, profile) the caller already derived, so overrides actually resolve. Adds three regression tests that execute the real command path against a real stack fixture and assert the manager receives the resolved atmos_component, replacing gomock.Any() assertions CodeRabbit flagged as too permissive to catch this. Also fixes two stale doc/comment references from the same review round (obsolete BuildPath encoding description; a stale fixture path in a test comment). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(cmd,provisioner): address PR cloudposse#2879 CodeRabbit round 4 findings Fixes four independent path-identity gaps in the vendoring/workdir subsystem: DetermineTargetDirectory's default vendoring target permitted resolving to the shared component-type directory itself (component name "." or "child/.."); shouldSkipSyncFile protected local-backend state files by basename, over-broadly excluding nested source files with the same name; migrateLegacyWorkdir could rename the wrong identity's directory since its legacy-name formula isn't injective across stack/component; and validateStackForPath's segment split silently dropped empty segments from a leading or repeated "/", letting a stack name alias another's workdir path. Also fixes a test helper that suppressed all panics instead of only the expected one, and corrects three stale doc references from earlier rounds. Skipped one invalid finding (adding perf.Track to pkg/schema/workflow.go would create an import cycle with pkg/perf). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
zack-is-cool
pushed a commit
to zack-is-cool/atmos
that referenced
this pull request
Sep 8, 2026
…posse#2896) * feat(migration): add Makefile, Justfile, and Taskfile migration guides Extend the atmos-migration skill and docs to cover moving task-runner orchestration (Make, Just, Task) to Atmos custom commands and workflows, alongside the existing native-Terraform/Terraform-Workspaces coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(config): stop directory's own commands: inheriting unrelated .atmos.d subcommands A directory's own inline atmos.yaml commands: entry named the same as a command discovered from git-root .atmos.d (e.g. an unrelated outer project's dev tooling) silently inherited that command's subcommand tree and other subcommand-referencing fields such as default:. Treat a leaf command with no commands: key as fully authoritative instead of merging it field-by-field against the discovered default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(validate): stacks validation no longer requires name_template/name_pattern createComponentStackMap derived a component's logical stack name via a stricter, older code path that predated zero-config filename-based stack naming (cloudposse#1934), so atmos validate stacks hard-failed on any repo that terraform plan, list stacks, and describe component already resolved stacks for fine, including this repo's own examples/native-terraform. Reuse resolveStackName's precedence (manifest name > name_template > name_pattern > filename) instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(migration): correct field-tested gaps in task-runner migration guides Field-testing the new Makefile/Justfile/Taskfile migration references against real fixtures and the real atmos binary surfaced several gaps: from-native-terraform.md's Shape B recipe used a component name that never resolved (component names must match the physical directory); workflows.base_path has no default and needs to be called out; an orphaned [private] Justfile recipe and Just's command-echo behavior weren't addressed; from-taskfile.md overstated the need for `import:` when atmos.d/.atmos.d is auto-discovered; and the migration docs sidebar order contradicted the pages' own sidebar_position values. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(migration): address CodeRabbit review on PR cloudposse#2896 - Use the terraform component name (matching the physical terraform/ directory) instead of the never-resolving infra in every single-directory Makefile/Justfile/Taskfile example, consistent with the from-native-terraform.md Shape B fix. - Reserve type: atmos for native Atmos verbs only in from-taskfile.md's Shape A guidance; calling another custom command still needs type: shell. - Fix from-justfile.md's Common Problems link fragment (verified against the actual github-slugger algorithm). - Stop telling readers import: is required for auto-discovered atmos.d/.atmos.d files in from-makefile.md and website/docs/migration/taskfile.mdx. - Document that workflows.base_path has no default in website/docs/migration/taskfile.mdx, matching the equivalent fix already applied to the agent-skill references. - Normalize from-native-terraform.md's odd-space (3/5/7) list-continuation and nested-YAML indentation to even, matching the EditorConfig multiple-of-2 rule applied to the other reference files earlier in this branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(commands): add hidden custom commands and --help=hidden topic Custom commands can now set `hidden: true` to stay runnable (directly, as a `default:` target, or from another command's steps) while dropping out of `--help` listings, completions, and the AI `atmos_list_commands` tool. This closes the gap the Just/Task/Make migration guides used to call "no match", where a `[private]`/`internal: true` recipe or task needed to be reusable across callers or invoked directly for debugging rather than folded into a single caller's step. Add a matching `--help=hidden` topic to reveal a command's hidden subcommands on demand; the default-help hint only mentions it when a command actually has one, to avoid cluttering the common case. Refresh the affected migration guides (website + agent-skills mirrors) to point at `hidden: true` instead of the old "no match" guidance, and add previously-missing coverage for Task's `internal: true` flag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(links): exclude reproducible-builds.org from link check The Check Markdown Links workflow failed on the SOURCE_DATE_EPOCH citation in docs/prd/archive-step.md with "Connection refused". The domain refuses connections from every network tested (CI, curl, and WebFetch), not just this path or CI specifically — an upstream outage, not a broken/moved link — so exclude it the same way other known-flaky external docs are already handled in this file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(migration): address CodeRabbit review on PR cloudposse#2896 - lychee.toml: narrow the reproducible-builds.org exclude to the exact SOURCE_DATE_EPOCH path instead of the whole domain, so other links on that domain stay covered by the link check. - justfile.mdx/makefile.mdx/taskfile.mdx (+ agent-skills mirrors): the "after" Terraform-apply examples ran `terraform apply terraform`, confusing the atmos verb with a component literally named "terraform" that didn't match the shown legacy `terraform/` directory layout. Rename the placeholder component to `infra` and add a one-line note on where it maps to under `components.terraform.base_path`. - cmd_utils_test.go: document why the hidden-command tests' printf/ redirection is cross-platform (Atmos's TaskTypeShell runs through the in-process mvdan/sh interpreter, not the host shell) rather than replacing it — flagged as a platform-specific-binary risk, but it isn't one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(io): make LinePrefixWriter's cross-node line batches atomic TestExecuteTerraformConcurrentHooksUseNodeWriters was failing in CI (reproduced locally under `go test -race -count=200`, ~30% failure rate): concurrent nodes' hook output was interleaving mid-record instead of staying grouped per node. writeLine() acquired the shared writeMu once per line, but a single Write() call can flush multiple buffered lines at once (e.g. a \r-terminated segment held back by a prior Write, completed by the next). Between the two per-line lock acquisitions for one node's burst, another node's own burst could interleave into the shared writer. Fixed by collecting a burst's complete lines up front and writing them under one writeMu acquisition (writeLinesLocked), so a whole burst lands as one contiguous block. Preserves the existing partial-write-error retry behavior: a failed line and everything after it, plus any trailing partial content, are restored to the buffer for the next Write/Flush to retry. Verified with `go test ./pkg/scheduler/adapters/... -race -count=500` (0 failures, was reproducibly failing before) and the full pkg/io suite, race detector, 5x. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(security): remediate 7 npm Dependabot alerts in website/ Bump pnpm.overrides for transitively-pulled packages to their patched versions, all within the semver-major bump the dependabot.yml ignore policy blocks: - js-yaml 3.15.0 -> 3.15.1, 4.3.0 -> 4.3.1 (GHSA-5p4m-2wfm-xmqj, GHSA for the 4.x line): quadratic CPU consumption in !!omap resolution, fixes cloudposse#269, cloudposse#268. - mermaid 11.16.0 -> 11.16.1: fixes cloudposse#267 (radar diagram DoS), cloudposse#266 (config API prototype pollution), cloudposse#265 (CSS injection), cloudposse#264 (Architecture diagram prototype pollution), cloudposse#263 (XY Chart infinite-loop DoS). No open CodeQL alerts. Verified with `atmos lint --changed` (0 issues) and `npm run build` in website/ (succeeds, same pre-existing unrelated broken-anchor warning as before this change). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(io): preserve unwritten suffix on partial LinePrefixWriter writes Write and stdio.WriteString can return n > 0 with an error; the prior code ignored n and restored the entire raw line on retry, so bytes the underlying writer already accepted (including the prefix) could be resent. A nil-error short write (n < len(payload)) also silently dropped the unwritten tail. Track the encoded pending payload and retry only its unwritten suffix, converting a nil-error short write into io.ErrShortWrite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(website): repair broken pnpm lockfile and migration doc links website-deploy-preview failed on pnpm install --frozen-lockfile because a duplicate nanoid@^3.3.16 override (added independently by two commits and merged from main) produced a duplicate YAML key in pnpm-lock.yaml. Also fix 5 new migration docs linking to the nonexistent /ai/agent-skills route instead of /ai/skills, which broke the docusaurus build once the lockfile issue was resolved. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(commands): qualify that internal commands don't appear in help Addresses CodeRabbit review comment: the `name` field description said names unconditionally appear in `atmos help`, contradicting the `internal` field's documented exclusion. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(migration): fix stale no-parity claims for deps/freshness now shipped cloudposse#2882 (already merged into this branch) added dependencies.commands/ dependencies.workflows and step-level inputs/artifacts, giving Atmos direct parity with Task's deps:/sources:/generates: and Make's dependency ordering and file-timestamp caching. This branch's own migration guides -- the subject of this PR -- still declared those exact features unsupported gaps, written before cloudposse#2882 landed. - taskfile.mdx / from-taskfile.md: rewrite "parallel-by-default" and "sources/generates gap" sections to document dependencies.commands and inputs/artifacts as the direct matches, including the automatic dedup behavior a hand-built parallel step doesn't provide. - makefile.mdx / from-makefile.md: document dependencies.commands for target chains with a shared prerequisite, and inputs/artifacts (with timestamp.changed for make's exact mtime semantics) for file-timestamp targets. - SKILL.md: fix the same false claims in the top-level "Common Problems" summary agents read before the per-tool reference files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(migration): fix reviewer feedback on dependencies/freshness guidance Local review comments: - Recommend `atmos --help` (scriptable, direct) over the interactive `atmos help` (pages the same listing) for command discovery, across all three migration guides and their skill mirrors. - Reframe makefile.mdx around "Atmos is the front door either way": a custom command can call `make <target>` as its one step, permanently if desired, rather than treating full migration into native `steps:` as the only end state. CodeRabbit findings, verified against actual behavior before fixing: - GNU Make's default is to build a target's prerequisites one at a time, in listed order -- `-j` is required for concurrency. `dependencies.commands` runs concurrently by default, so presenting it as Make's/Just's "direct match" changes behavior and can race prerequisites that were only ever sequential by accident. Ordered steps are now the default-preserving match; `dependencies.commands` is reserved for a shared prerequisite (dedup, independent of concurrency), genuine independence, or an explicit `-j` source. Fixed in SKILL.md, makefile.mdx, and from-makefile.md, including a corrected Shape B example showing how to keep `build` ordered ahead of `test` even under the concurrent scheduler. - Confirmed in cmd/cmd_utils.go/internal/exec/workflow_utils.go that a skipped step just `continue`s the loop: inputs/artifacts freshness is evaluated and recorded per step, unlike Task's/Make's whole-recipe/task scope. Documented this across taskfile.mdx, from-makefile.md, and SKILL.md, with guidance to combine multiple commands into one step when a single freshness decision must gate all of them. - Removed the stale "target chains become workflows" claim, which contradicted the dependencies.commands guidance it now sits next to; workflows are reserved for fixed, multi-step orchestration across more than one component. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore(gitignore): ignore cached tools/gomodcheck binary Mirrors the existing tools/lintroller/.lintroller entry for the mage lint helper's cached build output. * fix(docs): address CodeRabbit findings on atmos-migration skill docs Corrects eight documentation-accuracy issues flagged by CodeRabbit on the atmos-migration skill: prefer `type: atmos` over `type: shell` for calling another custom command (preserves stack context and structured output); map Make's `@` prefix to `show: { command: false }` instead of `output: none` (which discards stdout/stderr entirely); describe `build-all`'s `-j4` loop as sequential, not parallel; define a proper per-service `build-service` command for the Shape C matrix example; document the `metadata.component` no-move option for mapping a stack component onto an existing directory; scope "target chains become workflows" guidance to ordered custom-command steps instead; and fix the justfile.mdx examples to carry environment variables and per-environment Terraform vars across build/test/deploy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(docs): address second round of CodeRabbit findings on migration docs Makes the terraform-directory move optional (not required) in from-justfile.md, makefile.mdx, and taskfile.mdx; corrects the mischaracterization of Just's `{{ }}` interpolation as Go templates; clarifies that Task's `deps:` maps directly to the concurrent-by-default `dependencies.commands` rather than ordered steps; fixes a broken no-move `.tfvars` path example in justfile.mdx; stops mapping a single `$(MAKE) -C dir` invocation to `matrix` (reserving it for genuine `$(SUBDIRS)`-style loops); and documents that Atmos's `internal: true` (Cobra Hidden) does not block direct invocation the way Task's `internal: true` does. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(docs): preserve per-environment tfvars contract in migration guides Both the Justfile source (-var-file=envs/{{env}}.tfvars) and Makefile source (-var-file=envs/$(ENV).tfvars) load per-environment Terraform variables, but the migrated `atmos terraform apply infra -s <env>` examples in from-justfile.md and makefile.mdx dropped that behavior -- `-s` only selects the stack, it doesn't load vars. Document the per-stack `vars: !include` mapping for both the moved-directory and no-move component layouts, matching the fix already applied to the sibling justfile.mdx doc. * fix(security): remediate 3 npm Dependabot alerts in website deps Pin transitive browserslist and postcss-selector-parser via pnpm.overrides to patched versions: - browserslist <= 4.28.6 (GHSA alerts cloudposse#281, cloudposse#282, high) -> ^4.28.7 - postcss-selector-parser >=6.1.0 <6.1.3 (alert cloudposse#280, low) -> ^6.1.3 Both are patch-level bumps within dependabot.yml's semver-major ignore policy. The postcss-selector-parser override is scoped to the ^6 line only (via postcss-calc's ^6.0.11 request) so it doesn't touch the separate, already-unaffected ^7.0.0 line used elsewhere. * fix(docs): add terminal periods to from-mise/from-aqua resource bullets --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
what
why