Skip to content

Commit 9c709b9

Browse files
authored
RUBY-3451 Release signing (#2874)
* okay, very rough draft of a release GA workflow * fix ref to mongodb-labs/drivers-github-tools * action is not prefixed with garasign * struggling to get some visibility here... * specify release environment * fixes * more fixes * don't need to load the whole driver just to get the version * make sure we push the new tag to the repo * `gh release` commands want a tag name * I *think* the sig gets placed under $RELEASE_ASSETS? * remove obsolete release-related code, and add explicit `rake release` task * okay, dummy gem name in gemspec, let's see if the gem gets pushed * okay, let's try using the provided github action instead * are the backticks being interpolated by the shell? * whelp, guess we need to bundle install * don't require the spec organizers unless it is needed * don't wait for the gem to be released mostly because our gem is in a non-standard location (expected to be in pkg/, but we're generating it directly to the root directory) * bump the gemspec * linter appeasement * add documentation related to release verification
1 parent 7b06fb6 commit 9c709b9

File tree

11 files changed

+227
-171
lines changed

11 files changed

+227
-171
lines changed

.github/release.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# For configuring how release notes are auto-generated.
2+
# Requires the use of labels to categorize pull requests.
3+
#
4+
# See: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes

.github/workflows/release.yml

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: "Driver Release"
2+
run-name: "Ruby Driver Release ${{ github.ref_name }}"
3+
4+
on: workflow_dispatch
5+
6+
env:
7+
RELEASE_MESSAGE_TEMPLATE: |
8+
Version {0} of the [MongoDB Ruby Driver](https://rubygems.org/gems/mongo) is now available.
9+
10+
**Release Highlights**
11+
12+
TODO: one or more paragraphs describing important changes in this release
13+
14+
**Documentation**
15+
16+
Documentation is available at [MongoDB.com](https://www.mongodb.com/docs/ruby-driver/current/).
17+
18+
**Installation**
19+
20+
You may install this version via RubyGems, with:
21+
22+
gem install --version {0} mongo
23+
24+
jobs:
25+
release:
26+
name: "Driver Release"
27+
environment: release
28+
runs-on: 'ubuntu-latest'
29+
30+
permissions:
31+
# required for all workflows
32+
security-events: write
33+
34+
# required to fetch internal or private CodeQL packs
35+
packages: read
36+
37+
# only required for workflows in private repositories
38+
actions: read
39+
contents: write
40+
41+
# required by the mongodb-labs/drivers-github-tools/setup@v2 step
42+
# also required by `rubygems/release-gem`
43+
id-token: write
44+
45+
steps:
46+
- name: "Create temporary app token"
47+
uses: actions/create-github-app-token@v1
48+
id: app-token
49+
with:
50+
app-id: ${{ vars.APP_ID }}
51+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
52+
53+
- name: "Store GitHub token in environment"
54+
run: echo "GH_TOKEN=${{ steps.app-token.outputs.token }}" >> "$GITHUB_ENV"
55+
shell: bash
56+
57+
- name: Checkout repository
58+
uses: actions/checkout@v4
59+
with:
60+
token: ${{ env.GH_TOKEN }}
61+
62+
- name: Setup Ruby
63+
uses: ruby/setup-ruby@v1
64+
with:
65+
ruby-version: '3.2'
66+
bundler-cache: true
67+
68+
- name: Setup GitHub tooling for DBX Drivers
69+
uses: mongodb-labs/drivers-github-tools/setup@v2
70+
with:
71+
aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}
72+
aws_region_name: ${{ vars.AWS_REGION_NAME }}
73+
aws_secret_id: ${{ secrets.AWS_SECRET_ID }}
74+
75+
- name: Get the driver version
76+
shell: bash
77+
run: |
78+
echo "DRIVER_VERSION=$(ruby -Ilib -rmongo/version -e 'puts Mongo::VERSION')" >> "$GITHUB_ENV"
79+
80+
- name: Set output gem file name
81+
shell: bash
82+
run: |
83+
echo "GEM_FILE_NAME=mongo-${{ env.DRIVER_VERSION }}.gem" >> "$GITHUB_ENV"
84+
85+
- name: Build the gem
86+
shell: bash
87+
run: |
88+
gem build --output=${{ env.GEM_FILE_NAME }} mongo.gemspec
89+
90+
- name: Sign the gem
91+
uses: mongodb-labs/drivers-github-tools/gpg-sign@v2
92+
with:
93+
filenames: '${{ env.GEM_FILE_NAME }}'
94+
95+
- name: Create and sign the tag
96+
uses: mongodb-labs/drivers-github-tools/git-sign@v2
97+
with:
98+
command: "git tag -u ${{ env.GPG_KEY_ID }} -m 'Release tag for v${{ env.DRIVER_VERSION }}' v${{ env.DRIVER_VERSION }}"
99+
100+
- name: Push the tag to the repository
101+
shell: bash
102+
run: |
103+
git push origin v${{ env.DRIVER_VERSION }}
104+
105+
- name: Create a new release
106+
shell: bash
107+
run: gh release create v${{ env.DRIVER_VERSION }} --title ${{ env.DRIVER_VERSION }} --generate-notes --draft
108+
109+
- name: Capture the changelog
110+
shell: bash
111+
run: gh release view v${{ env.DRIVER_VERSION }} --json body --template '{{ .body }}' >> changelog
112+
113+
- name: Prepare release message
114+
shell: bash
115+
run: |
116+
echo "${{ format(env.RELEASE_MESSAGE_TEMPLATE, env.DRIVER_VERSION) }}" > release-message
117+
cat changelog >> release-message
118+
119+
- name: Update release information
120+
shell: bash
121+
run: |
122+
echo "RELEASE_URL=$(gh release edit v${{ env.DRIVER_VERSION }} --notes-file release-message)" >> "$GITHUB_ENV"
123+
124+
- name: Upload release artifacts
125+
run: gh release upload v${{ env.DRIVER_VERSION }} ${{ env.GEM_FILE_NAME }} ${{ env.RELEASE_ASSETS }}/${{ env.GEM_FILE_NAME }}.sig
126+
127+
- name: Publish the gem
128+
uses: rubygems/release-gem@v1
129+
with:
130+
await-release: false

README.md

+37-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,43 @@ MongoDB Ruby Driver
55

66
The officially supported Ruby driver for [MongoDB](https://www.mongodb.org/).
77

8-
The Ruby driver supports Ruby 2.5-3.0 and JRuby 9.2.
8+
The Ruby driver supports Ruby 2.7-3.3 and JRuby 9.3-9.4.
9+
10+
## Installation
11+
12+
Install via RubyGems, either via the command-line for ad-hoc uses:
13+
14+
$ gem install mongo
15+
16+
Or via a Gemfile for more general use:
17+
18+
gem 'mongo'
19+
20+
### Release Integrity
21+
22+
Each release of the MongoDB Ruby driver after version 2.20.0 has been automatically built and signed using the team's GPG key.
23+
24+
To verify the driver's gem file:
25+
26+
1. [Download the GPG key](https://pgp.mongodb.com/ruby-driver.asc).
27+
2. Import the key into your GPG keyring with `gpg --import ruby-driver.asc`.
28+
3. Download the gem file (if you don't already have it). You can download it from RubyGems with `gem fetch mongo`, or you can download it from the [releases page](https://github.com/mongodb/mongo-ruby-driver/releases) on GitHub.
29+
4. Download the corresponding detached signature file from the [same release](https://github.com/mongodb/mongo-ruby-driver/releases). Look at the bottom of the release that corresponds to the gem file, under the 'Assets' list, for a `.sig` file with the same version number as the gem you wish to install.
30+
5. Verify the gem with `gpg --verify mongo-X.Y.Z.gem.sig mongo-X.Y.Z.gem` (replacing `X.Y.Z` with the actual version number).
31+
32+
You are looking for text like "Good signature from "MongoDB Ruby Driver Release Signing Key <[email protected]>" in the output. If you see that, the signature was found to correspond to the given gem file.
33+
34+
(Note that other output, like "This key is not certified with a trusted signature!", is related to *web of trust* and depends on how strongly you, personally, trust the `ruby-driver.asc` key that you downloaded from us. To learn more, see https://www.gnupg.org/gph/en/manual/x334.html)
35+
36+
### Why not use RubyGems' gem-signing functionality?
37+
38+
RubyGems' own gem signing is problematic, most significantly because there is no established chain of trust related to the keys used to sign gems. RubyGems' own documentation admits that "this method of signing gems is not widely used" (see https://guides.rubygems.org/security/). Discussions about this in the RubyGems community have been off-and-on for more than a decade, and while a solution will eventually arrive, we have settled on using GPG instead for the following reasons:
39+
40+
1. Many of the other driver teams at MongoDB are using GPG to sign their product releases. Consistency with the other teams means that we can reuse existing tooling for our own product releases.
41+
2. GPG is widely available and has existing tools and procedures for dealing with web of trust (though they are admittedly quite arcane and intimidating to the uninitiated, unfortunately).
42+
43+
Ultimately, most users do not bother to verify gems, and will not be impacted by our choice of GPG over RubyGems' native method.
44+
945

1046
## Documentation
1147

Rakefile

+45-21
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,12 @@
22
# rubocop:todo all
33

44
require 'bundler'
5-
require 'bundler/gem_tasks'
65
require 'rspec/core/rake_task'
7-
# TODO move the mongo require into the individual tasks that actually need it
8-
require 'mongo'
96

107
ROOT = File.expand_path(File.join(File.dirname(__FILE__)))
118

129
$: << File.join(ROOT, 'spec/shared/lib')
1310

14-
require 'mrss/spec_organizer'
15-
1611
CLASSIFIERS = [
1712
[%r,^mongo/server,, :unit_server],
1813
[%r,^mongo,, :unit],
@@ -33,18 +28,55 @@ RUN_PRIORITY = %i(
3328
spec spec_sdam_integration
3429
)
3530

36-
tasks = Rake.application.instance_variable_get('@tasks')
37-
tasks['release:do'] = tasks.delete('release')
38-
3931
RSpec::Core::RakeTask.new(:spec) do |t|
4032
#t.rspec_opts = "--profile 5" if ENV['CI']
4133
end
4234

4335
task :default => ['spec:prepare', :spec]
4436

37+
# stands in for the Bundler-provided `build` task, which builds the
38+
# gem for this project. Our release process builds the gems in a
39+
# particular way, in a GitHub action. This task is just to help remind
40+
# developers of that fact.
41+
task :build do
42+
abort <<~WARNING
43+
`rake build` does nothing in this project. The gem must be built via
44+
the `Driver Release` action on GitHub, which is triggered manually when
45+
a new release is ready.
46+
WARNING
47+
end
48+
49+
# overrides the default Bundler-provided `release` task, which also
50+
# builds the gem. Our release process assumes the gem has already
51+
# been built (and signed via GPG), so we just need `rake release` to
52+
# push the gem to rubygems.
53+
task :release do
54+
require 'mongo/version'
55+
56+
if ENV['GITHUB_ACTION'].nil?
57+
abort <<~WARNING
58+
`rake release` must be invoked from the `Driver Release` GitHub action,
59+
and must not be invoked locally. This ensures the gem is properly signed
60+
and distributed by the appropriate user.
61+
62+
Note that it is the `rubygems/release-gem@v1` step in the `Driver Release`
63+
action that invokes this task. Do not rename or remove this task, or the
64+
release-gem step will fail. Reimplement this task with caution.
65+
66+
mongo-#{Mongo::VERSION}.gem was NOT pushed to RubyGems.
67+
WARNING
68+
end
69+
70+
system 'gem', 'push', "mongo-#{Mongo::VERSION}.gem"
71+
end
72+
73+
task :mongo do
74+
require 'mongo'
75+
end
76+
4577
namespace :spec do
4678
desc 'Creates necessary user accounts in the cluster'
47-
task :prepare do
79+
task prepare: :mongo do
4880
$: << File.join(File.dirname(__FILE__), 'spec')
4981

5082
require 'support/utils'
@@ -53,7 +85,7 @@ namespace :spec do
5385
end
5486

5587
desc 'Waits for sessions to be available in the deployment'
56-
task :wait_for_sessions do
88+
task wait_for_sessions: :mongo do
5789
$: << File.join(File.dirname(__FILE__), 'spec')
5890

5991
require 'support/utils'
@@ -77,7 +109,7 @@ namespace :spec do
77109
end
78110

79111
desc 'Prints configuration used by the test suite'
80-
task :config do
112+
task config: :mongo do
81113
$: << File.join(File.dirname(__FILE__), 'spec')
82114

83115
# Since this task is usually used for troubleshooting of test suite
@@ -90,6 +122,8 @@ namespace :spec do
90122
end
91123

92124
def spec_organizer
125+
require 'mrss/spec_organizer'
126+
93127
Mrss::SpecOrganizer.new(
94128
root: ROOT,
95129
classifiers: CLASSIFIERS,
@@ -109,16 +143,6 @@ namespace :spec do
109143
end
110144
end
111145

112-
namespace :release do
113-
task :check_private_key do
114-
unless File.exist?('gem-private_key.pem')
115-
raise "No private key present, cannot release"
116-
end
117-
end
118-
end
119-
120-
task :release => ['release:check_private_key', 'release:do']
121-
122146
desc 'Build and validate the evergreen config'
123147
task eg: %w[ eg:build eg:validate ]
124148

gem-public_cert.pem

-26
This file was deleted.

mongo.gemspec

+8-11
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,25 @@ Gem::Specification.new do |s|
99
s.name = 'mongo'
1010
s.version = Mongo::VERSION
1111
s.platform = Gem::Platform::RUBY
12-
s.authors = ["The MongoDB Ruby Team"]
13-
s.email = "[email protected]"
12+
s.authors = [ 'The MongoDB Ruby Team' ]
13+
s.email = '[email protected]'
1414
s.homepage = 'https://mongodb.com/docs/ruby-driver/'
1515
s.summary = 'Ruby driver for MongoDB'
16-
s.description = 'A Ruby driver for MongoDB'
1716
s.license = 'Apache-2.0'
17+
s.description = <<~DESC
18+
A pure-Ruby driver for connecting to, querying, and manipulating MongoDB
19+
databases. Officially developed and supported by MongoDB, with love for
20+
the Ruby community.
21+
DESC
1822

1923
s.metadata = {
2024
'bug_tracker_uri' => 'https://jira.mongodb.org/projects/RUBY',
2125
'changelog_uri' => 'https://github.com/mongodb/mongo-ruby-driver/releases',
22-
'documentation_uri' => 'https://mongodb.com/docs/ruby-driver/',
2326
'homepage_uri' => 'https://mongodb.com/docs/ruby-driver/',
27+
'documentation_uri' => 'https://mongodb.com/docs/ruby-driver/current/tutorials/quick-start/',
2428
'source_code_uri' => 'https://github.com/mongodb/mongo-ruby-driver',
2529
}
2630

27-
if File.exist?('gem-private_key.pem')
28-
s.signing_key = 'gem-private_key.pem'
29-
s.cert_chain = ['gem-public_cert.pem']
30-
else
31-
warn "[#{s.name}] Warning: No private key present, creating unsigned gem."
32-
end
33-
3431
s.files = Dir.glob('{bin,lib,spec}/**/*')
3532
s.files += %w[mongo.gemspec LICENSE README.md CONTRIBUTING.md Rakefile]
3633
s.test_files = Dir.glob('spec/**/*')

profile/driver_bench/rake/tasks.rake

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# frozen_string_literal: true
22

3-
require_relative '../suite'
4-
53
task driver_bench: %i[ driver_bench:data driver_bench:run ]
64

75
SPECS_REPO_URI = '[email protected]:mongodb/specifications.git'
@@ -32,7 +30,9 @@ namespace :driver_bench do
3230
end
3331

3432
desc 'Runs the DriverBench benchmark suite'
35-
task :run do
33+
task run: 'driver_bench:initialize' do
34+
require_relative '../suite'
35+
3636
Mongo::DriverBench::Suite.run!
3737
end
3838
end

0 commit comments

Comments
 (0)