From 5c9b691528a42454933913784f4b9aa03640cdfd Mon Sep 17 00:00:00 2001 From: Matias Charriere Date: Thu, 23 Jul 2026 12:09:27 -0300 Subject: [PATCH] feat(gen-circleci): add --skip-ats to opt out of ATS chart tests By default an app-flavour repo runs architect/run-tests-with-ats between build-chart and the chart push, and generation emits tests/ats/Pipfile. --skip-ats suppresses those test jobs and the Pipfile; the chart push then gates directly on build-chart. --- CHANGELOG.md | 4 ++ cmd/gen/circleci/command.go | 3 +- cmd/gen/circleci/flag.go | 3 ++ cmd/gen/circleci/runner.go | 1 + pkg/gen/input/circleci/circleci.go | 11 +++++- pkg/gen/input/circleci/circleci_test.go | 39 +++++++++++++++++++ .../input/circleci/internal/file/config.go | 1 + .../internal/file/workflows.yml.template | 10 +++++ .../input/circleci/internal/params/params.go | 6 +++ 9 files changed, 75 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8c48c51d..e9cf6a590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- `gen circleci`: `--skip-ats` opts an app repo out of the ATS chart tests, suppressing the `run-tests-with-ats` jobs and the `tests/ats/Pipfile`; the chart push then gates directly on `build-chart`. + ### Fixed - `semantic-pull-request`: the generated workflow now allows to be run in merge groups, avoiding stale queues because of the required check not running. diff --git a/cmd/gen/circleci/command.go b/cmd/gen/circleci/command.go index ebad15f8c..759e05b2a 100644 --- a/cmd/gen/circleci/command.go +++ b/cmd/gen/circleci/command.go @@ -21,7 +21,8 @@ block. Jobs are selected by: - Dockerfile present -> architect/push-to-registries (buildx + split-china-push) and architect/sync-china-registry - app flavour -> architect/push-to-app-catalog (app-build-suite executor) - and architect/run-tests-with-ats + and architect/run-tests-with-ats (--skip-ats opts out + of the ATS chart tests) The giantswarm/architect orb is pinned to a version baked into devctl (not a flag): a major orb bump changes the template's required job/param shape, so it diff --git a/cmd/gen/circleci/flag.go b/cmd/gen/circleci/flag.go index 64dbe2b4b..ca0bf6b53 100644 --- a/cmd/gen/circleci/flag.go +++ b/cmd/gen/circleci/flag.go @@ -23,6 +23,7 @@ const ( flagImagePlatforms = "image-platforms" flagImageDockerfile = "image-dockerfile" flagResourceClass = "resource-class" + flagSkipATS = "skip-ats" flagFlavour = "flavour" flagLanguage = "language" flagRepoName = "repo-name" @@ -45,6 +46,7 @@ type flag struct { ImagePlatforms string ImageDockerfile string ResourceClass string + SkipATS bool Flavours gen.FlavourSlice Language gen.Language RepoName string @@ -67,6 +69,7 @@ func (f *flag) Init(cmd *cobra.Command) { cmd.Flags().StringVar(&f.ImagePlatforms, flagImagePlatforms, "", "Override the buildx platform list on the image jobs (push-to-registries `platforms` param). Empty lets the orb default apply (linux/amd64,linux/arm64 when no go-build .platforms file). Set it for single-architecture images (e.g. vllm -> linux/arm64, whose amd64 build has no prebuilt wheels).") cmd.Flags().StringVar(&f.ImageDockerfile, flagImageDockerfile, "", "Override the Dockerfile path on the image jobs (push-to-registries `dockerfile` param). Set it for repos whose Dockerfile is not at the repo root (e.g. backstage -> packages/backend/Dockerfile); a non-empty value also turns the image pipeline on, since the root-Dockerfile derivation misses a nested Dockerfile. The append-only custom.yml merge cannot set this on a generated job. Empty keeps the orb default.") cmd.Flags().StringVar(&f.ResourceClass, flagResourceClass, "", `Override the CircleCI resource_class on the cli-flavour go-build job. Empty defaults to "large". Raise it (e.g. "xlarge") for repos that need more RAM/CPU headroom for the cold cross-compile. Only applies to the cli flavour.`) + cmd.Flags().BoolVar(&f.SkipATS, flagSkipATS, false, `Opt the chart pipeline out of app-test-suite (ATS) chart tests. By default an "app" flavour repo runs architect/run-tests-with-ats between build-chart and the chart push, and generation emits the canonical tests/ats/Pipfile. When set, those test jobs and the Pipfile are not generated and the chart push gates directly on build-chart. Only applies to the app flavour.`) cmd.Flags().VarP(gen.NewFlavourSliceFlagValue(&f.Flavours, gen.FlavourSlice{}), flagFlavour, "f", fmt.Sprintf(`List of project flavours. The "app" flavour selects the chart pipeline. Possible values: <%s>`, strings.Join(gen.AllFlavours(), "|"))) cmd.Flags().VarP(gen.NewLanguageFlagValue(&f.Language, gen.Language("")), flagLanguage, "l", fmt.Sprintf(`The programming language. "go" selects the go-build job. Possible values: <%s>`, strings.Join(gen.AllLanguages(), "|"))) cmd.Flags().StringVarP(&f.RepoName, flagRepoName, "r", "", "Repository name under the giantswarm organization (used for the binary, chart, and job names).") diff --git a/cmd/gen/circleci/runner.go b/cmd/gen/circleci/runner.go index 2cdc1f0a9..9a0299623 100644 --- a/cmd/gen/circleci/runner.go +++ b/cmd/gen/circleci/runner.go @@ -59,6 +59,7 @@ func (r *runner) run(ctx context.Context, _ *cobra.Command, _ []string) error { RepoName: r.flag.RepoName, Language: r.flag.Language, Flavours: r.flag.Flavours, + SkipATS: r.flag.SkipATS, HasDockerfile: hasDockerfile, AppCatalog: r.flag.AppCatalog, AppCatalogTest: r.flag.AppCatalogTest, diff --git a/pkg/gen/input/circleci/circleci.go b/pkg/gen/input/circleci/circleci.go index 24f897646..c7e48958e 100644 --- a/pkg/gen/input/circleci/circleci.go +++ b/pkg/gen/input/circleci/circleci.go @@ -188,6 +188,11 @@ type Config struct { // Flavours are the devctl gen flavours. The "app" flavour selects the // chart pipeline. Flavours gen.FlavourSlice + // SkipATS opts the chart pipeline out of app-test-suite (ATS) chart tests. + // When set, the run-tests-with-ats jobs and the canonical tests/ats/Pipfile + // are not generated, and the chart push jobs gate directly on build-chart. + // Only applies to a chart/app repo (the "app" flavour). + SkipATS bool // HasDockerfile selects the image pipeline. The runner derives this from // the presence of a Dockerfile in the repo. HasDockerfile bool @@ -431,6 +436,7 @@ func New(config Config) (*CircleCI, error) { Language: config.Language.String(), HasDockerfile: hasDockerfile, HasApp: hasApp, + SkipATS: config.SkipATS, ChartName: chartName, ForcePublic: config.ForcePublic, AppCatalog: appCatalog, @@ -488,9 +494,10 @@ func (c *CircleCI) Workflows() input.Input { // call site (devctl gen circleci, the only generator invoked inside align's // `if (ci && ci.generate)` guard). That makes "ATS Pipfile only when CI is // generated, and only for chart/app repos" structurally guaranteed rather than -// dependent on a separate, differently-scoped invocation. +// dependent on a separate, differently-scoped invocation. A repo that opts out +// of ATS (SkipATS) gets no Pipfile either, matching the suppressed jobs. func (c *CircleCI) ATSInputs() []input.Input { - if !c.params.HasApp { + if !c.params.HasApp || c.params.SkipATS { return nil } diff --git a/pkg/gen/input/circleci/circleci_test.go b/pkg/gen/input/circleci/circleci_test.go index 4b404c236..5b0e96b51 100644 --- a/pkg/gen/input/circleci/circleci_test.go +++ b/pkg/gen/input/circleci/circleci_test.go @@ -1251,6 +1251,45 @@ func Test_ATSPipfileOmittedForNonApp(t *testing.T) { } } +// Test_SkipATSOmitsChartTests verifies the ATS opt-out: an app repo with +// SkipATS gets the chart pipeline but no run-tests-with-ats jobs, and the +// chart push jobs gate directly on build-chart instead of the test jobs. The +// canonical Pipfile is suppressed too. +func Test_SkipATSOmitsChartTests(t *testing.T) { + c := Config{ + RepoName: repoMCPKubernetes, + Language: gen.LanguageGo, + Flavours: gen.FlavourSlice{gen.FlavourApp}, + HasDockerfile: true, + BranchPublish: true, + SkipATS: true, + } + + got := render(t, c) + + // The chart pipeline itself stays. + for _, want := range []string{"name: build-chart", "name: push-chart", "name: push-chart-release"} { + if !contains(got, want) { + t.Errorf("SkipATS config missing %q:\n%s", want, got) + } + } + // The ATS test jobs are gone. + for _, unwanted := range []string{jobRunTests, "execute-chart-tests", "execute-chart-tests-release"} { + if contains(got, unwanted) { + t.Errorf("SkipATS config should not contain %q:\n%s", unwanted, got) + } + } + // The chart push jobs gate on build-chart now that the test jobs are gone. + if !contains(got, "requires:\n - build-chart") { + t.Errorf("SkipATS chart push should require build-chart directly:\n%s", got) + } + + // No canonical ATS Pipfile is emitted. + if inputs := newCircleCI(t, c).ATSInputs(); len(inputs) != 0 { + t.Errorf("expected no ATS inputs with SkipATS, got %d: %+v", len(inputs), inputs) + } +} + // Test_NodeBuildOutputCache verifies the build-output cache (node_modules + // Yarn install-state) is emitted for the Yarn package managers, keyed on the // node image version, and is absent for npm (npm ci wipes node_modules) and diff --git a/pkg/gen/input/circleci/internal/file/config.go b/pkg/gen/input/circleci/internal/file/config.go index 69db907a2..6fd1dae43 100644 --- a/pkg/gen/input/circleci/internal/file/config.go +++ b/pkg/gen/input/circleci/internal/file/config.go @@ -42,6 +42,7 @@ func NewWorkflowsInput(p params.Params) input.Input { "Language": p.Language, "HasDockerfile": p.HasDockerfile, "HasApp": p.HasApp, + "SkipATS": p.SkipATS, "ChartName": p.ChartName, "ForcePublic": p.ForcePublic, "AppCatalog": p.AppCatalog, diff --git a/pkg/gen/input/circleci/internal/file/workflows.yml.template b/pkg/gen/input/circleci/internal/file/workflows.yml.template index d2fc4f18b..aa00a3e01 100644 --- a/pkg/gen/input/circleci/internal/file/workflows.yml.template +++ b/pkg/gen/input/circleci/internal/file/workflows.yml.template @@ -340,6 +340,7 @@ workflows: branches: ignore: - main +{{- if not .SkipATS }} # Branch: run chart tests after build-chart. When branchPublish pushes a dev # image, the test also waits on push-to-registries -- run-tests-with-ats @@ -371,6 +372,7 @@ workflows: only: /^v.*/ branches: ignore: /.*/ +{{- end }} {{- if .BranchPublish }} # Branch chart push (opt-in): publish the dev chart after tests + the amd64 @@ -387,7 +389,11 @@ workflows: app_catalog_test: {{ .AppCatalogTest }} chart: {{ .ChartName }} requires: +{{- if .SkipATS }} + - build-chart +{{- else }} - execute-chart-tests +{{- end }} {{- if .HasDockerfile }} - push-to-registries {{- end }} @@ -412,7 +418,11 @@ workflows: app_catalog_test: {{ .AppCatalogTest }} chart: {{ .ChartName }} requires: +{{- if .SkipATS }} + - build-chart +{{- else }} - execute-chart-tests-release +{{- end }} {{- if .HasDockerfile }} - push-to-registries-release {{- end }} diff --git a/pkg/gen/input/circleci/internal/params/params.go b/pkg/gen/input/circleci/internal/params/params.go index 0c7b34e66..85ef45aa0 100644 --- a/pkg/gen/input/circleci/internal/params/params.go +++ b/pkg/gen/input/circleci/internal/params/params.go @@ -19,6 +19,12 @@ type Params struct { // Helm chart). It selects the chart pipeline (push-to-app-catalog with the // app-build-suite executor and run-tests-with-ats). HasApp bool + // SkipATS opts the chart pipeline out of app-test-suite (ATS) chart tests. + // When true the run-tests-with-ats jobs (execute-chart-tests / + // execute-chart-tests-release) and the canonical tests/ats/Pipfile are not + // emitted, and the chart push jobs gate directly on build-chart instead. + // Only meaningful for a chart/app repo (HasApp); ignored otherwise. + SkipATS bool // ChartName is the chart name used for the push-to-app-catalog `chart` // param and the helm/ directory. Defaults to RepoName. Set it for // repos whose chart directory does not match the repo name (e.g.