Skip to content

Commit 3ab052e

Browse files
dblockCopilot
andcommitted
Modernize CI and add Ruby 4 and Grape 4 coverage
Retain existing compatibility combinations while adding current ORM/database versions, native service containers, dependency caching, and maintained browser setup. Update pagination test query decoding for Ruby 4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2e8c460 commit 3ab052e

8 files changed

Lines changed: 170 additions & 67 deletions

File tree

‎.github/workflows/rubocop.yml‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
---
22
name: lint
33
on: [push, pull_request]
4+
permissions:
5+
contents: read
6+
concurrency:
7+
group: rubocop-${{ github.ref }}
8+
cancel-in-progress: true
49
jobs:
510
lint:
611
name: RuboCop
7-
runs-on: ubuntu-latest
12+
runs-on: ubuntu-24.04
13+
timeout-minutes: 10
814
steps:
9-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v7
1016
- name: Set up Ruby
1117
uses: ruby/setup-ruby@v1
1218
with:
13-
ruby-version: 3.4
19+
ruby-version: '4.0'
1420
bundler-cache: true
1521
- name: Run RuboCop
1622
run: bundle exec rubocop

‎.github/workflows/test-mongodb.yml‎

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,56 @@
11
---
2-
name: test
2+
name: MongoDB
33
on: [push, pull_request]
4+
permissions:
5+
contents: read
6+
concurrency:
7+
group: mongodb-${{ github.ref }}
8+
cancel-in-progress: true
49
jobs:
510
test:
6-
runs-on: ubuntu-latest
11+
runs-on: ubuntu-24.04
12+
timeout-minutes: 20
13+
env:
14+
DATABASE_ADAPTER: mongoid
15+
MONGOID_VERSION: ${{ matrix.entry.mongoid }}
16+
GRAPE_VERSION: ${{ matrix.entry.grape || '>= 0' }}
17+
JSON_VERSION: ${{ matrix.entry.json }}
718
strategy:
19+
fail-fast: false
820
matrix:
921
entry:
10-
- { ruby: 3.2, mongoid: 6.4.8, mongodb: 4.4 }
11-
- { ruby: 3.2, mongoid: 7.2.3, mongodb: 4.4 }
12-
- { ruby: 3.2, mongoid: 7.3.0, mongodb: 4.4 }
13-
- { ruby: 3.2, mongoid: 7.3.0, mongodb: 4.4 }
14-
- { ruby: 3.3, mongoid: 7.3.0, mongodb: 5.0 }
15-
- { ruby: 3.4, mongoid: 7.5.2, mongodb: 5.0 }
16-
- { ruby: 3.4, mongoid: 8.1.11, mongodb: 6.0 }
17-
- { ruby: 3.4, mongoid: 8.1.11, mongodb: 7.0 }
18-
- { ruby: 3.4, mongoid: 8.1.11, mongodb: 8.0 }
19-
name: test (ruby=${{ matrix.entry.ruby }}, mongoid=${{ matrix.entry.mongoid }}, mongodb=${{ matrix.entry.mongodb }})
22+
# These Active Support versions pass quirks_mode, removed in JSON 3.
23+
- { ruby: '3.2', mongoid: '6.4.8', mongodb: '4.4', json: '~> 2.0' }
24+
- { ruby: '3.2', mongoid: '7.2.3', mongodb: '4.4', json: '~> 2.0' }
25+
- { ruby: '3.2', mongoid: '7.3.0', mongodb: '4.4', json: '~> 2.0' }
26+
- { ruby: '3.3', mongoid: '7.3.0', mongodb: '5.0', json: '~> 2.0' }
27+
- { ruby: '3.4', mongoid: '7.5.2', mongodb: '5.0', json: '~> 2.0' }
28+
- { ruby: '3.4', mongoid: '8.1.11', mongodb: '6.0', json: '~> 2.0' }
29+
- { ruby: '3.4', mongoid: '8.1.11', mongodb: '7.0', json: '~> 2.0' }
30+
- { ruby: '3.4', mongoid: '8.1.11', mongodb: '8.0', json: '~> 2.0' }
31+
- { ruby: '3.3', mongoid: '~> 9.1', mongodb: '8.0', grape: '~> 3.2', json: '~> 3.0' }
32+
- { ruby: '3.4', mongoid: '~> 9.1', mongodb: '8.0', grape: '~> 4.0', json: '~> 3.0' }
33+
- { ruby: '4.0', mongoid: '~> 9.1', mongodb: '8.0', grape: '~> 4.0', json: '~> 3.0' }
34+
name: test (ruby=${{ matrix.entry.ruby }}, mongoid=${{ matrix.entry.mongoid }}, mongodb=${{ matrix.entry.mongodb }}, grape=${{ matrix.entry.grape || 'auto' }}, json=${{ matrix.entry.json }})
35+
services:
36+
mongodb:
37+
image: mongo:${{ matrix.entry.mongodb }}
38+
ports:
39+
- 27017:27017
40+
options: >-
41+
--health-cmd "mongosh --quiet --eval 'db.adminCommand({ ping: 1 })' || mongo --quiet --eval 'db.adminCommand({ ping: 1 })'"
42+
--health-interval 10s
43+
--health-timeout 5s
44+
--health-retries 10
2045
steps:
21-
- uses: actions/checkout@v2
46+
- uses: actions/checkout@v7
2247
- uses: ruby/setup-ruby@v1
2348
with:
2449
ruby-version: ${{ matrix.entry.ruby }}
25-
- uses: browser-actions/setup-geckodriver@latest
50+
bundler-cache: true
51+
cache-version: mongoid-${{ matrix.entry.mongoid }}-grape-${{ matrix.entry.grape || 'auto' }}-json-${{ matrix.entry.json }}
52+
- uses: browser-actions/setup-firefox@v1.7.2
2653
with:
27-
token: ${{ secrets.GITHUB_TOKEN }}
28-
- run: geckodriver --version
29-
- uses: supercharge/mongodb-github-action@1.7.0
30-
with:
31-
mongodb-version: ${{ matrix.entry.mongodb }}
54+
firefox-version: latest
3255
- name: Test
33-
uses: GabrielBB/xvfb-action@v1
34-
env:
35-
DATABASE_ADAPTER: mongoid
36-
MONGOID_VERSION: ${{ matrix.entry.mongoid }}
37-
with:
38-
run: |
39-
bundle install
40-
bundle exec rake spec
56+
run: xvfb-run --auto-servernum bundle exec rake spec
Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,61 @@
11
---
2-
name: test
2+
name: PostgreSQL
33
on: [push, pull_request]
4+
permissions:
5+
contents: read
6+
concurrency:
7+
group: postgresql-${{ github.ref }}
8+
cancel-in-progress: true
49
jobs:
510
test:
6-
runs-on: ubuntu-latest
11+
runs-on: ubuntu-24.04
12+
timeout-minutes: 20
13+
env:
14+
DATABASE_ADAPTER: activerecord
15+
ACTIVERECORD_VERSION: ${{ matrix.entry.active_record }}
16+
GRAPE_VERSION: ${{ matrix.entry.grape || '>= 0' }}
17+
JSON_VERSION: ${{ matrix.entry.json }}
18+
DATABASE_URL: postgres://test:password@localhost/slack_ruby_bot_server_test
719
strategy:
20+
fail-fast: false
821
matrix:
922
entry:
10-
- { ruby: 3.2, postgresql: 11, active_record: '~> 6.0.0' }
11-
- { ruby: 3.2, postgresql: 11, active_record: '~> 6.1.0' }
12-
- { ruby: 3.2, postgresql: 14, active_record: '~> 7.0.0' }
13-
- { ruby: 3.3, postgresql: 14, active_record: '~> 7.2.0' }
14-
- { ruby: 3.4, postgresql: 14, active_record: '~> 7.2.0' }
15-
- { ruby: 3.4, postgresql: 15, active_record: '~> 8.0.2' }
16-
- { ruby: 3.4, postgresql: 16, active_record: '~> 8.0.2' }
17-
- { ruby: 3.4, postgresql: 17, active_record: '~> 8.0.2' }
18-
name: test (ruby=${{ matrix.entry.ruby }}, postgresql=${{ matrix.entry.postgresql }}, active_record=${{ matrix.entry.active_record }})
23+
# Active Support 6.x, 7.0, and 8.0 pass quirks_mode, removed in JSON 3.
24+
- { ruby: '3.2', postgresql: '11', active_record: '~> 6.0.0', json: '~> 2.0' }
25+
- { ruby: '3.2', postgresql: '11', active_record: '~> 6.1.0', json: '~> 2.0' }
26+
- { ruby: '3.2', postgresql: '14', active_record: '~> 7.0.0', json: '~> 2.0' }
27+
- { ruby: '3.3', postgresql: '14', active_record: '~> 7.2.0', json: '~> 3.0' }
28+
- { ruby: '3.4', postgresql: '14', active_record: '~> 7.2.0', json: '~> 3.0' }
29+
- { ruby: '3.4', postgresql: '15', active_record: '~> 8.0.2', json: '~> 2.0' }
30+
- { ruby: '3.4', postgresql: '16', active_record: '~> 8.0.2', json: '~> 2.0' }
31+
- { ruby: '3.4', postgresql: '17', active_record: '~> 8.0.2', json: '~> 2.0' }
32+
- { ruby: '3.3', postgresql: '18', active_record: '~> 8.1', grape: '~> 3.2', json: '~> 3.0' }
33+
- { ruby: '3.4', postgresql: '18', active_record: '~> 8.1', grape: '~> 4.0', json: '~> 3.0' }
34+
- { ruby: '4.0', postgresql: '18', active_record: '~> 8.1', grape: '~> 4.0', json: '~> 3.0' }
35+
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 }})
36+
services:
37+
postgresql:
38+
image: postgres:${{ matrix.entry.postgresql }}
39+
env:
40+
POSTGRES_DB: slack_ruby_bot_server_test
41+
POSTGRES_USER: test
42+
POSTGRES_PASSWORD: password
43+
ports:
44+
- 5432:5432
45+
options: >-
46+
--health-cmd "pg_isready -U test -d slack_ruby_bot_server_test"
47+
--health-interval 10s
48+
--health-timeout 5s
49+
--health-retries 10
1950
steps:
20-
- uses: actions/checkout@v2
51+
- uses: actions/checkout@v7
2152
- uses: ruby/setup-ruby@v1
2253
with:
2354
ruby-version: ${{ matrix.entry.ruby }}
24-
- uses: browser-actions/setup-geckodriver@latest
25-
with:
26-
token: ${{ secrets.GITHUB_TOKEN }}
27-
- run: geckodriver --version
28-
- uses: harmon758/postgresql-action@v1
55+
bundler-cache: true
56+
cache-version: activerecord-${{ matrix.entry.active_record }}-grape-${{ matrix.entry.grape || 'auto' }}-json-${{ matrix.entry.json }}
57+
- uses: browser-actions/setup-firefox@v1.7.2
2958
with:
30-
postgresql version: ${{ matrix.entry.postgresql }}
31-
postgresql db: slack_ruby_bot_server_test
32-
postgresql user: test
33-
postgresql password: password
59+
firefox-version: latest
3460
- name: Test
35-
uses: GabrielBB/xvfb-action@v1
36-
env:
37-
DATABASE_ADAPTER: activerecord
38-
ACTIVERECORD_VERSION: ${{ matrix.entry.active_record }}
39-
DATABASE_URL: postgres://test:password@localhost/slack_ruby_bot_server_test
40-
with:
41-
run: |
42-
bundle install
43-
bundle exec rake spec
61+
run: xvfb-run --auto-servernum bundle exec rake spec

