diff --git a/.github/prompts/changelog-entries.prompt.yml b/.github/prompts/changelog-entries.prompt.yml new file mode 100644 index 00000000..f8f47519 --- /dev/null +++ b/.github/prompts/changelog-entries.prompt.yml @@ -0,0 +1,52 @@ +messages: + - role: system + content: |- + You write CHANGELOG entries describing an automated API update to slack-ruby-client, a + Ruby gem whose Web API endpoint methods, argument validations, specs, and bin commands + are code-generated from JSON method definitions vendored from + https://github.com/slack-ruby/slack-api-ref (via a git submodule and rake task). + + Given a diffstat and a diff of the regenerated files, respond with a JSON object: + {"entries": [string, ...]} + + Rules for entries: + - Each entry is a single short line, imperative mood, no leading bullet/dash and no + trailing period (the caller adds both). + - Group related changes together into one entry per notable change or affected method + group (e.g. one entry for "Add chat.appendStream and chat.stopStream methods", one + entry for "Add a metadata argument to chat.postEphemeral, chat.postMessage, + chat.scheduleMessage, and chat.update"), rather than one entry per file. + - Focus on developer-visible API surface changes: new/removed/renamed methods, new/ + removed/changed arguments (including required/mutually-exclusive validation changes), + new/removed error codes, and behavioral changes to generated code. + - Skip purely mechanical noise (e.g. reformatted docs/comments with no semantic change). + - Produce at most 8 entries. If there are no notable API surface changes, return a single + generic entry describing the update in one line. + - role: user + content: |- + Diffstat: + {{diff_stat}} + + Diff (may be truncated): + {{diff}} +model: openai/gpt-4o-mini +responseFormat: json_schema +jsonSchema: |- + { + "name": "changelog_entries", + "strict": true, + "schema": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "type": "string" + }, + "description": "1-8 short, imperative-mood CHANGELOG entry descriptions, no leading bullet or trailing period" + } + }, + "additionalProperties": false, + "required": ["entries"] + } + } diff --git a/.github/workflows/update_api.yml b/.github/workflows/update_api.yml index fe069e48..3eeefc0a 100644 --- a/.github/workflows/update_api.yml +++ b/.github/workflows/update_api.yml @@ -10,6 +10,7 @@ jobs: permissions: contents: write pull-requests: write + models: read steps: - uses: actions/checkout@v7 with: @@ -32,6 +33,47 @@ jobs: - name: Get slack-api-ref ref id: api-ref run: echo "api-ref=$(git rev-parse --short HEAD:lib/slack/web/api/slack-api-ref)" >> $GITHUB_OUTPUT + - name: Check for changes + id: changes + run: | + if git diff --quiet; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + - name: Prepare diff for AI summary + if: steps.changes.outputs.changed == 'true' + run: | + git diff --stat > /tmp/diff_stat.txt + git diff | head -c 20000 > /tmp/diff.txt + - name: Generate changelog entries with AI + if: steps.changes.outputs.changed == 'true' + id: ai + uses: actions/ai-inference@v1 + with: + prompt-file: ./.github/prompts/changelog-entries.prompt.yml + file_input: | + diff_stat: /tmp/diff_stat.txt + diff: /tmp/diff.txt + - name: Build changelog entries + id: entries + run: | + entries="" + if [ "${{ steps.changes.outputs.changed }}" = "true" ]; then + entries="$(jq -r '.entries[]? // empty' "${{ steps.ai.outputs.response-file }}" 2>/dev/null)" + fi + if [ -z "$entries" ]; then + entries="Update API from slack-api-ref@${{ steps.api-ref.outputs.api-ref }}" + fi + body_list="$(printf '%s\n' "$entries" | sed 's/^/- /')" + { + echo 'entries<> "$GITHUB_OUTPUT" - name: GitHub App token if: ${{ github.repository == 'slack-ruby/slack-ruby-client' }} id: github_app_token @@ -52,6 +94,10 @@ jobs: Update API from [slack-api-ref](https://github.com/slack-ruby/slack-api-ref). Rev: ${{ steps.api-ref.outputs.api-ref }} Date: ${{ steps.date.outputs.date }} + + ## Changes + + ${{ steps.entries.outputs.body_list }} branch: automated-api-update base: master committer: slack-ruby-ci-bot @@ -62,12 +108,24 @@ jobs: git fetch origin automated-api-update git checkout automated-api-update - name: Update CHANGELOG - uses: jacobtomlinson/gha-find-replace@v3 if: ${{ steps.cpr.outputs.pull-request-number != '' }} - with: - include: CHANGELOG.md - find: "\\* Your contribution here." - replace: "* [#${{steps.cpr.outputs.pull-request-number}}](https://github.com/slack-ruby/slack-ruby-client/pull/${{steps.cpr.outputs.pull-request-number}}): Update API from [slack-api-ref@${{ steps.api-ref.outputs.api-ref }}](https://github.com/slack-ruby/slack-api-ref/commit/${{ steps.api-ref.outputs.api-ref }}) - [@slack-ruby-ci-bot](https://github.com/apps/slack-ruby-ci-bot).\n* Your contribution here." + env: + PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }} + ENTRIES: ${{ steps.entries.outputs.entries }} + run: | + ruby - <<'RUBYEOF' + pr_number = ENV.fetch("PR_NUMBER") + pr_url = "https://github.com/slack-ruby/slack-ruby-client/pull/#{pr_number}" + entries = ENV.fetch("ENTRIES").each_line.map(&:strip).reject(&:empty?) + + lines = entries.map do |entry| + "* [##{pr_number}](#{pr_url}): #{entry} - [@slack-ruby-ci-bot](https://github.com/apps/slack-ruby-ci-bot)." + end.join("\n") + + content = File.read("CHANGELOG.md") + content = content.sub("* Your contribution here.", "#{lines}\n* Your contribution here.") + File.write("CHANGELOG.md", content) + RUBYEOF - name: Commit and Push if: ${{ steps.cpr.outputs.pull-request-number != '' }} run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a60b654..83b7f8f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 3.2.1 (Next) +* [#591](https://github.com/slack-ruby/slack-ruby-client/pull/591): Generate AI CHANGELOG entries and PR summaries for automated API update PRs, lock simplecov below 1.1.0 to avoid breaking Coveralls - [@dblock](https://github.com/dblock). * Your contribution here. ### 3.2.0 (2026/07/05) diff --git a/Gemfile b/Gemfile index ccc186e4..5f209263 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,10 @@ group :test do gem 'rubocop-performance' gem 'rubocop-rake' gem 'rubocop-rspec' - gem 'simplecov' + # Lock below 1.1.0, which started writing float timestamps to + # coverage/.resultset.json, breaking coverallsapp/github-action's parser + # (coverallsapp/github-action#269, coverallsapp/coverage-reporter#191). + gem 'simplecov', '< 1.1.0' gem 'simplecov-lcov' gem 'timecop' gem 'vcr'