diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..9a3b9a468 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +.github/chainguard/serverless-init-ci-publish.sts.yaml @DataDog/serverless +.github/publish-serverless-init-to-ghcr.yaml @DataDog/serverless \ No newline at end of file diff --git a/.github/chainguard/serverless-init-ci-publish.sts.yaml b/.github/chainguard/serverless-init-ci-publish.sts.yaml index 07d915ec5..30ce1176a 100644 --- a/.github/chainguard/serverless-init-ci-publish.sts.yaml +++ b/.github/chainguard/serverless-init-ci-publish.sts.yaml @@ -1,24 +1,25 @@ # DD Octo STS Trust Policy for serverless-init-ci GitLab pipeline # -# This policy allows the serverless-init-ci GitLab pipeline to publish -# serverless-init images to GitHub Container Registry (GHCR). +# This policy allows the serverless-init-ci GitLab pipeline to trigger +# GitHub Actions workflows that publish serverless-init images to GHCR. # # Reference: https://datadoghq.atlassian.net/wiki/spaces/SECENG/pages/5138645099 # Pipeline: https://gitlab.ddbuild.io/DataDog/serverless-init-ci issuer: https://gitlab.ddbuild.io -# Subject pattern matches the serverless-init-ci repo on any branch or tag +# Subject pattern matches the serverless-init-ci repo on any protected branch or tag subject_pattern: "project_path:DataDog/serverless-init-ci:ref_type:(branch|tag):ref:.*" -# Allow all branches and tags for building RC and prod images +# Only allow protected branches and tags (security control) claim_pattern: project_path: "DataDog/serverless-init-ci" ref_type: "^(branch|tag)$" - pipeline_source: "^(web|pipeline|push)$" + ref_protected: "true" + pipeline_source: "^(web|pipeline)$" ci_config_ref_uri: "^gitlab\\.ddbuild\\.io/DataDog/serverless-init-ci//\\.gitlab-ci\\.yml@refs/(heads|tags)/.*$" -# Minimal permissions: only write packages to GHCR +# Minimal permissions: only trigger GitHub Actions workflows +# The workflow itself uses GITHUB_TOKEN for GHCR access permissions: - packages: write - metadata: read \ No newline at end of file + actions: write diff --git a/.github/workflows/publish-serverless-init-to-ghcr.yml b/.github/workflows/publish-serverless-init-to-ghcr.yml new file mode 100644 index 000000000..e8576549a --- /dev/null +++ b/.github/workflows/publish-serverless-init-to-ghcr.yml @@ -0,0 +1,129 @@ +name: Publish serverless-init to GHCR + +on: + workflow_dispatch: + inputs: + source_image: + description: 'Source image from registry.datadoghq.com (e.g., registry.datadoghq.com/serverless-init:1.7.8)' + required: true + type: string + version: + description: 'Version tag (e.g., 1.7.8 or 1.7.8-rc1)' + required: true + type: string + image_suffix: + description: 'Image suffix (empty for standard, -alpine for alpine)' + required: false + type: string + default: '' + pipeline_id: + description: 'GitLab pipeline ID' + required: true + type: string + is_latest: + description: 'Tag as latest (true for prod releases, false for RCs)' + required: true + type: boolean + default: false + +permissions: + packages: write + contents: read + +jobs: + publish-to-ghcr: + runs-on: ubuntu-latest + steps: + - name: Install crane + run: | + cd /tmp + wget https://github.com/google/go-containerregistry/releases/download/v0.20.2/go-containerregistry_Linux_x86_64.tar.gz + tar -xzf go-containerregistry_Linux_x86_64.tar.gz + sudo mv crane /usr/local/bin/ + crane version + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Wait for image availability + run: | + SOURCE_IMAGE="${{ inputs.source_image }}" + MAX_ATTEMPTS=20 + RETRY_DELAY=30 + # Maximum wait time: 20 attempts × 30s = 600s (10 minutes) + + echo "⏳ Waiting for image to be available: ${SOURCE_IMAGE}" + echo "Will check every ${RETRY_DELAY}s for up to $((MAX_ATTEMPTS * RETRY_DELAY))s" + + for i in $(seq 1 $MAX_ATTEMPTS); do + echo "Attempt $i/$MAX_ATTEMPTS: Checking if image exists..." + + if crane manifest ${SOURCE_IMAGE} >/dev/null 2>&1; then + echo "✅ Image is available!" + exit 0 + fi + + if [ $i -lt $MAX_ATTEMPTS ]; then + echo "⏳ Image not yet available, waiting ${RETRY_DELAY}s..." + sleep $RETRY_DELAY + fi + done + + echo "❌ Image did not become available after $((MAX_ATTEMPTS * RETRY_DELAY))s" + exit 1 + + - name: Copy image to GHCR + run: | + SOURCE_IMAGE="${{ inputs.source_image }}" + VERSION="${{ inputs.version }}" + IMAGE_SUFFIX="${{ inputs.image_suffix }}" + PIPELINE_ID="${{ inputs.pipeline_id }}" + IS_LATEST="${{ inputs.is_latest }}" + + DEST_BASE="ghcr.io/datadog/datadog-lambda-extension/serverless-init" + + echo "📦 Publishing serverless-init image to GHCR" + echo " Source: ${SOURCE_IMAGE}" + echo " Destinations:" + echo " - ${DEST_BASE}:${VERSION}${IMAGE_SUFFIX}" + echo " - ${DEST_BASE}:v${PIPELINE_ID}${IMAGE_SUFFIX}" + + # Copy with version tag (with retry logic) + # Maximum retry duration: 3 attempts with 10s delays between retries + # This workflow is triggered in parallel with the publish attempt to registry.datadoghq.com + # Registry.datadoghq.com should normally need about ~30 seconds to recieve the new image + MAX_COPY_ATTEMPTS=3 + COPY_RETRY_DELAY=10 + + for i in $(seq 1 $MAX_COPY_ATTEMPTS); do + echo "Copying image (attempt $i/$MAX_COPY_ATTEMPTS)..." + + if crane copy ${SOURCE_IMAGE} ${DEST_BASE}:${VERSION}${IMAGE_SUFFIX}; then + echo "✅ Image copied successfully!" + break + fi + + if [ $i -lt $MAX_COPY_ATTEMPTS ]; then + echo "⚠️ Copy failed, retrying in ${COPY_RETRY_DELAY}s..." + sleep $COPY_RETRY_DELAY + else + echo "❌ Failed to copy image after $MAX_COPY_ATTEMPTS attempts" + exit 1 + fi + done + + # Tag for pipeline ID + crane tag ${DEST_BASE}:${VERSION}${IMAGE_SUFFIX} v${PIPELINE_ID}${IMAGE_SUFFIX} + + # Tag as latest if this is a production release + if [ "$IS_LATEST" = "true" ]; then + echo " - ${DEST_BASE}:latest${IMAGE_SUFFIX}" + crane tag ${DEST_BASE}:${VERSION}${IMAGE_SUFFIX} latest${IMAGE_SUFFIX} + fi + + echo "✅ Successfully published image to GHCR!" + echo "📍 View at: https://github.com/DataDog/datadog-lambda-extension/pkgs/container/datadog-lambda-extension%2Fserverless-init"