‎AGENTS.md‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Agent Instructions
2+
3+
## Project
4+
5+
Slack Ruby Bot Server is a Ruby gem providing a Grape API and multi-team Slack bot lifecycle management. Read [CONTRIBUTING.md](CONTRIBUTING.md) before making changes.
6+
7+
- `lib/slack-ruby-bot-server/api/`: endpoints, presenters, helpers, and middleware.
8+
- `lib/slack-ruby-bot-server/models/` and `lib/slack-ruby-bot-server/ext/`: models and database-specific behavior.
9+
- `lib/slack-ruby-bot-server/app.rb` and `service.rb`: application setup and service lifecycle.
10+
- `public/`: registration UI and static assets.
11+
- `spec/`: RSpec API, model, service, and browser integration tests.
12+
- `spec/database_adapters/`: adapter setup, test schemas, and database cleanup.
13+
- `.github/workflows/`: compatibility matrices, lint, and Danger.
14+
15+
## Dependencies and Tests
16+
17+
Set the adapter and version selectors before installing dependencies. Use a Ruby version covered by CI; the examples below match the current Ruby 4/Grape 4 jobs.
18+
19+
Start MongoDB locally, then run:
20+
21+
```sh
22+
export DATABASE_ADAPTER=mongoid MONGOID_VERSION='~> 9.1' GRAPE_VERSION='~> 4.0' JSON_VERSION='~> 3.0'
23+
export DATABASE_URL=mongodb://localhost/slack_ruby_bot_server_test
24+
bundle install
25+
bundle exec rake
26+
```
27+
28+
For PostgreSQL, start the server and adjust local credentials in `DATABASE_URL` as needed:
29+
30+
```sh
31+
export DATABASE_ADAPTER=activerecord ACTIVERECORD_VERSION='~> 8.1' GRAPE_VERSION='~> 4.0' JSON_VERSION='~> 3.0'
32+
export DATABASE_URL=postgresql://localhost/slack_ruby_bot_server_test
33+
bundle install
34+
bundle exec rake
35+
```
36+
37+
Use only a dedicated local test database. The suite deletes or truncates records, and the Active Record schema recreates the teams table.
38+
39+
`bundle exec rake` runs RuboCop and the full RSpec suite. Use `bundle exec rspec path/to/spec.rb` for focused checks. Validate shared behavior against both adapters before committing; batch final checks rather than repeatedly running full suites during editing.
40+
41+
Browser tests require Firefox. Selenium Manager provisions geckodriver when needed. On Linux, install Xvfb and run `xvfb-run --auto-servernum bundle exec rake`. Adapter suites must run sequentially on one host because Capybara uses port 9293.
42+
43+
`Gemfile.lock` is intentionally ignored. Re-resolve dependencies when switching adapter/version combinations; do not add the lockfile to the gem repository.
44+
45+
## Changes and Compatibility
46+
47+
Preserve both database adapters and the existing unique CI matrix combinations, including explicit Grape 3/4 coverage. Quote version strings in workflow YAML and validate workflow changes with `actionlint`.
48+
49+
Keep JSON 2 constraints scoped to affected legacy matrix jobs through `JSON_VERSION`; modern jobs explicitly test JSON 3. Do not add a blanket JSON upper bound to the Gemfile or gemspec.
50+
51+
Follow existing Ruby conventions and `.rubocop.yml`; do not relax cops or add exclusions just to make checks pass. Add focused regression tests for changed behavior and update directly related documentation. Keep Markdown paragraphs on one line.
52+
53+
Add a changelog entry under the next release, with contributor attribution and the linked PR number once available. Danger checks the changelog and table of contents.
54+
55+
Keep each PR branch to one logical commit. Include `Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>` for Copilot contributions, and identify AI-authored GitHub descriptions/comments as posted on behalf of the requesting user. Never force-push `master`. Do not publish a release or push a release tag without explicit authorization.

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### 2.2.1 (Next)
44

