Skip to content

STAC-25142 Add GitHub Actions lint + unit-test workflow (agent CI migration phase 1) - #444

Open
LouisParkin wants to merge 37 commits into
stackstate-7.78.2from
STAC-25142-agent-lint-unit
Open

STAC-25142 Add GitHub Actions lint + unit-test workflow (agent CI migration phase 1)#444
LouisParkin wants to merge 37 commits into
stackstate-7.78.2from
STAC-25142-agent-lint-unit

Conversation

@LouisParkin

Copy link
Copy Markdown

What

Phase 1 of the stackstate-agent GitLab → GitHub CI migration (STAC-25142): ports the lint + unit-test jobs from .gitlab-ci-agent.yml to a GitHub Actions workflow, .github/workflows/lint-and-unit-tests.yml.

Job Runner Ported from
filename-linting docker filename_linting — cheap gate; only needs git ls-files, so the GitLab job's inv deps/go work vendor boilerplate is dropped
unbranded-unit-tests xlarge unbranded_unit_tests — full inv agent.build --race + inv test, DataDog branding
branded-unit-tests xlarge branded_unit_tests — same + ./fix_branding.sh, StackState branding

All three run inside container: <proxy>/quay/stackstate/datadog_build_linux_x64:0cd01a13 — the same BUILD_IMAGE the GitLab pipeline uses (produced by datadog-agent-buildimages, pulled via the read-only quay proxy). No new runner class is needed: xlarge (6 CPU / 40Gi, DinD) maps to GitLab sts-k8s-xl-runner.

Test-execution policy (architect directive — minimize redundant runs)

Each suite runs once on the path to master:

  • filename-linting on every PR (fast).
  • The two ~2h suites run on pull_request, deduped via concurrency: cancel-in-progress.
  • Follow-up (Phase 6): when the merge queue is enabled, the heavy suites move to merge_group (run once at land) and drop from pull_request; master/version-branch pushes then build + publish without re-running tests.

Open question raised for architects (biggest compute lever, tracked in the migration scope doc): unbranded and branded are nearly the same ~2h job twice — do we need both on every path, or branded-as-gate + unbranded on the queue only?

Prerequisite for green checks

This is a public repo, so the read-only registry-proxy credentials must be available to it for the container: image pull: vars.REGISTRY_HOST, vars.REGISTRY_USER, secrets.REGISTRY_PASSWORD. If the first run fails on the image pull, these need scoping to this repo (org secret scope, or a repo-level read robot as done for datadog-agent-buildimages). Org secrets are never exposed to fork PRs, so fork PRs skip these jobs by design.

Security

contents: read only, SHA-pinned actions/checkout, persist-credentials: false, no PATs, registry secret only via container.credentials. Zizmor: clean. actionlint: clean apart from expected self-hosted-runner label warnings (docker, xlarge).

Validation

  • YAML parses; Zizmor clean; actionlint clean (self-hosted labels aside).
  • CI (the heavy suites) will exercise the container:-on-xlarge (manual DinD) path for the first time — that's the main thing this PR validates.

Jira: https://stackstate.atlassian.net/browse/STAC-25142

🤖 Generated with Claude Code

@LouisParkin
LouisParkin changed the base branch from stackstate-7.71.2 to stackstate-7.78.2 July 10, 2026 15:20
@LouisParkin
LouisParkin force-pushed the STAC-25142-agent-lint-unit branch from 3cd1a54 to 7308812 Compare July 10, 2026 15:22
@LouisParkin
LouisParkin force-pushed the STAC-25142-agent-lint-unit branch from 76349d9 to c2e9da2 Compare July 27, 2026 09:25
LouisParkin and others added 8 commits July 28, 2026 11:39
…ration phase 1)

Ports filename_linting + unbranded/branded unit_tests from .gitlab-ci-agent.yml
to GitHub Actions, tracking the active stackstate-7.78.2 pipeline.

- filename-linting on `docker` (cheap gate; only needs `git ls-files`).
- unbranded/branded unit tests on `xlarge` inside the datadog_build image
  (tag 7af9194f), pulled via the read-only quay proxy.
- 7.78.2 deltas vs 7.71.2: `--major-version` dropped from agent.build/inv test
  (removed upstream in DD 7.78), and branded runs scripts/verify_branding_literals.sh
  after fix_branding.sh.

Test-execution policy (architect directive — minimize redundant runs): each
suite runs once. filename-lint on every PR; the two heavy ~2h suites on
pull_request, deduped via concurrency cancel-in-progress. They move to
merge_group (run once at land) at phase 6; master/version-branch pushes then
build+publish without re-running tests.

Security: minimal contents: read, SHA-pinned checkout, persist-credentials
false, no PATs, registry-proxy secret only via container.credentials, fork PRs
skipped. Zizmor clean; actionlint clean apart from self-hosted labels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ic/xlarge-public)

Public repos can't use the shared docker/xlarge runners; use the isolated agent-public pool (pulumi-infra #227 + argocd-apps #115).
…on phase 2)

Port build_binaries and build_cluster_agent from .gitlab-ci-agent.yml to
.github/workflows/build-binaries.yml: build the branded (StackState) production
agent (+ rtloader) and cluster-agent in the datadog_build container on public
xlarge runners, once per change (pull_request), deduped via concurrency.

Carry over the GitLab pipeline speedups: self-contained jobs with no shared deps
prerequisite (the Go module cache is untransportable between jobs and shared
vendoring breaks the go.mod<->vendor reconcile), version.txt generated in-job
from git, and a runner-local persistent-cache follow-up noted for the real
warm-cache win.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e image

Speeds up the migrated stackstate-agent GitHub Actions build/test jobs by
replacing each job's cold `go clean -modcache` + `inv deps` (go mod download +
tidy) reconcile with a warm GOMODCACHE delivered as a prebuilt CI image, keyed
by a content hash of the module graph. Mirrors the StackGraph
ci-metadata / build-ci-image pattern.

