From 29c0729d5033bd2cd259e4d86f062db912a63612 Mon Sep 17 00:00:00 2001 From: Steve Gontzes Date: Thu, 30 Apr 2026 16:54:40 -0400 Subject: [PATCH 1/5] Remove legacy release recording lambdas --- .github/workflows/release.yaml | 139 ++--------------------------- docs/diagrams/release-workflow.dot | 8 +- docs/diagrams/release-workflow.png | Bin 167505 -> 168877 bytes docs/release-workflow.md | 15 +++- 4 files changed, 24 insertions(+), 138 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 953a082..99c913f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -908,7 +908,7 @@ jobs: done < "$DIGEST_FILE" record-connector-registry: - # Legacy dist recording: manifest + S3 upload. + # Release manifest publication: manifest + checksums + S3 upload. # Require binaries to succeed; windows and docker may be skipped based on inputs. # Each optional job must succeed if it ran — a failure means incomplete release artifacts. # see: https://docs.github.com/en/actions/using-jobs/using-conditions-to-control-job-execution @@ -1137,138 +1137,14 @@ jobs: --content-type "application/octet-stream" fi - - name: Invoke Lambda with retries - run: | - set +e # Disable default fail-fast to support retries - if [[ "$ACTIONS_STEP_DEBUG" == "true" ]]; then - set -x # Debug logging - fi - - TMPFILE=$(mktemp) - MAX_RETRIES=5 - RETRY_DELAY=10 # seconds - - for ((i=1; i<=MAX_RETRIES; i++)); do - echo "Attempt $i to invoke Lambda..." - - RESPONSE=$(aws lambda invoke \ - --function-name "${{ github.event.repository.owner.login }}-${{ github.event.repository.name }}-artifact-releases" \ - --payload "{\"tag\":\"${{ inputs.tag }}\"}" \ - --cli-binary-format raw-in-base64-out \ - "$TMPFILE" 2>&1) - EXIT_CODE=$? - - echo "AWS CLI exited with code: $EXIT_CODE" - cat "$TMPFILE" - - STATUS_CODE=$(jq -r '.statusCode' < "$TMPFILE" 2>/dev/null) - - if [[ $EXIT_CODE -eq 0 && "$STATUS_CODE" == "200" ]]; then - echo "Lambda invoked successfully." - break - - elif [[ "$RESPONSE" == *"CodeArtifactUserPendingException"* ]]; then - echo "Lambda not ready (CodeArtifactUserPendingException)." - - if [[ $i -lt $MAX_RETRIES ]]; then - WAIT_TIME=$((i * RETRY_DELAY)) - echo "Retrying in $WAIT_TIME seconds..." - sleep "$WAIT_TIME" - else - echo "Lambda still not ready after $MAX_RETRIES attempts." - rm -f "$TMPFILE" - exit 1 - fi - - else - echo "Lambda invoke failed with unexpected error: $RESPONSE" - rm -f "$TMPFILE" - exit 1 - fi - done - - rm -f "$TMPFILE" - - record-lambda-registry: - if: inputs.lambda == true - # Legacy per-connector Lambda invocation — records to connectorreleases DynamoDB. - # Only needs binaries + docker (container images). Does not gate on windows/msi since - # the Lambda pipeline only cares about container images. Will be removed after cutover - # to the registry API (record-connector-registry replaces this path). - needs: [goreleaser-binaries, goreleaser-docker] - permissions: - id-token: write - contents: read - runs-on: ubuntu-latest - steps: - - name: Configure AWS credentials via OIDC - uses: aws-actions/configure-aws-credentials@v5 - with: - role-to-assume: "arn:aws:iam::168442440833:role/GitHubActionsECRPushRole-${{ github.event.repository.name }}" - aws-region: us-west-2 - - - name: Invoke Lambda with retries - run: | - set +e # Disable default fail-fast to support retries - if [[ "$ACTIONS_STEP_DEBUG" == "true" ]]; then - set -x # Debug logging - fi - - TMPFILE=$(mktemp) - MAX_RETRIES=5 - RETRY_DELAY=10 # seconds - - for ((i=1; i<=MAX_RETRIES; i++)); do - echo "Attempt $i to invoke Lambda..." - - RESPONSE=$(aws lambda invoke \ - --function-name "${{ github.event.repository.name }}-releases" \ - --payload "{\"tag\":\"${{ inputs.tag }}\"}" \ - --cli-binary-format raw-in-base64-out \ - "$TMPFILE" 2>&1) - EXIT_CODE=$? - - echo "AWS CLI exited with code: $EXIT_CODE" - cat "$TMPFILE" - - STATUS_CODE=$(jq -r '.statusCode' < "$TMPFILE" 2>/dev/null) - - if [[ $EXIT_CODE -eq 0 && "$STATUS_CODE" == "200" ]]; then - echo "Lambda invoked successfully." - break - - elif [[ "$RESPONSE" == *"CodeArtifactUserPendingException"* ]]; then - echo "Lambda not ready (CodeArtifactUserPendingException)." - - if [[ $i -lt $MAX_RETRIES ]]; then - WAIT_TIME=$((i * RETRY_DELAY)) - echo "Retrying in $WAIT_TIME seconds..." - sleep "$WAIT_TIME" - else - echo "Lambda still not ready after $MAX_RETRIES attempts." - rm -f "$TMPFILE" - exit 1 - fi - - else - echo "Lambda invoke failed with unexpected error: $RESPONSE" - rm -f "$TMPFILE" - exit 1 - fi - done - - rm -f "$TMPFILE" - # ================================================================ - # Registry API: record release after all legacy recording completes. - # Depends on both dist (record-connector-registry) and lambda (record-lambda-registry) - # so the recording has the full picture: assets, images, config_schema, capabilities. - # continue-on-error on the recording step so failures don't block the release. - # Will become the sole recording path after cutover. + # Registry API: record release after release manifest publication. + # This is the sole release metadata recording path. # ================================================================ record-registry-api: - if: ${{ !cancelled() && needs.record-connector-registry.result == 'success' && (needs.record-lambda-registry.result == 'success' || needs.record-lambda-registry.result == 'skipped') }} - needs: [determine-workflows-ref, record-connector-registry, record-lambda-registry] + # Use !cancelled() so the explicit needs.result check controls skipped-job behavior. + if: ${{ !cancelled() && needs.record-connector-registry.result == 'success' }} + needs: [determine-workflows-ref, record-connector-registry] permissions: id-token: write contents: read @@ -1325,7 +1201,7 @@ jobs: RELEASED_AT=$(echo "$RELEASE_JSON" | jq -r '.published_at // .created_at // empty') echo "released_at=$RELEASED_AT" >> "$GITHUB_OUTPUT" - - name: Write merged manifest from dist recording job + - name: Write merged manifest from manifest publication job working-directory: _workflows env: MERGED_MANIFEST: ${{ needs.record-connector-registry.outputs.merged_manifest }} @@ -1424,7 +1300,6 @@ jobs: goreleaser-windows, goreleaser-docker, record-connector-registry, - record-lambda-registry, record-registry-api, verify-release, ] diff --git a/docs/diagrams/release-workflow.dot b/docs/diagrams/release-workflow.dot index 8631dbb..49cd29b 100644 --- a/docs/diagrams/release-workflow.dot +++ b/docs/diagrams/release-workflow.dot @@ -23,9 +23,9 @@ digraph ReleaseWorkflow { docker [label="goreleaser-docker\n• multi-arch OCI images\n• Lambda image (arm64)\n• GHCR push\n• ECR Public push\n• image attestations", fillcolor="#ecfeff"]; - record [label="record-connector-registry\n• merge manifests\n• sign manifest\n• upload manifest\n• invoke Lambda (release event)", fillcolor="#ecfeff"]; + record [label="record-connector-registry\n• merge manifests\n• sign manifest\n• upload manifest/checksums", fillcolor="#ecfeff"]; - record_lambda [label="record-lambda-registry\n• invoke Lambda (release event)", fillcolor="#ecfeff"]; + registry_api [label="record-registry-api\n• attach docs + metadata\n• record release via API", fillcolor="#ecfeff"]; verify [label="verify-release\n• validate artifacts\n• verify attestations", fillcolor="#f0fdf4"]; } @@ -34,6 +34,7 @@ digraph ReleaseWorkflow { s3 [label="S3\n• archives\n• attestations\n• manifest", fillcolor="#fef9c3"]; ghcr [label="GHCR\n• images\n• OCI attestations", fillcolor="#f9fafb"]; ecr [label="ECR Public\n• OCI images\n• Lambda image", fillcolor="#fef9c3"]; + registry [label="Connector Registry API\n• release metadata\n• assets + images", fillcolor="#dcfce7"]; // Flow connector_repo -> tag; @@ -45,11 +46,12 @@ digraph ReleaseWorkflow { binaries -> record; windows -> record; docker -> record; - docker -> record_lambda; binaries -> s3 [label="artifacts"]; windows -> s3 [label="artifacts"]; docker -> ghcr [label="push"]; docker -> ecr [label="push"]; record -> s3 [label="manifest"]; + record -> registry_api; + registry_api -> registry [label="record"]; record -> verify; } diff --git a/docs/diagrams/release-workflow.png b/docs/diagrams/release-workflow.png index 7401662ca219a1d01e15bcef87bf3f43be9ef068..38a5203c04f755e44705226a19acebe971af3dc8 100644 GIT binary patch literal 168877 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