Skip to content

Commit 97f2a40

Browse files
committed
fix(ci): publish system-tests image via S3 + GH Actions dispatch
#4013 pushed the per-branch system-tests image to GHCR directly from GitLab CI, authenticating with an octo-sts token scoped `packages: write`. That token comes from a GitHub App installation, and GHCR refuses package writes from installation tokens (permission_denied: installation not allowed to Write organization package). The job has allow_failure: true, so it has been failing silently since #4013 merged and no image has actually been published. Replace it with a two-step, two-runtime pipeline: 1. GitLab CI ("publish docker image for system tests (upload)") uploads the built tarballs and installer to s3://dd-trace-php-builds/ci/<ref-slug>/, then ("... (dispatch)") mints an actions: write-scoped octo-sts token and dispatches a new GitHub Actions workflow. 2. .github/workflows/publish-system-tests-image.yml downloads the artifacts from S3 and pushes to GHCR using the workflow's native GITHUB_TOKEN, which is actually allowed to write org packages. Update gitlab-ci-publish-packages.sts.yaml from packages: write to actions: write to match, since nothing mints a packages-scoped token through this policy anymore.
1 parent b2d669e commit 97f2a40

3 files changed

Lines changed: 118 additions & 90 deletions

File tree

.github/chainguard/gitlab-ci-publish-packages.sts.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ issuer: https://gitlab.ddbuild.io
33
subject_pattern: "project_path:DataDog/apm-reliability/dd-trace-php:ref_type:(branch|tag):ref:.*"
44

55
permissions:
6-
packages: write
6+
actions: write
77
pull_requests: read
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: "Publish system-tests image"
2+
3+
# Pushes the per-branch dd-library-php image that DataDog/system-tests pulls
4+
# (LIBRARY_TARGET_BRANCH), tagged with the GitLab CI_COMMIT_REF_SLUG.
5+
on:
6+
workflow_dispatch:
7+
inputs:
8+
ref_slug:
9+
description: "Sanitised branch name (GitLab CI_COMMIT_REF_SLUG); used as the image tag and the S3 prefix."
10+
required: true
11+
12+
concurrency:
13+
group: "publish-system-tests-image-${{ github.event.inputs.ref_slug }}"
14+
cancel-in-progress: true
15+
16+
env:
17+
IMAGE: "ghcr.io/datadog/dd-trace-php/dd-library-php"
18+
PACKAGE_PATH: "orgs/DataDog/packages/container/dd-trace-php%2Fdd-library-php"
19+
S3_BASE: "https://dd-trace-php-builds.s3.us-east-1.amazonaws.com/ci"
20+
21+
jobs:
22+
publish:
23+
runs-on: ubuntu-latest
24+
permissions:
25+
packages: write # push + delete versions of ghcr.io/datadog/dd-trace-php/dd-library-php
26+
contents: read
27+
steps:
28+
- name: Download build artifacts from S3
29+
env:
30+
REF_SLUG: ${{ github.event.inputs.ref_slug }}
31+
run: |
32+
set -euo pipefail
33+
base="${S3_BASE}/${REF_SLUG}"
34+
mkdir -p packages/amd64 packages/arm64
35+
curl -fSL --retry 3 "${base}/dd-library-php-x86_64-linux-gnu.tar.gz" -o packages/amd64/dd-library-php-x86_64-linux-gnu.tar.gz
36+
curl -fSL --retry 3 "${base}/dd-library-php-aarch64-linux-gnu.tar.gz" -o packages/arm64/dd-library-php-aarch64-linux-gnu.tar.gz
37+
curl -fSL --retry 3 "${base}/datadog-setup.php" -o packages/amd64/datadog-setup.php
38+
cp packages/amd64/datadog-setup.php packages/arm64/datadog-setup.php
39+
40+
- name: Log in to GHCR
41+
env:
42+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+
ACTOR: ${{ github.actor }}
44+
run: echo "$GH_TOKEN" | docker login ghcr.io -u "$ACTOR" --password-stdin
45+
46+
- name: Delete any pre-existing image with this tag
47+
env:
48+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49+
REF_SLUG: ${{ github.event.inputs.ref_slug }}
50+
run: | # avoid piling up untagged versions when re-pushing the same tag
51+
gh api "${PACKAGE_PATH}/versions" \
52+
--jq ".[] | select(.metadata.container.tags[]? == \"${REF_SLUG}\") | .id" \
53+
| xargs -r -I {} gh api "${PACKAGE_PATH}/versions/{}" -X DELETE
54+
echo "Pre-delete complete for tag ${REF_SLUG}"
55+
56+
- name: Set up Docker Buildx
57+
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
58+
59+
- name: Build and push image
60+
env:
61+
REF_SLUG: ${{ github.event.inputs.ref_slug }}
62+
run: |
63+
set -euo pipefail
64+
# FROM scratch + COPY only (no RUN) -> no QEMU needed for the foreign arch.
65+
printf 'FROM scratch\nARG TARGETARCH\nCOPY packages/${TARGETARCH}/dd-library-php-*-linux-gnu.tar.gz /\nCOPY packages/${TARGETARCH}/datadog-setup.php /\n' > Dockerfile.system-tests
66+
docker buildx build --platform linux/amd64,linux/arm64 \
67+
-f Dockerfile.system-tests -t "${IMAGE}:${REF_SLUG}" --push .
68+
echo "Pushed ${IMAGE}:${REF_SLUG}"

