Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/scripts/test-changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail

case "$EVENT_NAME" in
pull_request)
comparison="${BASE_SHA}...${HEAD_SHA}"
;;
push)
if [[ "$BASE_SHA" == 0000000000000000000000000000000000000000 ]] ||
! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
echo '::notice::No previous push revision available; running the full test matrix.'
echo 'run_tests=true' >> "$GITHUB_OUTPUT"
exit 0
fi
comparison="${BASE_SHA}..${HEAD_SHA}"
;;
*)
echo "::error::Unsupported event: $EVENT_NAME"
exit 1
;;
esac

changed_files="$(mktemp)"
trap 'rm -f "$changed_files"' EXIT
git diff --name-only --no-renames -z "$comparison" > "$changed_files"

run_tests=false
while IFS= read -r -d '' path; do
case "$path" in
*.md|LICENSE|LICENSE.txt|images/*) ;;
*) run_tests=true ;;
esac
done < "$changed_files"

echo "run_tests=$run_tests" >> "$GITHUB_OUTPUT"
25 changes: 25 additions & 0 deletions .github/workflows/test-changes.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: Test changes
on:
workflow_call:
outputs:
run_tests:
value: ${{ jobs.changes.outputs.run_tests }}
permissions:
contents: read
jobs:
changes:
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
run_tests: ${{ steps.changes.outputs.run_tests }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- id: changes
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: bash .github/scripts/test-changes.sh
12 changes: 11 additions & 1 deletion .github/workflows/test-mongodb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ concurrency:
group: mongodb-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
uses: ./.github/workflows/test-changes.yml
test:
needs: changes
runs-on: ubuntu-24.04
timeout-minutes: 20
env:
Expand All @@ -34,7 +37,7 @@ jobs:
name: test (ruby=${{ matrix.entry.ruby }}, mongoid=${{ matrix.entry.mongoid }}, mongodb=${{ matrix.entry.mongodb }}, grape=${{ matrix.entry.grape || 'auto' }}, json=${{ matrix.entry.json }})
services:
mongodb:
image: mongo:${{ matrix.entry.mongodb }}
image: ${{ needs.changes.outputs.run_tests == 'true' && format('mongo:{0}', matrix.entry.mongodb) || '' }}
ports:
- 27017:27017
options: >-
Expand All @@ -44,13 +47,20 @@ jobs:
--health-retries 10
steps:
- uses: actions/checkout@v7
if: needs.changes.outputs.run_tests == 'true'
- uses: ruby/setup-ruby@v1
if: needs.changes.outputs.run_tests == 'true'
with:
ruby-version: ${{ matrix.entry.ruby }}
bundler-cache: true
cache-version: mongoid-${{ matrix.entry.mongoid }}-grape-${{ matrix.entry.grape || 'auto' }}-json-${{ matrix.entry.json }}
- uses: browser-actions/setup-firefox@v1.7.2
if: needs.changes.outputs.run_tests == 'true'
with:
firefox-version: latest
- name: Test
if: needs.changes.outputs.run_tests == 'true'
run: xvfb-run --auto-servernum bundle exec rake spec
- name: Documentation only
if: needs.changes.outputs.run_tests == 'false'
run: echo 'Documentation-only change; database, dependencies, browser, and tests skipped.'
12 changes: 11 additions & 1 deletion .github/workflows/test-postgresql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ concurrency:
group: postgresql-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
uses: ./.github/workflows/test-changes.yml
test:
needs: changes
runs-on: ubuntu-24.04
timeout-minutes: 20
env:
Expand Down Expand Up @@ -35,7 +38,7 @@ jobs:
name: test (ruby=${{ matrix.entry.ruby }}, postgresql=${{ matrix.entry.postgresql }}, active_record=${{ matrix.entry.active_record }}, grape=${{ matrix.entry.grape || 'auto' }}, json=${{ matrix.entry.json }})
services:
postgresql:
image: postgres:${{ matrix.entry.postgresql }}
image: ${{ needs.changes.outputs.run_tests == 'true' && format('postgres:{0}', matrix.entry.postgresql) || '' }}
env:
POSTGRES_DB: slack_ruby_bot_server_test
POSTGRES_USER: test
Expand All @@ -49,13 +52,20 @@ jobs:
--health-retries 10
steps:
- uses: actions/checkout@v7
if: needs.changes.outputs.run_tests == 'true'
- uses: ruby/setup-ruby@v1
if: needs.changes.outputs.run_tests == 'true'
with:
ruby-version: ${{ matrix.entry.ruby }}
bundler-cache: true
cache-version: activerecord-${{ matrix.entry.active_record }}-grape-${{ matrix.entry.grape || 'auto' }}-json-${{ matrix.entry.json }}
- uses: browser-actions/setup-firefox@v1.7.2
if: needs.changes.outputs.run_tests == 'true'
with:
firefox-version: latest
- name: Test
if: needs.changes.outputs.run_tests == 'true'
run: xvfb-run --auto-servernum bundle exec rake spec
- name: Documentation only
if: needs.changes.outputs.run_tests == 'false'
run: echo 'Documentation-only change; database, dependencies, browser, and tests skipped.'
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 2.3.0 (Next)

* [#186](https://github.com/slack-ruby/slack-ruby-bot-server/pull/186): Document persisted custom Team attributes for both adapters and Rails concern loading/reloading - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
* [#187](https://github.com/slack-ruby/slack-ruby-bot-server/pull/187): Skip expensive database test setup for documentation-only changes while preserving every matrix check and Danger - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
* [#183](https://github.com/slack-ruby/slack-ruby-bot-server/pull/183): Add a `config.server_pages` block to configure page middleware options centrally without changing the installed library's defaults - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
* [#184](https://github.com/slack-ruby/slack-ruby-bot-server/pull/184): Add a configurable API application and separately mountable team read/registration endpoints, preserving the default API while supporting custom routes and registration-only applications - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
* [#185](https://github.com/slack-ruby/slack-ruby-bot-server/pull/185): Skip inactive installations in status checks and deactivate revoked credentials through service callbacks, returning an explicit failed ping instead of failing the whole status response - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ On Linux, install [Firefox](https://www.mozilla.org/firefox/) and `Xvfb`, then r
xvfb-run --auto-servernum bundle exec rake
```

Documentation-only changes (`*.md`, `LICENSE`, `LICENSE.txt`, and `images/`) do not start database containers, install dependencies or Firefox, or run the database test suites. The workflows still publish every existing matrix check name so required checks can complete; Danger and RuboCop remain unchanged. Code, assets in `public/`, dependency files, and CI configuration still run the full matrices. New branches or unavailable previous push revisions conservatively run all tests.

## Write Tests

Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build.
Expand Down
93 changes: 93 additions & 0 deletions spec/ci/test_changes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
require 'spec_helper'
require 'open3'
require 'tmpdir'
require 'fileutils'

describe 'CI test change classification' do
around do |example|
Dir.mktmpdir('test-changes') do |directory|
@repository = directory
example.run
end
end

before do
git('init', '--quiet')
git('config', 'user.name', 'CI')
git('config', 'user.email', 'ci@example.invalid')
@base = commit('README.md')
end

def git(*args)
output, error, status = Open3.capture3('git', *args, chdir: @repository)
raise error unless status.success?

output.strip
end

def commit(path)
FileUtils.mkdir_p(File.dirname(File.join(@repository, path)))
File.write(File.join(@repository, path), "content for #{path}\n")
git('add', '--all')
git('commit', '--quiet', '-m', 'Test change')
git('rev-parse', 'HEAD')
end

def classify(base: @base, head: git('rev-parse', 'HEAD'), event: 'pull_request')
output = File.join(@repository, 'output')
script = File.expand_path('../../.github/scripts/test-changes.sh', __dir__)
_, error, status = Open3.capture3(
{ 'EVENT_NAME' => event, 'BASE_SHA' => base, 'HEAD_SHA' => head, 'GITHUB_OUTPUT' => output },
'bash', script, chdir: @repository
)
raise error unless status.success?

File.read(output).strip
end

it 'skips Markdown and documentation images' do
commit('docs/guide.md')
commit('images/example.png')
expect(classify).to eq 'run_tests=false'
end

it 'runs tests when documentation is mixed with runtime changes' do
commit('UPGRADING.md')
commit('public/styles.css')
expect(classify).to eq 'run_tests=true'
end

it 'does not ignore a code file renamed to Markdown' do
@base = commit('lib/example.rb')
File.rename(File.join(@repository, 'lib/example.rb'), File.join(@repository, 'example.md'))
git('add', '--all')
git('commit', '--quiet', '-m', 'Rename')
expect(classify).to eq 'run_tests=true'
end

it 'compares pull requests from their merge base, not unrelated base-branch changes' do
head = commit('CHANGELOG.md')
git('checkout', '--quiet', '--detach', @base)
base = commit('lib/base-only.rb')
expect(classify(base: base, head: head)).to eq 'run_tests=false'
end

it 'includes removed code when a push rewrites history' do
before = commit('lib/removed.rb')
git('checkout', '--quiet', '--detach', @base)
head = commit('CHANGELOG.md')
expect(classify(base: before, head: head, event: 'push')).to eq 'run_tests=true'
end

it 'runs the full matrix for a new branch' do
expect(classify(base: '0' * 40, event: 'push')).to eq 'run_tests=true'
end

it 'runs the full matrix when a previous pushed revision is unavailable' do
expect(classify(base: 'f' * 40, event: 'push')).to eq 'run_tests=true'
end

it 'fails instead of silently skipping tests when a pull request comparison is invalid' do
expect { classify(base: 'f' * 40) }.to raise_error(RuntimeError)
end
end
Loading