From 12fdb229e836e273a6f54e828d3fc9f9f3bc64c3 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Sat, 13 Jun 2026 00:43:25 -0700 Subject: [PATCH 1/2] Emit workflow pins as a downloadable patch artifact GITHUB_TOKEN cannot push commits that modify files under .github/workflows/, so the release workflow can no longer pin the labeler workflow files to the release commit on its own. Re-pin them as a patch instead: * Commit and push the predict/action.yml image digest update to the release branch as before. * Apply the dotnet/issue-labeler/* pin updates to the working tree, capture them with git diff into $RUNNER_TEMP/workflow-pins.patch, and reset the working tree to keep the post-checkout cleanup clean. * Upload workflow-pins.patch as a run artifact and reproduce the diff in the step summary inside a ```diff fence so the patch can be applied via a follow-up pull request. * Permit the leading "- " in "- uses: ..." so the regex matches the inline step form (e.g. labeler-cache-retention.yml). * Replace the \1 backreference with ${1} so a SHA beginning with a digit is not parsed as an extended octal escape. * Drop the unused packages: read permission from this job; it only reads digest strings from upstream job outputs and never queries the packages API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release.yml | 80 +++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87a1c8c..e2faa41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,12 +112,12 @@ jobs: - publish-predictor permissions: contents: write - packages: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: "Update workflow pins and the `predict` action for the release" + - name: "Commit the `predict` image update and capture workflow pin patch" + id: release env: REF_NAME: ${{ github.ref_name }} IMAGE_TAG: ${{ needs.compute-image-tag.outputs.image_tag }} @@ -127,7 +127,6 @@ jobs: REPO_BLOB_URL_BASE: ${{ format('{0}/{1}/blob', github.server_url, github.repository) }} REPO_COMMIT_URL_BASE: ${{ format('{0}/{1}/commit', github.server_url, github.repository) }} run: | - RELEASE_SHA="$(git rev-parse HEAD)" WORKFLOW_FILES=( ".github/workflows/labeler-cache-retention.yml" ".github/workflows/labeler-predict-discussions.yml" @@ -137,33 +136,68 @@ jobs: ".github/workflows/labeler-train.yml" ) - for workflow_file in "${WORKFLOW_FILES[@]}"; do - perl -0pi -e "s{^([[:space:]]*uses:[[:space:]]*dotnet/issue-labeler/(?:download|train|test|restore|promote|predict)@)[^[:space:]#]+(?:[[:space:]]*#.*)?\$}{\\1${RELEASE_SHA} # ${REF_NAME}}mg" "$workflow_file" - done - PREDICT_ACTION="predict/action.yml" sed -i "s|^[[:space:]]*image:[[:space:]]*docker://ghcr\.io/.*\$| image: docker://${IMAGE_DIGEST} # ${IMAGE_TAG}|" "$PREDICT_ACTION" git config user.name "GitHub Actions" git config user.email "actions@github.com" - git add "${WORKFLOW_FILES[@]}" "$PREDICT_ACTION" - git commit -m "Release '${REF_NAME}' with predictor digest '${RELEASE_DIGEST}' and workflow pins" - RELEASE_COMMIT_SHA="$(git rev-parse HEAD)" - RELEASE_COMMIT_URL="${REPO_COMMIT_URL_BASE}/${RELEASE_COMMIT_SHA}" + git add "$PREDICT_ACTION" + git commit -m "Release '${REF_NAME}' with predictor digest '${RELEASE_DIGEST}'" git push origin "$REF_NAME" - echo "> [!NOTE]" >> "$GITHUB_STEP_SUMMARY" - echo "> Updated workflow pins on release branch [\`${REF_NAME}\`](${REPO_TREE_URL_BASE}/${REF_NAME}) to [\`${RELEASE_SHA}\`](${RELEASE_COMMIT_URL})." >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "> Workflow files:" >> "$GITHUB_STEP_SUMMARY" + RELEASE_SHA="$(git rev-parse HEAD)" + RELEASE_COMMIT_URL="${REPO_COMMIT_URL_BASE}/${RELEASE_SHA}" + for workflow_file in "${WORKFLOW_FILES[@]}"; do - echo "> - \`$workflow_file\`" >> "$GITHUB_STEP_SUMMARY" + perl -0pi -e "s{^([[:space:]]*(?:-[[:space:]]+)?uses:[[:space:]]+dotnet/issue-labeler/(?:download|train|test|restore|promote|predict)@)[^[:space:]#]+(?:[[:space:]]+#.*)?\$}{\${1}${RELEASE_SHA} # ${REF_NAME}}mg" "$workflow_file" done - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "> Updated [\`predict/action.yml\` (${REF_NAME})](${REPO_BLOB_URL_BASE}/${REF_NAME}/predict/action.yml) to:" >> "$GITHUB_STEP_SUMMARY" - echo "> \`${IMAGE_DIGEST}\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "\`\`\`yml" >> "$GITHUB_STEP_SUMMARY" - grep -i -B1 -A10 '^\s*using:\s*docker' "$PREDICT_ACTION" >> "$GITHUB_STEP_SUMMARY" - echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY" + PATCH_FILE="${RUNNER_TEMP}/workflow-pins.patch" + git diff -- "${WORKFLOW_FILES[@]}" > "$PATCH_FILE" + git checkout -- "${WORKFLOW_FILES[@]}" + + if [[ -s "$PATCH_FILE" ]]; then + HAS_PATCH="true" + else + HAS_PATCH="false" + rm -f "$PATCH_FILE" + fi + + echo "release_sha=$RELEASE_SHA" >> "$GITHUB_OUTPUT" + echo "release_commit_url=$RELEASE_COMMIT_URL" >> "$GITHUB_OUTPUT" + echo "patch_file=$PATCH_FILE" >> "$GITHUB_OUTPUT" + echo "has_patch=$HAS_PATCH" >> "$GITHUB_OUTPUT" + + { + echo "> [!NOTE]" + echo "> Released [\`${REF_NAME}\`](${REPO_TREE_URL_BASE}/${REF_NAME}) as [\`${RELEASE_SHA}\`](${RELEASE_COMMIT_URL})." + echo "" + echo "> Updated [\`predict/action.yml\` (${REF_NAME})](${REPO_BLOB_URL_BASE}/${REF_NAME}/predict/action.yml) image to:" + echo "> \`${IMAGE_DIGEST}\`" + echo "" + echo "\`\`\`yml" + grep -i -B1 -A10 '^\s*using:\s*docker' "$PREDICT_ACTION" + echo "\`\`\`" + } >> "$GITHUB_STEP_SUMMARY" + + if [[ "$HAS_PATCH" == "true" ]]; then + { + echo "" + echo "> [!IMPORTANT]" + echo "> The labeler workflow files reference \`dotnet/issue-labeler/*\` actions and need to be re-pinned to [\`${RELEASE_SHA}\`](${RELEASE_COMMIT_URL}) (\`${REF_NAME}\`)." + echo "> \`GITHUB_TOKEN\` cannot modify files under \`.github/workflows/\`, so the updates are emitted as a patch." + echo "> Download the \`workflow-pins.patch\` artifact from this run, apply it with \`git apply\`, and open a follow-up pull request." + echo "" + echo "\`\`\`diff" + cat "$PATCH_FILE" + echo "\`\`\`" + } >> "$GITHUB_STEP_SUMMARY" + fi + + - name: "Upload workflow-pins.patch artifact" + if: ${{ steps.release.outputs.has_patch == 'true' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: workflow-pins.patch + path: ${{ steps.release.outputs.patch_file }} + if-no-files-found: error From 4f5396dddd6d24fe4cbdeb10f1df9a0abffb3353 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Sat, 13 Jun 2026 01:03:20 -0700 Subject: [PATCH 2/2] Skip release commit when predict/action.yml is already pinned Under `set -e`, `git commit` with nothing staged exits non-zero and would abort the job when the release workflow is re-run against an already-pinned digest. Gate the commit and push on `git diff --cached --quiet` so a re-run is idempotent. RELEASE_SHA is still taken from `git rev-parse HEAD`, which correctly points at the original release commit in the skip path. Addresses PR #127 review feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2faa41..5834aed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -142,8 +142,12 @@ jobs: git config user.name "GitHub Actions" git config user.email "actions@github.com" git add "$PREDICT_ACTION" - git commit -m "Release '${REF_NAME}' with predictor digest '${RELEASE_DIGEST}'" - git push origin "$REF_NAME" + if git diff --cached --quiet; then + echo "predict/action.yml already pinned to ${IMAGE_DIGEST}; skipping release commit." + else + git commit -m "Release '${REF_NAME}' with predictor digest '${RELEASE_DIGEST}'" + git push origin "$REF_NAME" + fi RELEASE_SHA="$(git rev-parse HEAD)" RELEASE_COMMIT_URL="${REPO_COMMIT_URL_BASE}/${RELEASE_SHA}"