55
* Your contribution here.
6+
* [#182](https://github.com/slack-ruby/slack-ruby-bot-server/pull/182): Modernize CI with current actions, native database services, dependency caching, and explicit Ruby 4, Mongoid 9, Active Record 8.1, PostgreSQL 18, Grape 3/4, and JSON 2/3 coverage while retaining existing compatibility combinations - [@dblock](https://github.com/dblock), [@Copilot](https://github.com/apps/copilot-swe-agent).
67
* [#181](https://github.com/slack-ruby/slack-ruby-bot-server/pull/181): Migrated Danger to `danger-pr-comment` workflow - [@dblock](https://github.com/dblock).
78
* [#180](https://github.com/slack-ruby/slack-ruby-bot-server/pull/180): Add support for Grape v3 - [@markokajzer](https://github.com/markokajzer).
89

‎CONTRIBUTING.md‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,20 @@ git checkout -b my-feature-branch
2828

2929
## Bundle Install and Test
3030

31-
Ensure that you can build the project and run tests.
31+
Select a database adapter before installing dependencies. For example, to match the Ruby 4 MongoDB/Grape 4 job, start MongoDB locally and run:
3232

33-
```
33+
```sh
34+
export DATABASE_ADAPTER=mongoid MONGOID_VERSION='~> 9.1' GRAPE_VERSION='~> 4.0' JSON_VERSION='~> 3.0'
35+
export DATABASE_URL=mongodb://localhost/slack_ruby_bot_server_test
3436
bundle install
3537
bundle exec rake
3638
```
3739

38-
If you are a linux user, you will need to install [geckodriver](https://github.com/mozilla/geckodriver/releases), [firefox](https://download-installer.cdn.mozilla.net/pub/firefox/releases/), and `Xvfb`.
39-
And, to get a display for the headless browser, run `Xvfb` manually. You can see more explanations [here](http://elementalselenium.com/tips/38-headless).
40+
For PostgreSQL, set `DATABASE_ADAPTER=activerecord`, `ACTIVERECORD_VERSION`, and `DATABASE_URL` instead. `GRAPE_VERSION` optionally constrains the Grape version for either adapter; without it, Bundler selects a compatible version. CI preserves the older compatibility combinations and adds explicit Grape 3 and 4 jobs with current ORM versions, including Ruby 4. Each database runs in a health-checked service container, while Firefox runs under Xvfb. Selenium Manager provisions the matching driver.
41+
42+
`JSON_VERSION` optionally constrains the development/test JSON dependency; otherwise it is unrestricted. CI uses JSON 2 for Mongoid 6-8 and Active Record 6.x, 7.0, and 8.0 because their Active Support versions pass `quirks_mode`, removed in JSON 3. Mongoid 9, Active Record 7.2, and Active Record 8.1 jobs explicitly test JSON 3. These selectors do not restrict the published gem's runtime dependencies.
43+
44+
On Linux, install [Firefox](https://www.mozilla.org/firefox/) and `Xvfb`, then run:
4045

4146
```
4247
xvfb-run --auto-servernum bundle exec rake
@@ -121,7 +126,7 @@ git push origin my-feature-branch -f
121126

122127
## Check on Your Pull Request
123128

124-
Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above.
129+
Go back to your pull request after a few minutes and check its GitHub Actions results. Everything should look green, otherwise fix issues and update your commit as described above.
125130

126131
## Be Patient
127132

‎Gemfile‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ gem 'fabrication'
3131
gem 'faker'
3232
gem 'faraday'
3333
gem 'faraday-rack'
34+
gem 'grape', ENV.fetch('GRAPE_VERSION', '>= 0')
3435
gem 'hyperclient'
36+
gem 'json', ENV.fetch('JSON_VERSION', '>= 0')
3537
gem 'mutex_m'
3638
gem 'rack'
3739
gem 'rack-server-pages'

‎spec/support/api/endpoints/it_behaves_like_a_cursor_api.rb‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
models_ids.concat(response.map { |instance| instance._links.self._url.gsub("http://example.org/api/#{model_ps}/", '') })
3232
break unless response._links[:next]
3333

34-
next_cursor = CGI.parse(URI.parse(response._links.next._url).query).map { |a| [a[0], a[1][0]] }.to_h
34+
next_cursor = URI.decode_www_form(URI.parse(response._links.next._url).query).to_h
3535
end
3636
expect(models_ids.uniq.count).to eq model.all.count
3737
end
@@ -44,7 +44,7 @@
4444
models_ids.concat(response.map { |instance| instance._links.self._url.gsub("http://example.org/api/#{model_ps}/", '') })
4545
break unless response._links[:next]
4646

47-
next_cursor = CGI.parse(URI.parse(response._links.next._url).query).map { |a| [a[0], a[1][0]] }.to_h
47+
next_cursor = URI.decode_www_form(URI.parse(response._links.next._url).query).to_h
4848
end
4949
expect(models_ids.uniq.count).to eq model.all.count - 3
5050
end

0 commit comments

Comments
 (0)