- .github/scripts/agent-godeps-cache-metadata.sh: content-addressed image tag
  from every go.mod/go.sum + go.work + modules.yml plus the Dockerfile/script
  themselves, so any graph or mechanism change rotates the tag.
- .github/docker/godeps-cache/Dockerfile: FROM datadog_build, bakes external
  modules into GOMODCACHE via a per-module `go mod download` loop.
- .github/workflows/godeps-cache.yml: reusable workflow (tag lookup + build only
  when missing); pushes to quay.io/stackstate (private), pulled by consumers
  through the registry.tooling quay proxy with the existing REGISTRY_* creds.
- build-binaries.yml / lint-and-unit-tests.yml: add the godeps-cache job, repoint
  the 4 heavy jobs to the cache image, drop the per-job modcache reset + inv deps
  (keep `go work sync` / `go work vendor` against the warm cache).

Zizmor clean (narrow unpinned-images ignore on the content-addressed container
refs, which are effectively digest-pinned but computed per run).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@LouisParkin
LouisParkin force-pushed the STAC-25142-agent-lint-unit branch from c2e9da2 to c07d0c8 Compare July 28, 2026 09:40
LouisParkin and others added 12 commits July 30, 2026 15:27
…ncel

STAC-25494 stagger godeps-cache builds instead of gating on concurrency
STAC-25466 tidy the Go workspace manifests
GitLab fans .gitlab-ci-agent.yml out twice from .gitlab-ci.yml (agent-x86 /
agent-arm) with per-arch variables. Only the amd64 half had been ported. The
arm64-xlarge-public ARC scale set landing on argocd-apps main (STAC-25492)
removes the blocker, so the fan-out becomes an `arch` matrix here.

Make the Go dependency cache image arch-aware. The metadata script hashed only
the module graph plus BASE_IMAGE_TAG, and both datadog_build images share tag
7af9194f -- so the two arches would have computed the SAME cache tag and one
arch's image would have satisfied the other's existence check. Fold the base
image NAME into the hash and the arch into the tag, making that collision
impossible by construction.

godeps-cache.yml takes a required `arch` input, selects datadog_build_linux_x64
vs datadog_build_linux_arm64, and builds each arch NATIVELY on a runner of that
architecture -- QEMU/binfmt would make the Dockerfile's module extraction
unusably slow. The two arches are independent images rather than a multi-arch
manifest: consumers reference them from `container.image`, which resolves
against the runner's own architecture anyway. The metadata job stays on amd64,
since hashing files and calling `docker manifest inspect` are arch-independent.

build-binaries.yml and build-deb.yml matrix over arch, with `fail-fast: false`
so an arm64 regression cannot abort the amd64 leg that has been green for weeks.
Matrix legs upload artifacts concurrently and actions/upload-artifact v4 rejects
a second upload to an existing name, so the cluster-agent and DEB artifacts are
now per-arch. Cache-image stagger (STAC-25494) keeps one designated builder per
arch: lint-and-unit-tests owns amd64, build-binaries owns arm64, everyone else
waits.

Both arches run on pull requests, matching GitLab, where agent-x86 and agent-arm
shared the same commit-or-MR rule.

Of the GitLab per-arch variables only ARCH carries over; see the build-deb.yml
header for why PACKAGE_ARCH / DD_PKG_ARCH / STS_PKG_ARCH are not needed and why
DOCKER_ARCH (aarch64, not arm64) matters only to the publishing lane.

Lint and unit tests deliberately gain no arm64 lane: each suite runs once on the
path to master.

Validated with actionlint (only the expected unknown-self-hosted-label notes),
zizmor (no findings) and shellcheck (clean).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
STAC-25461 report release-branch build failures to Slack via Cerberus
Merging STAC-25461 (PR #449) into this branch produced no textual conflict but a
broken workflow: the new `cerberus-notify` job in build-binaries.yml and
build-deb.yml declares `needs: godeps-cache`, and this branch splits that job
into godeps-cache-amd64 / godeps-cache-arm64. Both files failed actionlint with
"needs job godeps-cache which does not exist in this workflow".

List both per-arch jobs instead. That also preserves the intent recorded in the
STAC-25461 comment -- `needs` lists every job, not just the leaves, so an early
cache failure that skips its dependents is still reported to Slack.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
STAC-25498 Add the arm64 build lane to the GitHub Actions pipeline
The Cerberus Lambda already knows which channel to post to, so passing one
from the workflow is redundant. `notify.go` resolves the channel as:

    Channel: util.GetOrDefault(req.Context, "channel", s.Channel)

and `util.GetOrDefault` (internal/util/context.go:27) treats an empty or
whitespace value as absent, falling back to the Lambda's own SLACK_CHANNEL
environment variable. Since this repo does not hold the secret, the previous
code was sending `channel: ""` on every call and hitting that fallback
anyway -- the field never did anything.

Dropping it halves what pulumi-infra has to provision for this repo
(StackVista/pulumi-infra#258 now asks for CERBERUS_LAMBDA_URL alone), which
matters because org-level secrets default to visibility=private and are
therefore invisible to this PUBLIC repo.

The `channel` key is removed from the payload entirely rather than sent
empty; GetOrDefault makes the two equivalent, and omitting it states the
intent. A comment records how to reintroduce it if agent failures should
ever go somewhere other than the shared CI channel.

Verified: actionlint clean (only the pre-existing self-hosted runner-label
warnings), zizmor clean, and the jq payload still emits valid JSON in the
shape Cerberus expects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
STAC-25461 Drop SLACK_CI_REPORT_CHANNEL from the Cerberus notify path
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant