release_ceremony 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -1
- data/README.md +61 -6
- data/lib/release_ceremony/runner.rb +16 -2
- data/lib/release_ceremony/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15c43fac7dba19c918054427d55e8799a7c029c347d487f3ef89428195112bd3
|
|
4
|
+
data.tar.gz: 43ca6ea3adcb1e615d0ca25c56f4bbb97aeaff3a6582897e6031fd8eb9e478b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88bcf75134463e12229886c261bd79d49965d87301a6ceec800f5712ba50ce9822a4e200a70c1b94851a1ec31e6490bb41c30d369a6dbfc45d4891e07b33289b
|
|
7
|
+
data.tar.gz: cf437bd36a3b1e3c1f29797d0331ceadb4bafdb0648731d190efc77bb5a3df4faa4643043be337fbc3e20c047498cddd59f13260910294fc28802287dde0e3dd
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,16 @@ and this project adheres to
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [1.0.1] - 2026-07-25
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Child commands no longer inherit the ceremony's own Bundler environment.
|
|
16
|
+
Running the ceremony with `bundle exec` used to leak `RUBYOPT` and the
|
|
17
|
+
`BUNDLE_*` variables into the verification commands, so a nested
|
|
18
|
+
`bundle install` crashed with a `LoadError` in the `bundle` executable.
|
|
19
|
+
Every command now runs in an unbundled environment when Bundler is loaded.
|
|
20
|
+
|
|
11
21
|
## [1.0.0] - 2026-07-25
|
|
12
22
|
|
|
13
23
|
### Added
|
|
@@ -24,5 +34,6 @@ and this project adheres to
|
|
|
24
34
|
overrides in `.release_ceremony.yml` (release file paths, verification
|
|
25
35
|
commands, workflow file, default branch, and remote).
|
|
26
36
|
|
|
27
|
-
[Unreleased]: https://github.com/dominicsayers/release_ceremony/compare/v1.0.
|
|
37
|
+
[Unreleased]: https://github.com/dominicsayers/release_ceremony/compare/v1.0.1...HEAD
|
|
38
|
+
[1.0.1]: https://github.com/dominicsayers/release_ceremony/compare/v1.0.0...v1.0.1
|
|
28
39
|
[1.0.0]: https://github.com/dominicsayers/release_ceremony/compare/v0.0.0...v1.0.0
|
data/README.md
CHANGED
|
@@ -20,17 +20,19 @@ until you type the confirmation phrase.
|
|
|
20
20
|
## The ceremony
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
VERSION=1.2.3
|
|
24
|
+
|
|
25
|
+
git switch -c "release-v$VERSION"
|
|
26
|
+
release_ceremony prepare "$VERSION"
|
|
25
27
|
# inspect the changes, then commit and push the branch
|
|
26
28
|
# open the pull request and merge it
|
|
27
|
-
release_ceremony publish
|
|
29
|
+
release_ceremony publish "$VERSION"
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
Publication itself happens in CI: the pushed tag triggers your release
|
|
31
|
-
workflow, which should publish to RubyGems (
|
|
32
|
-
[Trusted Publishing](
|
|
33
|
-
|
|
33
|
+
workflow, which should publish to RubyGems (see
|
|
34
|
+
[Setting up Trusted Publishing](#setting-up-trusted-publishing)). `publish`
|
|
35
|
+
only tags, watches, and verifies.
|
|
34
36
|
|
|
35
37
|
## Installation
|
|
36
38
|
|
|
@@ -60,6 +62,59 @@ The gem name and repository URL are read from the repository's gemspec
|
|
|
60
62
|
(`metadata['source_code_uri']`, falling back to `homepage`). Everything else
|
|
61
63
|
above is a convention that `.release_ceremony.yml` can override.
|
|
62
64
|
|
|
65
|
+
## Setting up Trusted Publishing
|
|
66
|
+
|
|
67
|
+
`publish` never pushes to RubyGems itself; the tag-triggered workflow does,
|
|
68
|
+
ideally with [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/)
|
|
69
|
+
and the [`rubygems/release-gem`](https://github.com/rubygems/release-gem)
|
|
70
|
+
action, so that no API key exists to be stored or leaked. One-time setup:
|
|
71
|
+
|
|
72
|
+
- **Existing gem**: on rubygems.org, open the gem → Trusted publishers →
|
|
73
|
+
Create, and enter the repository owner and name, the workflow filename
|
|
74
|
+
(e.g. `release.yml`), and the environment name the workflow declares
|
|
75
|
+
(e.g. `release`).
|
|
76
|
+
- **New gem**: nothing is created on RubyGems manually. Instead, register a
|
|
77
|
+
[pending trusted publisher](https://rubygems.org/profile/oidc/pending_trusted_publishers)
|
|
78
|
+
(Profile → OIDC → Pending trusted publishers) with the same fields plus the
|
|
79
|
+
gem's name. The first successful push from the workflow creates the gem,
|
|
80
|
+
converts the pending publisher into a normal one, and makes you the owner.
|
|
81
|
+
Pending publishers expire 12 hours after creation, so register on the day
|
|
82
|
+
of the first release; if it lapses, just register it again.
|
|
83
|
+
- **GitHub environment**: if the workflow's publish job declares
|
|
84
|
+
`environment: release`, create that environment in the repository settings
|
|
85
|
+
(Settings → Environments). Trusted Publishing only accepts tokens minted
|
|
86
|
+
from the registered environment, and you can attach protection rules such
|
|
87
|
+
as required reviewers to it.
|
|
88
|
+
|
|
89
|
+
RubyGems also ships a CLI that automates the RubyGems side:
|
|
90
|
+
[`gem exec configure_trusted_publisher`](https://github.com/rubygems/configure_trusted_publisher).
|
|
91
|
+
|
|
92
|
+
## Re-running publish
|
|
93
|
+
|
|
94
|
+
`publish` converges when reality matches expectations and stops loudly when
|
|
95
|
+
it doesn't, so after most failures you can simply run it again:
|
|
96
|
+
|
|
97
|
+
- Everything before the confirmation prompt is read-only validation; a
|
|
98
|
+
failure there has changed nothing.
|
|
99
|
+
- Tagging is guarded: if the remote tag already exists at the merge commit,
|
|
100
|
+
tagging and pushing are skipped; if only a local tag exists, it is pushed
|
|
101
|
+
as-is. A tag that exists anywhere but points to the wrong commit aborts the
|
|
102
|
+
ceremony before the prompt — `publish` never moves or deletes a tag.
|
|
103
|
+
- Watching the workflow and checking RubyGems are read-only, so a run that
|
|
104
|
+
died mid-watch or hit RubyGems index lag re-attaches to the completed
|
|
105
|
+
workflow run and re-verifies. Re-running after a fully successful publish
|
|
106
|
+
succeeds again without side effects.
|
|
107
|
+
|
|
108
|
+
The exception is a failed release workflow. Re-running `publish` does not
|
|
109
|
+
retry it: the tag is already on the remote, so nothing re-pushes, no new
|
|
110
|
+
workflow is triggered, and watching the same failed run fails again. Re-run
|
|
111
|
+
the workflow first, then re-run `publish` to watch and verify it:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
gh run rerun <run-id> --failed
|
|
115
|
+
release_ceremony publish "$VERSION"
|
|
116
|
+
```
|
|
117
|
+
|
|
63
118
|
## Configuration
|
|
64
119
|
|
|
65
120
|
Create `.release_ceremony.yml` in the repository root to override any of the
|
|
@@ -16,7 +16,7 @@ module ReleaseCeremony
|
|
|
16
16
|
|
|
17
17
|
def capture(*command)
|
|
18
18
|
validate_command!(command)
|
|
19
|
-
captured_stdout, captured_stderr, status = Open3.capture3(*command)
|
|
19
|
+
captured_stdout, captured_stderr, status = unbundled { Open3.capture3(*command) }
|
|
20
20
|
return captured_stdout if status.success?
|
|
21
21
|
|
|
22
22
|
raise_command_error(command, captured_stderr)
|
|
@@ -26,7 +26,7 @@ module ReleaseCeremony
|
|
|
26
26
|
|
|
27
27
|
def run(*command)
|
|
28
28
|
validate_command!(command)
|
|
29
|
-
result = system(*command)
|
|
29
|
+
result = unbundled { system(*command) }
|
|
30
30
|
return true if result
|
|
31
31
|
|
|
32
32
|
result.nil? ? raise_spawn_error(command) : raise_command_error(command)
|
|
@@ -56,6 +56,20 @@ module ReleaseCeremony
|
|
|
56
56
|
|
|
57
57
|
private
|
|
58
58
|
|
|
59
|
+
# The ceremony normally runs under bundle exec, but its child processes
|
|
60
|
+
# must not inherit that Bundler context: a nested bundle command crashes
|
|
61
|
+
# when RUBYOPT and the BUNDLE_* variables point at the ceremony's own
|
|
62
|
+
# bundle instead of the repository being released
|
|
63
|
+
def unbundled(&)
|
|
64
|
+
return yield unless bundler_loaded?
|
|
65
|
+
|
|
66
|
+
Bundler.with_unbundled_env(&)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def bundler_loaded?
|
|
70
|
+
!defined?(Bundler).nil?
|
|
71
|
+
end
|
|
72
|
+
|
|
59
73
|
def validate_command!(command)
|
|
60
74
|
return if command.length > 1 && command.all?(String)
|
|
61
75
|
|