Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
0b954d0
STAC-25142 Add GitHub Actions lint + unit-test workflow (agent CI mig…
LouisParkin Jul 10, 2026
d9ca269
STAC-25142 CI: run lint+unit on dedicated public runners (docker-publ…
LouisParkin Jul 22, 2026
51c513e
STAC-25142 CI: run workflow steps in bash (dash lacks set -o pipefail…
LouisParkin Jul 24, 2026
936010c
STAC-25142 CI: mark workspace safe.directory (git dubious ownership i…
LouisParkin Jul 24, 2026
adc6b70
STAC-25142 CI: full-depth checkout for unit tests so git describe fin…
LouisParkin Jul 24, 2026
c7d7a42
STAC-25142 Pass workspace dir to fix_branding.sh (CI_PROJECT_DIR is G…
LouisParkin Jul 24, 2026
c07d0c8
STAC-25142 Add GitHub Actions binary-build workflow (agent CI migrati…
LouisParkin Jul 27, 2026
ae94fa8
STAC-25429-godeps-cache-image: warm Go module cache via prebuilt cach…
LouisParkin Jul 28, 2026
7bedf74
STAC-25142 Add GitHub Actions omnibus DEB build workflow
LouisParkin Jul 28, 2026
229df15
STAC-25142 Add agent and cluster-agent container image builds
LouisParkin Jul 28, 2026
c45407d
STAC-25142 Read the PyPI index URL as a variable, not a secret
LouisParkin Jul 28, 2026
ecb9c60
STAC-25429 Push the Go dep cache to its own quay repo, not sts-ci-images
LouisParkin Jul 28, 2026
ffabcac
STAC-25429 Correct visibility comments: datadog_build base and cache …
LouisParkin Jul 28, 2026
e2e6e1c
STAC-25142 Restore the cluster-agent binary's exec bit after artifact…
LouisParkin Jul 28, 2026
7049264
STAC-25429 fix godeps cache image baking an empty module cache
LouisParkin Jul 28, 2026
4e986be
STAC-25429 keep only module zips in the godeps cache image
LouisParkin Jul 29, 2026
0ebc9f1
STAC-25459 wire inv check-mod-tidy into CI, gated on the godeps cache…
LouisParkin Jul 29, 2026
1996c90
Merge pull request #445 from StackVista/STAC-25429-godeps-cache-image
LouisParkin Jul 29, 2026
a9a92e0
Merge pull request #447 from StackVista/STAC-25459-mod-tidy-gate
LouisParkin Jul 29, 2026
a0e85fa
Merge remote-tracking branch 'origin/STAC-25142-agent-lint-unit' into…
LouisParkin Jul 29, 2026
9a2658b
STAC-25429 Build the DEB package in the warm Go module cache image
LouisParkin Jul 29, 2026
77780d0
Merge pull request #446 from StackVista/STAC-25142-agent-omnibus-deb
LouisParkin Jul 30, 2026
36d9298
STAC-25466 tidy the Go workspace manifests
LouisParkin Jul 30, 2026
272d975
STAC-25461 report release-branch build failures to Slack via Cerberus
LouisParkin Jul 30, 2026
b048ba9
STAC-25494 stagger godeps-cache builds instead of gating on concurrency
LouisParkin Jul 30, 2026
46a089e
STAC-25461 read SLACK_CI_REPORT_CHANNEL as a secret, not a variable
LouisParkin Jul 30, 2026
279f418
Merge pull request #450 from StackVista/STAC-25494-godeps-cache-no-ca…
LouisParkin Jul 30, 2026
5980757
STAC-25461 point the Cerberus prerequisite at the repo-level secrets PR
LouisParkin Jul 30, 2026
73f7ee7
Merge pull request #448 from StackVista/STAC-25466-workspace-tidy
LouisParkin Jul 31, 2026
72c17a1
STAC-25461 make Cerberus secrets optional so the missing-secret path …
LouisParkin Jul 31, 2026
2c64bf5
STAC-25498 Add the arm64 build lane to the GitHub Actions pipeline
LouisParkin Jul 31, 2026
1fe5bf3
Merge pull request #449 from StackVista/STAC-25461-cerberus-notify
LouisParkin Jul 31, 2026
e782f72
STAC-25498 Merge integration branch (Cerberus notify, push triggers) …
LouisParkin Jul 31, 2026
41f575b
STAC-25498 point cerberus-notify at the per-arch godeps-cache jobs
LouisParkin Jul 31, 2026
f32f73a
Merge pull request #451 from StackVista/STAC-25498-arm64-lane
LouisParkin Jul 31, 2026
de015d5
STAC-25461 Drop SLACK_CI_REPORT_CHANNEL from the Cerberus notify path
LouisParkin Jul 31, 2026
2678b05
Merge pull request #452 from StackVista/STAC-25461-drop-slack-channel
LouisParkin Jul 31, 2026
526986e
STAC-24889: authenticate Cerberus calls
VioletCranberry Aug 5, 2026
689909a
STAC-25536: send Cerberus bearer token from the notify workflow
LouisParkin Aug 5, 2026
5ea461d
Merge pull request #453 from StackVista/STAC-24889-cerberus-auth
VioletCranberry Aug 5, 2026
ca28691
STAC-25142 Remove comments from the workflow YAML
LouisParkin Aug 7, 2026
1786afa
STAC-25457: publish and sign the agent and cluster-agent images
LouisParkin Aug 5, 2026
9c11a9c
STAC-25457 Gate agent image publication on scans, deb branding, and p…
LouisParkin Aug 6, 2026
19fa3b9
STAC-25457 Manage the agent image CVE backlog with time-limited excep…
LouisParkin Aug 7, 2026
b656b1f
STAC-25500: port the DEB signing and pre-release publishing lane to G…
LouisParkin Aug 5, 2026
e2977c0
STAC-25500: pin deb-s3 and gate install.sh publication on DEB publica…
LouisParkin Aug 6, 2026
185e763
STAC-25500 Restore the image scan exception wiring lost in the rebase
LouisParkin Aug 7, 2026
65a9c2f
Merge pull request #455 from StackVista/STAC-25500-deb-prerelease
LouisParkin Aug 10, 2026
1143ded
Merge pull request #454 from StackVista/STAC-25457-publish-lane
LouisParkin Aug 10, 2026
3e2a23f
STAC-25575 add a ci-success aggregate to each agent workflow
LouisParkin Aug 11, 2026
05d578f
STAC-25583 run the agent build nightly, as GitLab does
LouisParkin Aug 12, 2026
ba25dc9
STAC-25464 delete the GitLab agent pipelines
LouisParkin Aug 12, 2026
15681e3
STAC-25583 keep the nightly build scan-only, never publishing
LouisParkin Aug 13, 2026
2a43e94
STAC-25459 always run the go.mod tidiness check
LouisParkin Aug 13, 2026
4a212d5
STAC-0 Update https://curl.se/ca/cacert.pem sha256sum.
LouisParkin Aug 13, 2026
86ecee6
STAC-25455 Gate publishing on the other workflows' verification checks
LouisParkin Aug 13, 2026
bfe1ba6
STAC-25429 Give the amd64 dependency cache a leader on scheduled runs
LouisParkin Aug 13, 2026
36633e4
STAC-25461 Keep the commit title in Cerberus alerts on scheduled runs
LouisParkin Aug 13, 2026
59fe938
STAC-25455 Add a Zizmor workflow security lint job
LouisParkin Aug 13, 2026
f3bb79e
STAC-25464 Stop the upstream merge runbook prescribing deletion of .g…
LouisParkin Aug 13, 2026
90014e4
STAC-25429 Trim the dependency cache metadata commentary
LouisParkin Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 9 additions & 1 deletion .cerberus/cerberus_notify_failure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ PAYLOAD=$(cat << END_OF_PAYLOAD
END_OF_PAYLOAD
)

curl --verbose --fail --data "${PAYLOAD?Payload is empty}" "${CERBERUS_LAMBDA_URL?No URL Provided}"
# Suspend xtrace: with `set -x` the expanded curl command - including the bearer
# token - would be echoed into the job log.
set +x
curl --fail --show-error --silent \
--header "Content-Type: application/json" \
--header "Authorization: Bearer ${CERBERUS_API_TOKEN?No Cerberus API token provided}" \
--data "${PAYLOAD?Payload is empty}" \
"${CERBERUS_LAMBDA_URL?No URL Provided}"
set -x
9 changes: 9 additions & 0 deletions .github/deb-s3-gems.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b aws-eventstream-1.4.0.gem
40bda996876a45a60c43fbf489b04b46216e98c1814c1ac6453b942e0df6501e aws-partitions-1.1277.0.gem
ee3e3220b8468a3c9e59daba18e6ec897bf5c7ce8adcc0670cfa2f1f092112fe aws-sdk-core-3.254.0.gem
a2e83662ca31b77a2a19c9aa2f40a98165a67270c18c718fe1c70d0cbd7cd749 aws-sdk-kms-1.130.0.gem
1217b878b554b45f2152115c5d2623e3497222f46a738e5a96e9767cbf41468b aws-sdk-s3-1.228.2.gem
6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 aws-sigv4-1.12.1.gem
8beb36bd7d5f524644f2e4b947e9212bcb47cab0b50cd8ad459ce527f938b956 deb-s3-26.1.0.gem
238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1 jmespath-1.6.2.gem
e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 thor-1.5.0.gem
63 changes: 63 additions & 0 deletions .github/docker/godeps-cache/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Go dependency cache image for stackstate-agent CI (STAC-25429). Derives FROM the
# datadog_build CI image and bakes the workspace's external Go module
# downloads into GOMODCACHE, so consumer jobs get a warm module cache with no
# per-job `go mod download`/network round-trips. Rebuilt only when the module graph
# hash changes -- the image tag is content-addressed
# (see .github/scripts/agent-godeps-cache-metadata.sh).
#
# Only the module ZIPS ($GOMODCACHE/cache/download) are kept; the extracted module
# trees are deleted before the layer closes and Go re-extracts on demand. Measured on
# a full cache, the extracted trees are ~79% of the bytes but ~97% of the *files*, and
# image pull cost here is bound by filesystem metadata, not transfer: warm-proxy pulls
# of the full-cache image spent 0.7m downloading and 4.9m extracting. Shipping ~400k
# tiny files through overlayfs cost more than it saved (+3.0m pull against ~1.45m of
# avoided in-build downloading, so ~1.5m/job net loss).
#
# No `# syntax=` frontend directive and no BuildKit secret mounts: the base image is
# pulled by the daemon after `docker login` (creds stay in the daemon, not the
# build), so the build itself needs no secrets and no Docker Hub frontend pull.
ARG BASE_IMAGE
FROM ${BASE_IMAGE}

# `read -d ''` and `pipefail` are bashisms and RUN defaults to /bin/sh (dash), where
# the download loop silently iterates zero times and publishes an empty cache.
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Pre-download external modules for every module in the workspace, mirroring what
# `inv deps` does per module (tasks/libs/common/go.py) at the same parallelism.
# manifests/ preserves the repo's directory layout so nested go.mod files (and their
# local `replace ../` targets) resolve. No GOFLAGS override: go.work puts this in
# workspace mode, where anything other than -mod=readonly is rejected outright. Only
# the zip cache is kept -- the manifests and extracted trees are removed in the same
# RUN, so the layer diff only ever contains the final state.
#
# The prune needs `chmod -R u+w` first: Go writes the module cache read-only (dirs
# 0555, files 0444) and `rm -rf` cannot descend into it as a non-root user.
# cache/vcs holds full git clones for any module fetched outside the proxy; it is not
# needed to rebuild a module from its zip, so it goes too.
COPY manifests/ /workspace/
RUN set -eux; \
git config --global --add safe.directory '*'; \
export PATH="${PATH}:/usr/local/go/bin"; \
cd /workspace; \
modules="$(find . -type f -name go.mod | wc -l)"; \
echo "workspace modules: ${modules}"; \
test "${modules}" -gt 0; \
find . -type f -name go.mod -print0 \
| xargs -0 -n1 -P8 bash -c \
'moddir="$(dirname "$1")"; echo "go mod download :: ${moddir}"; cd "${moddir}" && go mod download' _; \
cache="$(go env GOMODCACHE)"; \
echo "== populated module cache =="; \
du -sh "${cache}"; \
echo "files: $(find "${cache}" -type f | wc -l)"; \
echo "== zip cache =="; \
du -sh "${cache}/cache/download"; \
echo "files: $(find "${cache}/cache/download" -type f | wc -l)"; \
test "$(du -sm "${cache}/cache/download" | cut -f1)" -gt 100; \
find "${cache}" -mindepth 1 -maxdepth 1 ! -name cache -exec chmod -R u+w {} +; \
find "${cache}" -mindepth 1 -maxdepth 1 ! -name cache -exec rm -rf {} +; \
if [ -d "${cache}/cache/vcs" ]; then chmod -R u+w "${cache}/cache/vcs"; rm -rf "${cache}/cache/vcs"; fi; \
echo "== retained in image =="; \
du -sh "${cache}"; \
echo "files: $(find "${cache}" -type f | wc -l)"; \
rm -rf /workspace
44 changes: 44 additions & 0 deletions .github/scripts/agent-godeps-cache-metadata.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Computes the content-addressed tag for the stackstate-agent Go dependency cache
# image (STAC-25429), and the two refs it is reached by:
# * push ref -> quay.io/stackstate/stackstate-agent-godeps-cache (authoritative)
# * pull ref -> ${REGISTRY_HOST}/quay/... (registry.tooling proxy)
#
# The cache has its own quay repo rather than sharing sts-ci-images with the ARC
# runner images: stackstate-agent is public, so whatever credential this workflow
# carries is reachable from any org member's same-repo branch. A dedicated repo
# keeps that reach down to a rebuildable cache.

agent_godeps_compute_metadata() {
: "${QUAY_REGISTRY:?QUAY_REGISTRY is required}" # quay.io
: "${REGISTRY_HOST:?REGISTRY_HOST is required}" # registry.tooling.stackstate.io (quay proxy)
: "${BASE_IMAGE_TAG:?BASE_IMAGE_TAG is required}" # datadog_build tag the cache derives FROM
: "${BASE_IMAGE_NAME:?BASE_IMAGE_NAME is required}" # datadog_build_linux_x64 | datadog_build_linux_arm64
: "${ARCH:?ARCH is required}" # amd64 | arm64

# The Dockerfile and this script are hashed alongside the module manifests, so
# changing how the cache is built also rotates the tag.
#
# The base image NAME is hashed, not just its tag: the amd64 and arm64
# datadog_build images share a tag, so hashing the tag alone would let one
# arch's cache satisfy the other's existence check.
local godeps_hash
godeps_hash="$(
{
git ls-files -z -- \
'*go.mod' '*go.sum' 'go.work' 'go.work.sum' 'modules.yml' \
'.github/docker/godeps-cache/Dockerfile' \
'.github/scripts/agent-godeps-cache-metadata.sh' \
| LC_ALL=C sort -z \
| xargs -0 sha256sum
printf 'base:%s:%s\n' "${BASE_IMAGE_NAME}" "${BASE_IMAGE_TAG}"
} | sha256sum | cut -c1-16
)"

local image_repo="stackstate/stackstate-agent-godeps-cache"
local image_tag="godeps-${ARCH}-${godeps_hash}"
# shellcheck disable=SC2034 # consumed by the sourcing workflow step
ci_image_push="${QUAY_REGISTRY}/${image_repo}:${image_tag}"
# shellcheck disable=SC2034 # consumed by the sourcing workflow step
ci_image="${REGISTRY_HOST}/quay/${image_repo}:${image_tag}"
}
65 changes: 65 additions & 0 deletions .github/scripts/install-deb-s3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
#
# Install deb-s3 and its full runtime dependency tree from a pinned, checksum
# verified manifest.
#
# deb-s3 runs with the package signing key and the pre-release AWS credentials
# in scope, so it must not be resolved at run time. Every gem is fetched at the
# exact version recorded in the manifest and verified against the SHA256 that
# RubyGems publishes for that release before anything is installed or executed.
#
# Usage: install-deb-s3.sh [manifest]
#
# Regenerating the manifest: fetch each gem and record
# "<sha256> <name>-<version>.gem", matching the checksum published at
# https://rubygems.org/api/v1/versions/<name>.json for that version.

set -euo pipefail

MANIFEST="${1:-.github/deb-s3-gems.sha256}"

if [[ ! -f "${MANIFEST}" ]]; then
echo "gem manifest not found: ${MANIFEST}" >&2
exit 1
fi

MANIFEST_ABS="$(cd "$(dirname "${MANIFEST}")" && pwd)/$(basename "${MANIFEST}")"

WORKDIR="$(mktemp -d)"
trap 'rm -rf "${WORKDIR}"' EXIT

cp "${MANIFEST_ABS}" "${WORKDIR}/gems.sha256"
cd "${WORKDIR}"

while read -r _sha file; do
[[ -n "${file:-}" ]] || continue
name="${file%-*}"
version="${file##*-}"
version="${version%.gem}"
echo "fetching ${name} ${version}"
gem fetch "${name}" --version "${version}" --platform ruby
done < gems.sha256

echo "verifying checksums"
sha256sum --check --strict gems.sha256

echo "installing"
${GEM_INSTALL_SUDO-sudo} gem install --local --no-document --ignore-dependencies ./*.gem

# RubyGems installs versioned binstubs on some distributions (deb-s3.ruby3.2,
# deb-s33.2), so a plain "deb-s3" on PATH is not guaranteed. publish_package.sh
# invokes it by bare name, so link the canonical executable when it is missing.
if ! command -v deb-s3 >/dev/null 2>&1; then
canonical="$(gem contents deb-s3 | grep -E '/bin/deb-s3$' | head -n 1)"
if [[ -z "${canonical}" ]]; then
echo "deb-s3 was installed but its executable could not be located" >&2
exit 1
fi
${GEM_INSTALL_SUDO-sudo} ln -sf "${canonical}" "${LINK_DIR:-/usr/local/bin}/deb-s3"
fi

# Smoke check: this activates the whole pinned dependency set, so a missing or
# incompatible gem fails here rather than midway through publishing.
echo "verifying deb-s3"
deb-s3 help >/dev/null
echo "deb-s3 ready: $(command -v deb-s3)"
84 changes: 84 additions & 0 deletions .github/workflows/await-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Await checks

on:
workflow_call:
inputs:
checks:
description: >-
Newline-separated check-run names that must have concluded successfully
for this commit before the calling workflow publishes anything. GitLab
enforced this with stage barriers inside a single pipeline; `needs:`
cannot cross workflow boundaries, so the barrier is rebuilt here.
required: true
type: string
timeout_minutes:
description: How long to wait for every check to conclude before failing.
type: number
default: 150

permissions: {}

jobs:
await:
name: Wait for verification to pass
runs-on: ubuntu-24.04
timeout-minutes: ${{ inputs.timeout_minutes }}
permissions:
checks: read
steps:
- name: Wait for the required checks to conclude
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
COMMIT_SHA: ${{ github.sha }}
REQUIRED_CHECKS: ${{ inputs.checks }}
TIMEOUT_MINUTES: ${{ inputs.timeout_minutes }}
run: |
set -euo pipefail

mapfile -t checks <<EOF
${REQUIRED_CHECKS}
EOF

deadline=$(( $(date +%s) + TIMEOUT_MINUTES * 60 ))

while :; do
pending=()

for check in "${checks[@]}"; do
[ -n "${check}" ] || continue

# filter=latest collapses re-runs to the current attempt, so a
# re-run that fixed a failure is what gets read here.
conclusions=$(CHECK_NAME="${check}" gh api \
"repos/${REPOSITORY}/commits/${COMMIT_SHA}/check-runs?filter=latest&per_page=100" \
--paginate \
--jq '.check_runs[] | select(.name == env.CHECK_NAME and .status == "completed") | .conclusion' \
</dev/null)
conclusion=${conclusions%%$'\n'*}

if [ -z "${conclusion}" ]; then
pending+=("${check}")
continue
fi

if [ "${conclusion}" != "success" ]; then
echo "::error title=Verification failed::'${check}' concluded '${conclusion}' for ${COMMIT_SHA}. Refusing to publish."
exit 1
fi

echo "Passed: ${check}"
done

[ "${#pending[@]}" -gt 0 ] || break

if [ "$(date +%s)" -ge "${deadline}" ]; then
echo "::error title=Verification timed out::Still waiting on ${pending[*]} after ${TIMEOUT_MINUTES} minutes. Refusing to publish."
exit 1
fi

echo "Waiting on: ${pending[*]}"
sleep 30
done

echo "All required checks passed for ${COMMIT_SHA}."
Loading
Loading