.gitlab/generate-package.php

Lines changed: 49 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,42 +1558,19 @@ function appsec_image_from_tag_mapping(string $tag): string
15581558
paths:
15591559
- packages/datadog-setup.php
15601560

1561-
# Runs on every non-default branch so system tests can be run against any (non-default) in-progress branch.
1562-
"publish docker image for system tests (token)":
1561+
# System-tests image publishing. Runs on every non-default branch so system tests can be
1562+
# run against any in-progress branch (DataDog/system-tests LIBRARY_TARGET_BRANCH).
1563+
1564+
"publish docker image for system tests (upload)":
15631565
stage: release
1564-
image: registry.ddbuild.io/images/dd-octo-sts-ci-base:2025.06-1
1566+
image: registry.ddbuild.io/images/mirror/amazon/aws-cli:2.17.32
15651567
tags: [ "arch:amd64" ]
1566-
id_tokens:
1567-
DDOCTOSTS_ID_TOKEN:
1568-
aud: dd-octo-sts
15691568
rules:
15701569
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
15711570
when: never
15721571
- when: always
15731572
allow_failure: true
1574-
variables:
1575-
GIT_STRATEGY: none
1576-
script:
1577-
- dd-octo-sts token --scope DataDog/dd-trace-php --policy gitlab-ci-publish-packages > github_token_system_tests.txt
1578-
artifacts:
1579-
paths:
1580-
- github_token_system_tests.txt
1581-
expire_in: 1 hour
1582-
when: on_success
1583-
1584-
"publish docker image for system tests":
1585-
stage: release
1586-
image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/docker:29.4.0-noble
1587-
tags: [ "docker-in-docker:amd64" ]
1588-
resource_group: "publish-system-tests-image-${CI_COMMIT_REF_SLUG}"
1589-
interruptible: true
1590-
rules:
1591-
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
1592-
when: never
1593-
- when: always
15941573
needs:
1595-
- job: "publish docker image for system tests (token)"
1596-
artifacts: true
15971574
- job: "datadog-setup.php"
15981575
artifacts: true
15991576
- job: "package extension: [amd64, x86_64-unknown-linux-gnu]"
@@ -1602,74 +1579,57 @@ function appsec_image_from_tag_mapping(string $tag): string
16021579
artifacts: true
16031580
variables:
16041581
GIT_STRATEGY: none
1582+
script: |
1583+
set -e
1584+
# Normalise the arch tarball names so the GitHub Actions side needs only the branch slug.
1585+
DEST="s3://dd-trace-php-builds/ci/${CI_COMMIT_REF_SLUG}"
1586+
aws s3 cp packages/dd-library-php-*-x86_64-linux-gnu.tar.gz "${DEST}/dd-library-php-x86_64-linux-gnu.tar.gz"
1587+
aws s3 cp packages/dd-library-php-*-aarch64-linux-gnu.tar.gz "${DEST}/dd-library-php-aarch64-linux-gnu.tar.gz"
1588+
aws s3 cp packages/datadog-setup.php "${DEST}/datadog-setup.php"
1589+
echo "Uploaded system-tests artifacts to ${DEST}/"
1590+
1591+
"publish docker image for system tests (dispatch)":
1592+
stage: release
1593+
image: registry.ddbuild.io/images/dd-octo-sts-ci-base:2025.06-1
1594+
tags: [ "arch:amd64" ]
1595+
id_tokens:
1596+
DDOCTOSTS_ID_TOKEN:
1597+
aud: dd-octo-sts
1598+
rules:
1599+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
1600+
when: never
1601+
- when: always
16051602
allow_failure: true
1603+
needs:
1604+
- job: "publish docker image for system tests (upload)"
1605+
variables:
1606+
GIT_STRATEGY: none
16061607
script: |
16071608
set -e
1608-
IMAGE="ghcr.io/datadog/dd-trace-php/dd-library-php:${CI_COMMIT_REF_SLUG}"
1609-
VERSIONS_URL="https://api.github.com/orgs/DataDog/packages/container/dd-trace-php%2Fdd-library-php/versions"
1610-
GITHUB_TOKEN=$(<github_token_system_tests.txt)
1611-
1612-
apt-get update -qq && apt-get install -y -qq curl jq > /dev/null
1609+
# octo-sts token: actions:write to dispatch the workflow, pull_requests:read to gate on an open PR.
1610+
GITHUB_TOKEN=$(dd-octo-sts token --scope DataDog/dd-trace-php --policy gitlab-ci-publish-packages)
16131611

