From 4e8eaa662c47988920316f195b4780bcb732bdd4 Mon Sep 17 00:00:00 2001 From: KieranVR Date: Thu, 23 Jul 2026 13:22:47 -0700 Subject: [PATCH] Remove automated release-notes workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: on every release tag, the "Generate and Send Release Notes" workflow collected the commit log, summarized it with the Gemini API, and posted the result to Discord via an n8n webhook. The team has decided the automated summaries aren't worth maintaining — devs prefer to paste the commit log into whatever LLM they like and write the announcement by hand, which gives them editorial control over what users see. Change: delete .github/workflows/release-notes.yml. Nothing else depended on it — auto-tag.yml stays because tag-build.yml still uses the tags it creates, and pr-build.yml's "release notes" step is GitHub's plain generate-notes for prerelease bodies, unrelated to this workflow. Follow-ups this unlocks (outside this repo): the GEMINI_API_KEY and N8N_WEBHOOK_URL repo secrets and the GEMINI_MODEL repo variable are now unused and can be removed, and the n8n Discord workflow will simply stop receiving calls. Co-Authored-By: Claude Fable 5 --- .github/workflows/release-notes.yml | 272 ---------------------------- 1 file changed, 272 deletions(-) delete mode 100644 .github/workflows/release-notes.yml diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml deleted file mode 100644 index 33930ee86..000000000 --- a/.github/workflows/release-notes.yml +++ /dev/null @@ -1,272 +0,0 @@ -name: Generate and Send Release Notes - -on: - # Only handle GitHub's create event (branch/tag) and manual dispatch - create: - - # Allow manual run for testing - workflow_dispatch: - inputs: - version: - description: "Version to generate notes for (defaults to created tag)" - required: false - type: string - - # Trigger after auto-tag workflow completes (works even when tag was created with GITHUB_TOKEN) - workflow_run: - workflows: ["Auto Tag on Version Change"] - types: [completed] - -permissions: - contents: write - pull-requests: read - -jobs: - release-notes: - # Run only when a tag is created (or manual dispatch) - if: ${{ (github.event_name == 'create' && github.ref_type == 'tag' && github.actor != 'github-actions[bot]') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') }} - runs-on: ubuntu-latest - concurrency: - group: release-notes-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.ref || 'manual' }} - cancel-in-progress: false - steps: - - name: Prepare repository (without external actions) - id: prep - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub's built-in token - no manual setup needed! - shell: bash - run: | - set -euo pipefail - REPO="${GITHUB_REPOSITORY}" - mkdir -p repo - cd repo - git init -q - git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" - git fetch -q --tags --prune origin - # Ensure the commit for this workflow run is available - git fetch -q origin "${GITHUB_SHA}" || true - git checkout -q "${GITHUB_SHA}" || true - - - name: Determine version and previous tag - id: version - shell: bash - working-directory: repo - env: - HEAD_SHA: ${{ github.event.workflow_run.head_sha }} - run: | - set -euo pipefail - # Determine VERSION based on trigger type - if [ -n "${{ github.event.inputs.version || '' }}" ]; then - VERSION="${{ github.event.inputs.version }}" - elif [ "${GITHUB_EVENT_NAME}" = "create" ]; then - VERSION="${GITHUB_REF#refs/tags/}" - elif [ "${GITHUB_EVENT_NAME}" = "workflow_run" ] && [ -n "${HEAD_SHA:-}" ]; then - CANDIDATE_TAG=$(git tag --points-at "${HEAD_SHA}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true) - VERSION="${CANDIDATE_TAG:-}" - fi - - # Validate we found a numeric-only SemVer tag (no 'v' prefix) - if [ -z "${VERSION:-}" ] || ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Could not determine a numeric SemVer tag for this run; skipping." >&2 - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Find previous numeric tag for comparison (exclude current) - PREV_TAG=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^${VERSION}$" | sed -n '1p' || true) - if [ -z "$PREV_TAG" ]; then - PREV_TAG=$(git rev-list --max-parents=0 HEAD) - fi - - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "previous=$PREV_TAG" >> "$GITHUB_OUTPUT" - - # Resolve the commit pointed to by the version tag - if [ "${GITHUB_EVENT_NAME}" = "workflow_run" ] && [ -n "${HEAD_SHA:-}" ]; then - CURR_COMMIT="$HEAD_SHA" - elif git rev-parse "$VERSION" >/dev/null 2>&1; then - CURR_COMMIT=$(git rev-list -n 1 "$VERSION") - else - CURR_COMMIT="$GITHUB_SHA" - fi - echo "current_commit=$CURR_COMMIT" >> "$GITHUB_OUTPUT" - - - name: Stop if non-numeric tag - if: steps.version.outputs.skip == 'true' - run: echo "Non-numeric tag detected; workflow skipped." - - - name: Collect Raw Commit Messages - id: commits - if: steps.version.outputs.skip != 'true' - shell: bash - working-directory: repo - run: | - set -euo pipefail - PREV="${{ steps.version.outputs.previous }}" - CURR="${{ steps.version.outputs.current_commit }}" - - # Find the most recent 'bump version' commit within the current range - # Use precise subject-only matching to avoid merge commit bodies - if git rev-parse "$PREV" >/dev/null 2>&1; then - SEARCH_RANGE="$PREV..$CURR" - else - SEARCH_RANGE="$CURR" - fi - - BUMP_COMMIT=$(git log --pretty=format:"%H %s" "$SEARCH_RANGE" | grep -E "^[a-f0-9]{7,} bump version$" | head -n1 | awk '{print $1}' || true) - if [ -n "$BUMP_COMMIT" ]; then - # Use the parent of the bump as the effective end (exclude bump and post-bump commits) - EFFECTIVE_CURR=$(git rev-parse "$BUMP_COMMIT"^) - RANGE="$PREV..$EFFECTIVE_CURR" - echo "Using selective range up to bump commit $BUMP_COMMIT (parent: $EFFECTIVE_CURR)" - else - # Fallback to full tag range - RANGE="$PREV..$CURR" - echo "No bump commit found; using full tag range" - fi - - if ! git rev-parse "$PREV" >/dev/null 2>&1; then - RANGE="$PREV" - fi - - # Get a clean list of commit messages, excluding merges and version bumps - COMMIT_LIST=$(git log "$RANGE" --no-merges --pretty=format:"%s" \ - --grep='bump\|version\|release' --invert-grep -n 100) - - if [ -z "$COMMIT_LIST" ]; then - echo "No new user-facing commits found. Using a default message." - COMMIT_LIST="Routine maintenance and dependency updates." - fi - - printf '%s' "$COMMIT_LIST" > commits.txt - echo "Collected raw commit messages." - - - name: Summarize with Google Gemini - id: notes - if: steps.version.outputs.skip != 'true' - shell: bash - working-directory: repo - env: - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - run: | - set -euo pipefail - VERSION="${{ steps.version.outputs.version }}" - COMMIT_LIST=$(cat commits.txt) - - # Create a concise prompt to amalgamate and summarize, staying within Discord limits - { - printf '%s\n' "You are a user-friendly release-notes summarizer for Codex Editor." - printf '%s\n' "Goal: Succinctly summarize what changed for users in v${VERSION}. User facing changes are more important than the nitty gritty details of backend implementation tweaks. Simpler is better, but focus on specifics." - printf '%s\n' "" - printf '%s\n' "Output format (exactly):" - printf '%s\n' "" - printf '%s\n' "🚀 New Features" - printf '%s\n' "- bullets" - printf '%s\n' "" - printf '%s\n' "🐛 Bug Fixes" - printf '%s\n' "- bullets" - printf '%s\n' "" - printf '%s\n' "🔧 Improvements" - printf '%s\n' "- bullets" - printf '%s\n' "" - printf '%s\n' "Rules:" - printf '%s\n' "- Max 5 bullets per section; omit empty sections." - printf '%s\n' "- Each bullet ≤ 140 characters." - printf '%s\n' "- Entire output ≤ 1900 characters." - printf '%s\n' "- No greetings/thanks, no horizontal rules like '---', no extra headings." - printf '%s\n' "- No commit hashes, branch names, or internal jargon; focus on user-facing benefits." - printf '%s\n' "- Output only Markdown text in the format above." - printf '%s\n' "" - printf '%s\n' "Source commit subjects:" - printf '%s\n' "$COMMIT_LIST" - } > prompt.txt - - # Safely construct the JSON payload using jq, reading the prompt from a file - # to avoid shell argument length limits. - API_PAYLOAD=$(jq -n --rawfile prompt prompt.txt \ - '{contents: [{parts: [{text: $prompt}]}]}') - - # Select model (override with secret/env GEMINI_MODEL if desired) - MODEL="${GEMINI_MODEL:-gemini-2.5-flash}" - echo "Using Gemini model: $MODEL" - - # Call the Gemini API - RESPONSE_JSON=$(curl -sS -X POST \ - -H "Content-Type: application/json" \ - -H "X-goog-api-key: ${GEMINI_API_KEY}" \ - -d "$API_PAYLOAD" \ - --max-time 30 --retry 2 --retry-delay 5 \ - "https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent") - - # Check for curl errors - if [ $? -ne 0 ]; then - echo "Error: curl command failed to connect to the Gemini API." >&2 - exit 1 - fi - - # If model not found, try a fallback model once - if echo "$RESPONSE_JSON" | jq -e '.error.status=="NOT_FOUND"' >/dev/null 2>&1; then - echo "Model $MODEL not found on this API version. Trying fallback model gemini-2.5-pro..." >&2 - MODEL="gemini-2.5-pro" - RESPONSE_JSON=$(curl -sS -X POST \ - -H "Content-Type: application/json" \ - -H "X-goog-api-key: ${GEMINI_API_KEY}" \ - -d "$API_PAYLOAD" \ - --max-time 30 --retry 2 --retry-delay 5 \ - "https://generativelanguage.googleapis.com/v1beta/models/${MODEL}:generateContent") - fi - - # Try to extract text by concatenating all parts' text fields - NOTES_BODY=$(echo "$RESPONSE_JSON" | jq -r '[.candidates[0].content.parts[]? | .text // empty] | join("")') - - if [ -z "$NOTES_BODY" ] || [ "$NOTES_BODY" = "null" ]; then - echo "Error: Gemini API did not return a valid response or the response was empty." >&2 - echo "Prompt feedback (if any):" >&2 - echo "$RESPONSE_JSON" | jq -r '.promptFeedback // empty' >&2 || true - echo "Candidate safety ratings (if any):" >&2 - echo "$RESPONSE_JSON" | jq -r '.candidates[0].safetyRatings // empty' >&2 || true - echo "Full API Response for debugging:" >&2 - echo "$RESPONSE_JSON" >&2 - echo "Falling back to raw commit list." - { - echo "### AI Summary Failed: Raw Commit Log for v${VERSION}" - echo - cat commits.txt - } > NOTES.md - else - # Sanitize: remove '---' separators and trim length - SANITIZED=$(printf '%s' "$NOTES_BODY" | sed '/^---$/d') - LEN=$(printf '%s' "$SANITIZED" | wc -m | tr -d ' ') - if [ "$LEN" -gt 1900 ]; then - SANITIZED=$(printf '%.1900s' "$SANITIZED") - SANITIZED="$(printf '%s\n...(truncated)' "$SANITIZED")" - fi - printf '%s' "$SANITIZED" > NOTES.md - fi - - echo "Generated release notes (AI or fallback)." - - - name: Send to n8n webhook (Discord) - if: steps.version.outputs.skip != 'true' - env: - N8N_WEBHOOK_URL: ${{ secrets.N8N_WEBHOOK_URL }} - shell: bash - working-directory: repo - run: | - set -euo pipefail - if [ -z "${N8N_WEBHOOK_URL:-}" ]; then - echo "N8N_WEBHOOK_URL is not set; cannot send release notes." >&2 - exit 1 - fi - - VERSION="${{ steps.version.outputs.version }}" - - # Ensure proper JSON escaping by using jq to read the file - PAYLOAD=$(jq -n \ - --arg version "$VERSION" \ - --rawfile notes NOTES.md \ - '{version:$version, notes:$notes}') - - # The n8n Discord node reads $json.version and $json.notes - curl -sS -X POST -H 'Content-Type: application/json' -d "$PAYLOAD" "$N8N_WEBHOOK_URL" - echo "Sent release notes for v$VERSION to n8n."