1614-
STATUS=$(curl -s -o /tmp/pulls.json -w "%{http_code}" -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" \
1615-
"https://api.github.com/repos/DataDog/dd-trace-php/pulls?head=DataDog:${CI_COMMIT_BRANCH}&state=open")
1616-
if [ "${STATUS}" != "200" ]; then
1617-
echo "ERROR: failed to check for an open PR on branch ${CI_COMMIT_BRANCH} (HTTP ${STATUS}):"; cat /tmp/pulls.json
1618-
exit 1
1619-
fi
1620-
PR_COUNT=$(jq 'length' /tmp/pulls.json)
1621-
if [ "${PR_COUNT}" -eq 0 ]; then
1612+
# Only publish for branches with an open PR (system tests pull the image per-PR).
1613+
PR_COUNT=$(curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" \
1614+
"https://api.github.com/repos/DataDog/dd-trace-php/pulls?head=DataDog:${CI_COMMIT_BRANCH}&state=open" | jq 'length')
1615+
if [ "${PR_COUNT:-0}" -eq 0 ]; then
16221616
echo "No open PR for branch ${CI_COMMIT_BRANCH}; skipping system-tests image publish."
1623-
exit 3
1617+
exit 0
16241618
fi
1625-
echo "Found open PR(s) on branch ${CI_COMMIT_BRANCH}, continuing"
1626-
1627-
docker login ghcr.io -u DataDog --password-stdin < github_token_system_tests.txt
1628-
echo "Docker login to ghcr.io succeeded"
1629-
1630-
gh_api() { curl -s -o /tmp/resp.json -w "%{http_code}" -X "$1" \
1631-
-H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "$2"; }
1632-
1633-
# delete any pre-existing image with the same tag so pushing with the same tag doesn't pile up untagged versions.
1634-
echo "== Checking token and existing image tagged ${CI_COMMIT_REF_SLUG} =="
1635-
STATUS=$(gh_api GET "$VERSIONS_URL")
1636-
if [ "$STATUS" != "200" ]; then
1637-
echo "WARNING: token check failed (HTTP ${STATUS}), skipping pre-delete:"; cat /tmp/resp.json
1638-
else
1639-
echo "Token OK (HTTP 200): listed package versions for dd-trace-php/dd-library-php"
1640-
VERSION_ID=$(jq -r --arg TAG "${CI_COMMIT_REF_SLUG}" \
1641-
'.[] | select(.metadata.container.tags[]? == $TAG) | .id' /tmp/resp.json | head -n1)
1642-
if [ -z "$VERSION_ID" ]; then
1643-
echo "No pre-existing image tagged ${CI_COMMIT_REF_SLUG} found, nothing to delete"
1644-
else
1645-
STATUS=$(gh_api DELETE "${VERSIONS_URL}/${VERSION_ID}")
1646-
if [ "$STATUS" = "204" ]; then
1647-
echo "Deleted existing image version ${VERSION_ID} (HTTP 204)"
1648-
else
1649-
echo "WARNING: failed to delete existing image version ${VERSION_ID} (HTTP ${STATUS}), continuing anyway:"
1650-
cat /tmp/resp.json
1651-
fi
1652-
fi
1619+
echo "Found open PR(s) on branch ${CI_COMMIT_BRANCH}, dispatching publish workflow"
1620+
1621+
# GHCR write must come from GitHub Actions' GITHUB_TOKEN, so trigger the workflow that
1622+
# pulls the artifacts back down from S3 and pushes the image.
1623+
PAYLOAD=$(jq -n --arg ref "${CI_DEFAULT_BRANCH}" --arg slug "${CI_COMMIT_REF_SLUG}" \
1624+
'{ref: $ref, inputs: {ref_slug: $slug}}')
1625+
HTTP=$(curl -s -o /tmp/dispatch.json -w "%{http_code}" -X POST \
1626+
-H "Authorization: Bearer ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" \
1627+
"https://api.github.com/repos/DataDog/dd-trace-php/actions/workflows/publish-system-tests-image.yml/dispatches" \
1628+
-d "${PAYLOAD}")
1629+
if [ "${HTTP}" != "204" ]; then
1630+
echo "ERROR: workflow_dispatch failed (HTTP ${HTTP}):"; cat /tmp/dispatch.json; exit 1
16531631
fi
1654-
1655-
echo "== Building and pushing ${IMAGE} (linux/amd64, linux/arm64) =="
1656-
# stage each arch's tarball under its own dir so the Dockerfile can pick the right one via buildx's TARGETARCH arg.
1657-
mkdir -p packages/amd64 packages/arm64
1658-
cp packages/dd-library-php-*-x86_64-linux-gnu.tar.gz packages/amd64/
1659-
cp packages/dd-library-php-*-aarch64-linux-gnu.tar.gz packages/arm64/
1660-
cp packages/datadog-setup.php packages/amd64/
1661-
cp packages/datadog-setup.php packages/arm64/
1662-
1663-
printf 'FROM scratch\nARG TARGETARCH\nCOPY packages/${TARGETARCH}/dd-library-php-*-linux-gnu.tar.gz /\nCOPY packages/${TARGETARCH}/datadog-setup.php /\n' \
1664-
> Dockerfile.system-tests
1665-
BUILDER="system-tests-builder-${CI_JOB_ID}"
1666-
docker buildx create --name "$BUILDER" --driver docker-container
1667-
docker buildx build --builder "$BUILDER" --platform linux/amd64,linux/arm64 \
1668-
-f Dockerfile.system-tests -t "$IMAGE" --push .
1669-
echo "Pushed $IMAGE"
1670-
after_script:
1671-
- rm -f github_token_system_tests.txt
1672-
- docker buildx rm "system-tests-builder-${CI_JOB_ID}" || true
1632+
echo "Dispatched publish-system-tests-image.yml (tag ${CI_COMMIT_REF_SLUG}) on ${CI_DEFAULT_BRANCH}"
16731633

16741634
"bundle for reliability env":
16751635
stage: shared-pipeline

0 commit comments

Comments
 (0)