bake-gem-github 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7aeed59bfe0b809d8f5c4da2fdc4e3bc9366d250ce03af18df7bb9f505a1d9f9
4
- data.tar.gz: 0c77f24fafe65ee8725bff954195f98e1605565bae0ba681285a0a71cfa53e97
3
+ metadata.gz: cbf4137a4b137fec26486a02e3941687f2c5fc9ded9ab95eb95619657892e1ae
4
+ data.tar.gz: c4e2cf0281e703e2d2264e5b8f08420ba7afeb5f7819e075a196e9334f24a300
5
5
  SHA512:
6
- metadata.gz: 1a39b1792e3488197a369ce19817ca13c23a53bc61c2241912e5c310f6665e1e8ca31fac5a86af45ec26046ea2d50d7c7616c4cc5e75b4e137c549fba8fe1c0f
7
- data.tar.gz: e09fac61c9cc42f767c48673dcfda735cb502d1e453bfc0c4efe1a9319dd4ff4b6ab9d573761a039beb5e26f0d76ea7cbdda3340b186c55b18330df2a5a484b3
6
+ metadata.gz: 2996e3c56f916e0d367c6fc75e3b7d9a478d8a08ee4d6d9bd775412978965c8c83d0b2608655d5d0c23776ce29af835cfa8dea4ec23ffb9e1afb3d0cefb74069
7
+ data.tar.gz: 4b78a2dd8d672b8831b8408e59494832ef45afee93489e9f27dc4aa7aae593f541350133886fa2f7957e2befee20abd4faa92b9df0b110620db78cdd1c1b2b01
checksums.yaml.gz.sig CHANGED
Binary file
@@ -3,49 +3,75 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2026, by Samuel Williams.
5
5
 
6
- require_relative "../../../lib/bake/gem/github/publisher"
6
+ require "bake/gem/github/project"
7
7
 
8
8
  # Prepare a patch release and open its PR.
9
- def patch
10
- Bake::Gem::GitHub::Project.new(context.root).prepare(context, "patch")
9
+ # @parameter refresh [Boolean] Preserve and regenerate an existing release branch.
10
+ # @returns [String] The release PR URL.
11
+ def patch(refresh: false)
12
+ Bake::Gem::GitHub::Project.new(context.root).prepare(context, "patch", refresh: refresh)
11
13
  end
12
14
 
13
15
  # Prepare a minor release and open its PR.
14
- def minor
15
- Bake::Gem::GitHub::Project.new(context.root).prepare(context, "minor")
16
+ # @parameter refresh [Boolean] Preserve and regenerate an existing release branch.
17
+ # @returns [String] The release PR URL.
18
+ def minor(refresh: false)
19
+ Bake::Gem::GitHub::Project.new(context.root).prepare(context, "minor", refresh: refresh)
16
20
  end
17
21
 
18
22
  # Prepare a major release and open its PR.
19
- def major
20
- Bake::Gem::GitHub::Project.new(context.root).prepare(context, "major")
23
+ # @parameter refresh [Boolean] Preserve and regenerate an existing release branch.
24
+ # @returns [String] The release PR URL.
25
+ def major(refresh: false)
26
+ Bake::Gem::GitHub::Project.new(context.root).prepare(context, "major", refresh: refresh)
21
27
  end
22
28
 
23
- # Resolve and validate a merged PR, emitting a commit output for the publishing job.
24
- def resolve(number: ENV.fetch("RELEASE_PR"))
25
- result = Bake::Gem::GitHub::Project.new(context.root).inspect_release(number)
29
+ # Resolve and validate a pushed commit, emitting its merged PR and commit for publishing.
30
+ # @parameter number [String | Nil] A merged PR number for older workflows; defaults to `RELEASE_PR`.
31
+ # @parameter commit [String | Nil] The pushed commit SHA; defaults to `RELEASE_COMMIT` and takes precedence over `number`.
32
+ # @returns [Hash | Nil] Release metadata, or nil for an ordinary PR.
33
+ def resolve(number: ENV["RELEASE_PR"], commit: ENV["RELEASE_COMMIT"])
34
+ project = Bake::Gem::GitHub::Project.new(context.root)
35
+ result = commit ? project.inspect_commit(commit) : project.inspect_release(number)
36
+
26
37
  if path = ENV["GITHUB_OUTPUT"]
27
38
  File.open(path, "a") do |file|
28
39
  file.puts "release=#{!result.nil?}"
29
- file.puts "commit=#{result.fetch(:commit)}" if result
40
+ if result
41
+ file.puts "commit=#{result.fetch(:commit)}"
42
+ file.puts "pull_request=#{result.fetch(:pull_request)}"
43
+ end
30
44
  end
31
45
  end
32
- result
46
+
47
+ return result
33
48
  end
34
49
 
35
50
  # Build or restore the exact artifact for a merged release PR.
51
+ # @parameter number [String] The merged release PR number; defaults to `RELEASE_PR`.
52
+ # @returns [Hash] The built or restored release receipt.
36
53
  def build(number: ENV.fetch("RELEASE_PR"))
37
- Bake::Gem::GitHub::Publisher.new(context.root).build(number)
54
+ require "bake/gem/github/publisher"
55
+
56
+ return Bake::Gem::GitHub::Publisher.new(context.root).build(number)
38
57
  end
39
58
 
40
59
  # Verify, upload and finalize a merged release using its retained artifact.
60
+ # @parameter number [String] The merged release PR number; defaults to `RELEASE_PR`.
61
+ # @returns [Hash] The published release receipt.
41
62
  def publish(number: ENV.fetch("RELEASE_PR"))
42
- Bake::Gem::GitHub::Publisher.new(context.root).publish(number)
63
+ require "bake/gem/github/publisher"
64
+
65
+ return Bake::Gem::GitHub::Publisher.new(context.root).publish(number)
43
66
  end
44
67
 
45
68
  # Rerun the original publishing workflow, preserving event identity and artifact bytes.
69
+ # @parameter run [String] The original release-publish workflow run ID.
70
+ # @returns [Boolean] True when GitHub accepts the rerun request.
46
71
  def resume(run:)
47
72
  project = Bake::Gem::GitHub::Project.new(context.root)
48
73
  details = project.api("actions/runs/#{Integer(run)}")
49
74
  raise "Expected a release-publish workflow run." unless details.fetch("path") == ".github/workflows/release-publish.yaml"
50
- project.system("gh", "run", "rerun", run.to_s, "--repo", project.config.fetch("repository"), chdir: context.root)
75
+
76
+ return project.system("gh", "run", "rerun", run.to_s, "--repo", project.config.fetch("repository"), chdir: context.root)
51
77
  end
@@ -4,18 +4,23 @@
4
4
  # Copyright, 2026, by Samuel Williams.
5
5
 
6
6
  # Inspect external settings before applying the generated policy.
7
+ # @returns [Hash] Desired and observed settings for review.
7
8
  def plan
8
9
  context.lookup("gem:github:doctor").call
9
10
  end
10
11
 
11
- # Apply the four managed rulesets using the current gh administrator credentials.
12
+ # Apply the four managed rulesets and configured environment reviewers using the current gh administrator credentials.
13
+ # @returns [Hash] The managed ruleset payloads after successful application.
12
14
  def apply
13
- require_relative "../../../lib/bake/gem/github/project"
15
+ require "bake/gem/github/project"
16
+
14
17
  Bake::Gem::GitHub::Project.new(context.root).apply
15
18
  end
16
19
 
17
20
  # Update generated files in the working tree using config/release.yaml and the installed templates.
21
+ # @returns [Array(String)] Changed paths relative to the repository root.
18
22
  def update
19
- require_relative "../../../lib/bake/gem/github/setup"
23
+ require "bake/gem/github/setup"
24
+
20
25
  Bake::Gem::GitHub::Setup.new(context.root).update
21
26
  end
data/bake/gem/github.rb CHANGED
@@ -3,29 +3,43 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2026, by Samuel Williams.
5
5
 
6
- # Generate local release workflows, policy, and documentation for review.
6
+ # Generate local release workflows, policy payloads, and configuration for review.
7
7
  # @parameter checks [Array(String)] Required CI check names, including matrix entries.
8
- # @parameter repository [String] Canonical owner/repository.
9
- # @parameter branch [String] Default branch name.
8
+ # @parameter repository [String] Canonical owner/repository; discovered through GitHub when omitted.
9
+ # @parameter branch [String] Default branch name; discovered through GitHub when omitted.
10
10
  # @parameter approvals [Integer] Number of approving reviews.
11
- # @parameter signing [Boolean] Require legacy certificate signing.
11
+ # @parameter reviewers [Array(String)] Publishing environment reviewers: user logins or organization/team names. Omit to leave environment settings unmanaged.
12
+ # @parameter signing [Boolean] Require certificate signing; when omitted, enable it if `release.cert` exists.
12
13
  # @parameter ruby [String] Ruby version for release workflows.
13
- def setup(checks:, repository: nil, branch: nil, approvals: 2, signing: nil, ruby: "3.4")
14
- require_relative "../../lib/bake/gem/github/setup"
14
+ # @returns [Array(String)] Generated paths relative to the repository root.
15
+ def setup(checks:, repository: nil, branch: nil, approvals: 2, reviewers: nil, signing: nil, ruby: "3.4")
16
+ require "bake/gem/github/setup"
15
17
  require "bake/gem/shell"
18
+
16
19
  helper = Object.new.extend(Bake::Gem::Shell)
17
20
  remote = if repository && branch
18
21
  {}
19
22
  else
20
23
  JSON.parse(helper.readlines("gh", "repo", "view", "--json", "nameWithOwner,defaultBranchRef", chdir: context.root).join)
21
24
  end
22
- options = {repository: repository || remote.fetch("nameWithOwner"), branch: branch || remote.fetch("defaultBranchRef").fetch("name"), checks: checks, approvals: approvals, ruby: ruby}
25
+
26
+ options = {
27
+ repository: repository || remote.fetch("nameWithOwner"),
28
+ branch: branch || remote.fetch("defaultBranchRef").fetch("name"),
29
+ checks: checks,
30
+ approvals: approvals,
31
+ reviewers: reviewers,
32
+ ruby: ruby,
33
+ }
23
34
  options[:signing] = signing unless signing.nil?
35
+
24
36
  Bake::Gem::GitHub::Setup.new(context.root).generate(**options)
25
37
  end
26
38
 
27
- # Show the desired rules, existing rules, environments, and RubyGems bootstrap values.
39
+ # Show the desired rules and environment reviewers, existing settings, and RubyGems bootstrap values.
40
+ # @returns [Hash] Desired and observed settings; RubyGems values describe the expected configuration.
28
41
  def doctor
29
- require_relative "../../lib/bake/gem/github/project"
42
+ require "bake/gem/github/project"
43
+
30
44
  Bake::Gem::GitHub::Project.new(context.root).doctor
31
45
  end
@@ -1,121 +1,124 @@
1
- # GitHub Releases
1
+ # Getting Started
2
2
 
3
- This guide explains how to set up reviewed Ruby gem releases with `bake-gem-github`, native GitHub rules, RubyGems Trusted Publishing, and retained release artifacts.
3
+ This guide explains how to configure reviewed Ruby gem releases and prepare the first release PR with `bake-gem-github`.
4
4
 
5
- ## Installation
5
+ ## How releases work
6
6
 
7
- Add `bake-gem-github` and `agent-context` to your maintenance bundle. This companion requires `bake-gem` 0.15 or later for branch preparation and regeneration validation. Install the maintenance group in CI with `BUNDLE_WITH=maintenance`.
7
+ Maintainers prepare a release PR containing the version bump and generated release notes. CI regenerates those changes from the current base to verify the content. Native GitHub rules control approval and merging. A push to the default branch starts release inspection; GitHub Actions builds the exact pushed commit only when it is a validated, merged release PR, then publishes its verified artifact to RubyGems.
8
8
 
9
- Use one gemspec, a stable three-part version in `lib/.../version.rb`, and repeatable `after_gem_release_version_increment` hooks. Hooks run from a clean base during validation. Commit dependency locks when practical; changing generation tools or using live network/time inputs can make old release content fail validation.
9
+ `bake-gem` provides version updates, release hooks, and clean builds. `bake-gem-github` adds PR preparation, GitHub policy, and remote publishing. The supported process uses one gemspec, stable three-part versions, merge or squash merging, and RubyGems.org.
10
10
 
11
- ## Setup and migration
11
+ ## Installation
12
12
 
13
- Run setup in each repository. It discovers the canonical repository and default branch through `gh` and generates reviewable local files. Supply the actual required CI job names, including supported matrix entries:
13
+ Add these dependencies to the maintenance group in `gems.rb`:
14
14
 
15
- ``` bash
16
- bundle exec bake gem:github:setup checks="Test Ruby 3.3,Test Ruby 3.4,RuboCop"
17
- bundle exec bake agent:context:install
18
- bundle exec bake gem:github:setup:plan
15
+ ``` ruby
16
+ group :maintenance, optional: true do
17
+ gem "bake-gem-github"
18
+ gem "agent-context"
19
+ end
19
20
  ```
20
21
 
21
- Setup adds three release workflows, `config/release.yaml`, native ruleset payloads, and `.github/releasing.md`. Identical reruns do nothing; conflicting existing files stop before any file is written. Setup does not replace other publishers: remove conflicting release workflows during migration.
22
-
23
- To adopt template fixes after upgrading the gem, start from a clean working tree, edit `config/release.yaml` as needed, and regenerate:
22
+ Install that group and its guidance:
24
23
 
25
24
  ``` bash
26
- bundle exec bake gem:github:setup:update
27
- git diff
25
+ bundle config set --local with maintenance
26
+ bundle install
27
+ bundle exec bake agent:context:install
28
28
  ```
29
29
 
30
- The task updates the managed workflows, policy payloads, configuration formatting, and maintainer instructions directly in the working tree and returns the changed paths. It does not stage, commit, or change remote settings. An agent or maintainer can review the diff and selectively keep changes, restoring repository-specific customizations from Git where needed. Commit or stash existing edits first: generated files are replaced by the current templates. Repeating an update produces no further changes; intentionally retained customizations will appear in later update diffs. Apply remote rulesets after the corresponding workflows are running.
30
+ The companion requires `bake-gem` 0.15 or later. Its generated release workflows install maintenance dependencies with `BUNDLE_WITH=maintenance`.
31
31
 
32
- Release workflows follow `bake modernize` action versions and use moving major tags where upstream provides them. These tags receive upstream updates automatically; full commit hashes select fixed revisions. The RubyGems credentials action uses its [documented `@main` reference](https://github.com/rubygems/configure-rubygems-credentials#trusted-publisher-recommended), since upstream does not provide a moving major tag. Repositories that require fixed revisions can customize these references.
32
+ Use a version constant in `lib/.../version.rb` and repeatable `after_gem_release_version_increment` hooks. Validation invokes these hooks from a clean base. Generation must not depend on changing network responses or the current time; review dependency updates that could alter generated content.
33
33
 
34
- The default is two approvals with explicit administrator bypass, dismissed stale reviews, approval of the last push, strict up-to-date CI, and immutable default-branch history/release tags. These branch rules affect **all PRs** into the default branch. Ordinary administrator reviews count as one review. A human who dispatches a bot-authored PR is not its author under GitHub's native rules.
34
+ ## Generate repository configuration
35
35
 
36
- Review `gem:github:setup:plan`, merge the setup PR, and confirm that **Release validation** and every selected check run. Then apply the four managed rulesets using an administrator's `gh` login:
36
+ Run setup from the repository root with the actual required CI job names, including supported matrix entries:
37
37
 
38
38
  ``` bash
39
- bundle exec bake gem:github:setup:apply
39
+ bundle exec bake gem:github:setup checks="3.3 on ubuntu,3.3 on macos,3.4 on ubuntu,3.4 on macos,4.0 on ubuntu,4.0 on macos,check,ruby on ubuntu,ruby on macos,validate"
40
+ bundle exec bake gem:github:setup:plan
40
41
  ```
41
42
 
42
- This command changes remote rulesets and preserves unrelated rulesets. Existing rulesets with the four managed names are updated. Organization rules and other existing protections still apply. Check names in configuration must exactly match GitHub checks; a partial selection does not mean all CI is required. Keep rebase merging and merge queues disabled for the initial rollout.
43
+ This example follows the standard `bake modernize` test, RuboCop, and coverage job names. Select the jobs produced by your repository. Experimental Ruby jobs are not required. Setup adds `Release validation` automatically.
43
44
 
44
- Create a `rubygems` GitHub environment restricted to the default branch. Do not add a second routine reviewer gate. On RubyGems, an owner must configure a Trusted Publisher with the owner/repository, workflow filename **`release-publish.yaml`**, and environment **`rubygems`** shown by `doctor`. Ownership/MFA and environment/signing bootstrap are deliberate manual steps in this first implementation; `doctor` prints desired and observed GitHub settings, not a claim that RubyGems ownership or publisher trust has been verified. See [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/).
45
+ Setup discovers the canonical GitHub repository and default branch through `gh`. It generates three release workflows, `config/release.yaml`, and four native ruleset payloads. Identical reruns do nothing; conflicting existing files stop generation before any file is written. Review and commit the files in a setup PR, and remove conflicting publishing workflows.
45
46
 
46
- If `release.cert` exists, setup enables legacy signing. Keep this public certificate in Git and install its matching **private key** as the Actions secret `GEM_SIGNING_KEY`. Use the `rubygems` environment or an organization secret available to the release repositories. The workflow checks the certificate validity, key match, and resulting package signatures. To opt out explicitly, pass `signing=false` during setup. No long-lived RubyGems publishing key is required.
47
+ The rules require two approvals by default, allow explicit administrator bypass, dismiss stale reviews, require approval of the last push, and require up-to-date CI. They protect default-branch history and release tags against deletion or replacement. These branch rules apply to **all PRs** into the default branch. An ordinary administrator approval counts as one review; bypass is a separate action.
47
48
 
48
- Before enabling releases, confirm two people can administer the repository and recover the RubyGems account/signing key, and enough maintainers can satisfy the review policy. Pilot on one low-risk gem and prove publishing, administrator bypass, fork merges, and recovery before rolling out broadly. No organization-wide migration or live publisher setup is performed by these tasks.
49
+ ## Configure publishing credentials
49
50
 
50
- ## Request and review
51
+ Create a `rubygems` GitHub environment restricted to the default branch. On RubyGems, an owner must configure a Trusted Publisher with the values printed by `gem:github:setup:plan`: the owner/repository, workflow filename **`release-publish.yaml`**, and environment **`rubygems`**. See [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) for the account setup.
51
52
 
52
- ``` bash
53
- # Local branch and commit only:
54
- bundle exec bake gem:release:branch:patch
53
+ Ownership, MFA, and signing bootstrap are manual setup steps. The plan reports expected RubyGems values; it does not verify ownership or publisher trust. Trusted Publishing supplies the publishing credential for each run, so a long-lived RubyGems API key is unnecessary.
55
54
 
56
- # From the current default branch: prepare, validate, push and open PR:
57
- bundle exec bake gem:github:release:patch
55
+ When `release.cert` exists, setup enables certificate signing. Commit the public certificate and install its matching private key as `GEM_SIGNING_KEY`, either in the `rubygems` environment or as an organization secret available to the repository. The publisher checks certificate validity, key matching, and package signatures. Use `signing=false` during setup to disable certificate signing.
56
+
57
+ Ensure another maintainer can administer the repository and recover its RubyGems account and signing key.
58
58
 
59
- # Remote request (also available in the Actions UI):
60
- gh workflow run release-prepare.yaml -f bump=patch
59
+ ## Authorize publishing
60
+
61
+ PR reviews approve the source changes. To require a release manager to authorize publication after merge, configure required reviewers on the `rubygems` environment. The publishing job waits for this separate approval before running.
62
+
63
+ Pass `reviewers=your-org/managers` to `gem:github:setup`, or add the reviewers to an existing `config/release.yaml`:
64
+
65
+ ``` yaml
66
+ reviewers:
67
+ - your-org/managers
61
68
  ```
62
69
 
63
- Replace `patch` with `minor` or `major`. The wrapper fetches the default branch and tags, refuses a stale local checkout, and reports an existing release PR instead of opening another. GitHub's built-in token may require a writer to approve running workflows for its created PR; enable Actions' permission to create PRs. An organization-owned App token can be adopted later if automatic CI triggering is needed.
70
+ Replace `your-org/managers` with your organization and team slug, for example `socketry/managers`. Individual user logins are also supported. Reviewers need at least read access to the repository. Setup resolves their GitHub IDs without changing repository access or team membership.
71
+
72
+ GitHub accepts one to six users or teams, and **one approval from any listed reviewer or team member is sufficient**. It does not support a minimum environment approval count. The default two PR approvals are independent of this publishing approval.
64
73
 
65
- All release changes belong in the PR. Core preparation commits additions and deletions from release hooks but never pushes, tags or publishes. Validation independently generates the expected tree from the current base. A changed base SHA alone is fine; changed generated notes are not. Ordinary PRs with no version change pass release validation and still build unsigned.
74
+ Create the environment and restrict its deployment branch as described above before running plan or apply with reviewers configured. The plan previews the current and desired environment settings. Apply replaces its reviewer list while preserving its wait timer, self-review prevention, administrator bypass setting, and deployment branch restrictions. Custom deployment protection rules are managed separately and are not modified. Reapplying an identical reviewer list leaves the environment unchanged.
66
75
 
67
- If regeneration fails, prepare a new branch from the current default branch and review the new diff. Preserve manual release-branch edits separately. Automatic refresh/force-push is not implemented. A failure during preparation leaves the branch and generated changes available for inspection.
76
+ Omitting `reviewers` leaves environment settings unmanaged, including any existing reviewer requirement. An empty list is rejected. To remove an existing requirement, change the environment settings explicitly in GitHub.
68
77
 
69
- ## Publish and verify
78
+ The RubyGems Trusted Publisher must explicitly require the `rubygems` environment; leaving that field blank would allow this trusted publisher to authenticate jobs without the environment approval. Keep administrator bypass enabled if administrators should be able to explicitly authorize publication without a reviewer. Environment approvals are available for public repositories on GitHub Free.
70
79
 
71
- After merge/squash, the publishing workflow verifies GitHub's merged PR record and ancestry, then checks out the exact merged commit. Later development on the default branch is allowed. It regenerates against the merged commit's **first parent**, builds in a clean worktree, optionally certificate-signs, and creates two attestations over the final bytes:
80
+ ## Enable the policy
81
+
82
+ Merge the setup PR and confirm every selected CI job, including **Release validation**, runs. Review the plan again, then apply its rules using an administrator's `gh` login:
83
+
84
+ ``` bash
85
+ bundle exec bake gem:github:setup:plan
86
+ bundle exec bake gem:github:setup:apply
87
+ ```
72
88
 
73
- - A Sigstore bundle submitted explicitly with `gem push --attestation` using RubyGems 4.0.21.
74
- - GitHub's native SLSA provenance covering both the gem and `release.json`. This signed receipt binds the gem digest to the exact release commit, even when the workflow's own default-branch revision is newer.
89
+ Apply updates the four managed rulesets and, when configured, the existing environment's reviewer list. It preserves unrelated rulesets. Other repository and organization protections still apply. Keep check names in `config/release.yaml` synchronized with the workflows, and apply updated rules after renamed jobs are available. Keep rebase merging and merge queues disabled for this process.
75
90
 
76
- The workflow retains the gem, receipt and attestations before obtaining RubyGems publishing credentials. It verifies both attestations, checks the uploaded bytes and registry bundle, then pushes the specific version tag and creates the GitHub release. Existing tags/assets are checked and never overwritten. The old `after_gem_release` GitHub hook is not called by this pipeline, so there is one owner for release creation.
91
+ ## Prepare the first release PR
77
92
 
78
- The draft release description includes the exact version's notes from `releases.md` in the merged release checkout, followed by the PR URL, source commit, and gem digest. Notes are extracted using `bake-releases`; a missing or empty section leaves the metadata as the description. Retries preserve the existing release description.
93
+ From an up-to-date default branch:
79
94
 
80
95
  ``` bash
81
- set -e
82
- for file in example-1.2.3.gem release.json; do
83
- gh attestation verify "$file" \
84
- --repo OWNER/REPOSITORY --bundle provenance.sigstore.json \
85
- --cert-identity https://github.com/OWNER/REPOSITORY/.github/workflows/release-publish.yaml@refs/heads/main \
86
- --source-ref refs/heads/main --deny-self-hosted-runners
87
- done
88
-
89
- jq -e --arg commit MERGED_SHA \
90
- --arg digest "$(shasum -a 256 example-1.2.3.gem | cut -d ' ' -f1)" \
91
- '.commit == $commit and .sha256 == $digest' release.json
92
-
93
- gem exec sigstore-cli:0.2.3 verify example-1.2.3.gem \
94
- --bundle example-1.2.3.gem.sigstore.json \
95
- --certificate-identity https://github.com/OWNER/REPOSITORY/.github/workflows/release-publish.yaml@refs/heads/main \
96
- --certificate-oidc-issuer https://token.actions.githubusercontent.com
96
+ bundle exec bake gem:github:release:patch
97
97
  ```
98
98
 
99
- Download `release.json` and `provenance.sigstore.json` alongside the gem. Verify both subjects before reading the receipt's source commit and digest. GitHub CLI's `--source-digest` checks the workflow revision, which may differ from the release commit recorded in the signed receipt. Replace `main` with the configured default branch in these commands.
99
+ The task prepares, validates, pushes, and opens the release PR. Review its version and release notes, wait for CI, and merge under the repository's approval policy. If environment reviewers are configured, a release manager then approves the publishing job in GitHub Actions. The publish workflow builds the merged release, verifies and preserves the artifact, publishes to RubyGems, and finalizes the version tag and GitHub release.
100
100
 
101
- Native GitHub records the merge and any bypass; the signed artifact receipt includes the PR and merging actor. This version does not export organization audit-log evidence or infer bypass reasons from review counts.
101
+ See [Preparing Releases](../preparing-releases/index) for remote requests and stale-content refresh, [Verifying Releases](../verifying-releases/index) for artifact checks, and [Recovering Releases](../recovering-releases/index) when a workflow stops partway through.
102
102
 
103
- ## Recovery
103
+ ## Update generated files
104
104
 
105
- Use **Re-run all jobs** on the original publishing run, or:
105
+ After upgrading the gem, start from a clean working tree and regenerate using your existing configuration:
106
106
 
107
107
  ``` bash
108
- bundle exec bake gem:github:release:resume run=RUN_ID
108
+ bundle exec bake gem:github:setup:update
109
+ git diff
109
110
  ```
110
111
 
111
- Rerunning keeps the original event identity. A retained artifact is downloaded and its source identity/digest checked. A matching registry version resumes tag/release finalization; different bytes or a conflicting tag stop. There is no automatic yank, retag, or rebuild of an already-published version. Registry propagation is retried every ten seconds for up to one minute; a digest or attestation mismatch fails immediately.
112
+ This updates managed files in the working tree and returns their changed paths. Review the diff and selectively retain repository customizations before committing. The task does not stage, commit, or change remote settings. Repeated updates produce no further changes unless customizations differ from the templates. Apply changed rulesets after the corresponding workflows are running.
113
+
114
+ Regenerate existing workflows to adopt publishing on `push` instead of `pull_request_target`. The workflow filename and `rubygems` environment remain the same, so the RubyGems Trusted Publisher configuration does not change. No exception to GitHub's `pull_request_target` execution policy is needed. The resolve task continues to accept PR numbers from older workflows while you migrate.
112
115
 
113
- Before uploading to RubyGems, the publisher stores the verified gem, receipt and both attestation bundles in a draft GitHub release targeting the merged commit. It publishes the draft after registry verification and tag creation. Actions artifacts are also retained for 90 days, but can disappear on rerun. Recovery falls back to the draft or published release and verifies the original bytes and attestations. Keep the draft until finalization succeeds. If asset preservation was interrupted and neither backup is complete, restore the missing original files before retrying; conflicting assets are never overwritten.
116
+ The release workflows follow `bake modernize` action versions and use moving major tags where available. The RubyGems credentials action uses its [documented `@main` reference](https://github.com/rubygems/configure-rubygems-credentials#trusted-publisher-recommended). Repositories that require fixed revisions can customize these references.
114
117
 
115
- GitHub concurrency does not guarantee a durable FIFO queue: rerun any publishing run displaced while pending. Resume reruns all jobs, including integrity checks; it does not repeat or second-guess the native review policy or a permitted administrator bypass. Older publishing runs execute their original code; adding this recovery support to the default branch does not change an already-triggered workflow.
118
+ ## Current scope
116
119
 
117
- ## Development and current limits
120
+ The process has published `bake-gem-github` through GitHub Actions. Each adopting repository still needs its own reviewed setup and successful release. Public single-gem repositories, ordinary stable versions, merge/squash, GitHub-hosted Linux runners, and RubyGems.org are the supported starting point.
118
121
 
119
- The implementation has local repository and transport-fake tests. A real GitHub/RubyGems pilot remains necessary before enabling it across Socketry. Public single-gem repositories, ordinary stable versions, merge/squash, GitHub-hosted Linux runners, and RubyGems.org are the supported starting point. Native build matrices, reusable publisher workflows, merge queues, automated RubyGems ownership/MFA setup, cross-run artifact recovery, and organization-wide rollout are deferred.
122
+ Native build matrices, reusable publisher workflows, merge queues, automated RubyGems ownership/MFA setup, cross-run artifact recovery, and organization-wide migration are outside the current setup tasks.
120
123
 
121
- Edit this guide and regenerate `context/` with `bake utopia:project:agent:context:update`. Consumers install the generated guidance using `agent-context`.
124
+ Edit source guides under `guides/` and regenerate the distributed guidance with `bundle exec bake utopia:project:agent:context:update`. Consumers install it through `agent-context`.
data/context/index.yaml CHANGED
@@ -2,9 +2,25 @@
2
2
  # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
3
  ---
4
4
  description: Reviewable GitHub releases for Ruby gems.
5
- metadata: {}
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/bake-gem-github/
7
+ bug_tracker_uri: https://github.com/socketry/bake-gem-github/issues
8
+ changelog_uri: https://github.com/socketry/bake-gem-github/blob/main/releases.md
9
+ source_code_uri: https://github.com/socketry/bake-gem-github.git
6
10
  files:
7
11
  - path: getting-started.md
8
- title: GitHub Releases
9
- description: This guide explains how to set up reviewed Ruby gem releases with `bake-gem-github`,
10
- native GitHub rules, RubyGems Trusted Publishing, and retained release artifacts.
12
+ title: Getting Started
13
+ description: This guide explains how to configure reviewed Ruby gem releases and
14
+ prepare the first release PR with `bake-gem-github`.
15
+ - path: preparing-releases.md
16
+ title: Preparing Releases
17
+ description: This guide explains how to request a release PR, resume interrupted
18
+ preparation, and refresh generated content when the default branch changes.
19
+ - path: verifying-releases.md
20
+ title: Verifying Releases
21
+ description: This guide explains how publishing binds a gem to its reviewed source
22
+ and how to verify the downloaded artifact and attestations.
23
+ - path: recovering-releases.md
24
+ title: Recovering Releases
25
+ description: This guide explains how to resume an interrupted publishing workflow
26
+ using the original gem and its verification evidence.
@@ -0,0 +1,48 @@
1
+ # Preparing Releases
2
+
3
+ This guide explains how to request a release PR, resume interrupted preparation, and refresh generated content when the default branch changes.
4
+
5
+ Complete [Getting Started](../getting-started/index) first. Release preparation uses {ruby Bake::Gem::GitHub::Project#prepare} to coordinate the core Bake tasks and GitHub operations. Run local tasks from the repository root so gemspec paths resolve correctly.
6
+
7
+ ## Request a release
8
+
9
+ ``` bash
10
+ # Local branch and commit only:
11
+ bundle exec bake gem:release:branch:patch
12
+
13
+ # From the current default branch: prepare, validate, push and open PR:
14
+ bundle exec bake gem:github:release:patch
15
+
16
+ # Remote request (also available in the Actions UI):
17
+ gh workflow run release-prepare.yaml -f bump=patch
18
+ ```
19
+
20
+ Replace `patch` with `minor` or `major`. The wrapper fetches the default branch and tags, refuses a stale local checkout, and validates an existing release PR before returning its URL. A matching local or remote branch is reused if PR creation was interrupted. Multiple open release PRs or a different requested bump stop preparation. GitHub's built-in token may require a writer to approve running workflows for its created PR; enable Actions' permission to create PRs. An organization-owned App token can be adopted later if automatic CI triggering is needed.
21
+
22
+ All release changes belong in the PR. Core preparation commits additions and deletions from release hooks but never pushes, tags or publishes. Validation independently generates the expected tree from the current base. A changed base SHA alone is fine; changed generated notes are not. Ordinary PRs with no version change pass release validation and still build unsigned.
23
+
24
+ ## Publish the merged release
25
+
26
+ Merging into the configured default branch triggers `release-publish.yaml` through its `push` event. Inspection uses the exact pushed SHA and resolves its associated PR through GitHub. Publication requires one matching merged PR in this repository, targeting the configured branch, with that exact merge commit. Ordinary changes do not publish; release changes without a matching merged PR fail inspection. Both merge commits and squash merges are supported, including merged fork PRs.
27
+
28
+ Each release must land as the tip of its own push, as it does when merging a PR through GitHub. Later pushes do not change a pending release's source: inspection and publishing remain pinned to the original commit, and reruns use the same event. Do not combine a release and later changes into one direct push. If you automate merging, use a GitHub App or personal access token; pushes made using a workflow's `GITHUB_TOKEN` do not trigger another workflow.
29
+
30
+ Only validated releases enter the publishing queue and request approval from the `rubygems` environment when required. Ordinary pushes cannot replace them in that queue. The publishing job retains the existing signing, attestation, and artifact recovery checks.
31
+
32
+ ## Resume interrupted preparation
33
+
34
+ If preparation stops after creating or pushing the release branch, return to the current default branch and repeat the same command. The existing branch is validated and reused, so retries do not create a second version bump or PR. Resolve any uncommitted changes before switching branches.
35
+
36
+ ## Refresh stale content
37
+
38
+ When new changes alter the generated release notes or other artifacts, rebasing the branch alone does not regenerate them. Validation reports the stale content. Explicitly refresh the same release:
39
+
40
+ ``` bash
41
+ git switch main
42
+ git pull --ff-only
43
+ bundle exec bake gem:github:release:patch refresh=true
44
+ # Or dispatch remotely:
45
+ gh workflow run release-prepare.yaml -f bump=patch -f refresh=true
46
+ ```
47
+
48
+ Refresh first pushes the complete previous release commit to `release-backups/vVERSION/OLD_SHA`, including manual edits. It then regenerates in a clean worktree from the current default branch, validates, and updates the existing release branch using an explicit `--force-with-lease`. A concurrent remote edit causes the push to fail. Existing local release branches are left intact. Review the backup against the refreshed PR; incorporate necessary manual changes into the default branch or generation hooks and refresh again. Keep the backup until that review is complete. Replace `main` and `patch` with your configured branch and original bump type.
@@ -0,0 +1,29 @@
1
+ # Recovering Releases
2
+
3
+ This guide explains how to resume an interrupted publishing workflow using the original gem and its verification evidence.
4
+
5
+ Use this when a workflow fails during preservation, upload, registry propagation, or tag/release finalization. For failures while creating the PR, see [Preparing Releases](../preparing-releases/index). Publishing recovery retains the original source and artifact bytes.
6
+
7
+ ## Rerun the original workflow
8
+
9
+ Use **Re-run all jobs** on the original publishing run, or:
10
+
11
+ ``` bash
12
+ bundle exec bake gem:github:release:resume run=RUN_ID
13
+ ```
14
+
15
+ Rerunning keeps the original event identity. GitHub may request publishing environment approval again. A retained artifact is downloaded and its source identity/digest checked. A matching registry version resumes tag/release finalization; different bytes or a conflicting tag stop. There is no automatic yank, retag, or rebuild of an already-published version. Registry propagation is retried every ten seconds for up to one minute; a digest or attestation mismatch fails immediately.
16
+
17
+ ## Restore retained artifacts
18
+
19
+ Before uploading to RubyGems, the publisher stores the verified gem, receipt and both attestation bundles together in `release.tar`, uploaded as one draft-release asset before their individual assets. The draft targets the merged commit. It publishes the draft after registry verification and tag creation. Actions artifacts are also retained for 90 days, but can disappear on rerun. Recovery falls back to `release.tar` in the draft or published release, checking its digest and requiring exactly the four expected regular files before restoring them. It verifies the original bytes and attestations, then resumes any missing individual asset uploads. Older releases without an archive can still restore their four individual assets. Existing assets and backups are compared with the original files and never replaced with conflicting content.
20
+
21
+ ## Handle incomplete preservation
22
+
23
+ A rerun can recover an interrupted individual asset upload once `release.tar` is available, even if the Actions artifact has disappeared. An available Actions artifact can also resume an interrupted archive upload. If neither backup completed, restore the missing original files manually; the publisher stops before uploading to RubyGems. Keep the draft until finalization succeeds. A published version is never rebuilt to fill a missing backup.
24
+
25
+ ## Understand workflow reruns
26
+
27
+ Only publishing jobs share a concurrency group. They use `queue: max`, allowing up to 100 pending publishing jobs without replacing earlier ones; ordinary pushes only run inspection and do not enter this queue. GitHub cancels additional jobs if that limit is reached. Rerun a canceled publishing workflow after capacity becomes available.
28
+
29
+ Resume reruns all jobs against the original pushed commit, including integrity checks; it does not repeat or second-guess the native review policy or a permitted administrator bypass. Older publishing runs execute their original code and concurrency policy; updating the default branch does not change an already-triggered workflow.
@@ -0,0 +1,43 @@
1
+ # Verifying Releases
2
+
3
+ This guide explains how publishing binds a gem to its reviewed source and how to verify the downloaded artifact and attestations.
4
+
5
+ Use this when checking a completed release or confirming which source commit produced a package. [Getting Started](../getting-started/index) describes publisher configuration; [Recovering Releases](../recovering-releases/index) covers interrupted workflows.
6
+
7
+ ## What publishing verifies
8
+
9
+ After merge/squash, the publishing workflow verifies GitHub's merged PR record and ancestry, then checks out the exact merged commit. Later development on the default branch is allowed. It regenerates against the merged commit's **first parent**, builds in a clean worktree, optionally certificate-signs, and creates two attestations over the final bytes:
10
+
11
+ - A Sigstore bundle submitted explicitly with `gem push --attestation` using RubyGems 4.0.21.
12
+ - GitHub's native SLSA provenance covering both the gem and `release.json`. This signed receipt binds the gem digest to the exact release commit, even when the workflow's own default-branch revision is newer.
13
+
14
+ The workflow retains the gem, receipt and attestations before obtaining RubyGems publishing credentials. It verifies both attestations, checks the uploaded bytes and registry bundle, then pushes the specific version tag and creates the GitHub release. Existing tags/assets are checked and never overwritten. The old `after_gem_release` GitHub hook is not called by this pipeline, so there is one owner for release creation.
15
+
16
+ The draft release description includes the exact version's notes from `releases.md` in the merged release checkout, followed by the PR URL, source commit, and gem digest. Notes are extracted using `bake-releases`; a missing or empty section leaves the metadata as the description. Retries preserve the existing release description.
17
+
18
+ ## Verify downloaded artifacts
19
+
20
+ Download the gem, its `.sigstore.json` bundle, `release.json`, and `provenance.sigstore.json` from the GitHub release. Replace the example package, owner/repository, and `MERGED_SHA` below with the release being checked. Run from the directory containing those files:
21
+
22
+ ``` bash
23
+ set -e
24
+ for file in example-1.2.3.gem release.json; do
25
+ gh attestation verify "$file" \
26
+ --repo OWNER/REPOSITORY --bundle provenance.sigstore.json \
27
+ --cert-identity https://github.com/OWNER/REPOSITORY/.github/workflows/release-publish.yaml@refs/heads/main \
28
+ --source-ref refs/heads/main --deny-self-hosted-runners
29
+ done
30
+
31
+ jq -e --arg commit MERGED_SHA \
32
+ --arg digest "$(shasum -a 256 example-1.2.3.gem | cut -d ' ' -f1)" \
33
+ '.commit == $commit and .sha256 == $digest' release.json
34
+
35
+ gem exec sigstore-cli:0.2.3 verify example-1.2.3.gem \
36
+ --bundle example-1.2.3.gem.sigstore.json \
37
+ --certificate-identity https://github.com/OWNER/REPOSITORY/.github/workflows/release-publish.yaml@refs/heads/main \
38
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com
39
+ ```
40
+
41
+ Download `release.json` and `provenance.sigstore.json` alongside the gem. Verify both subjects before reading the receipt's source commit and digest. GitHub CLI's `--source-digest` checks the workflow revision, which may differ from the release commit recorded in the signed receipt. Replace `main` with the configured default branch in these commands.
42
+
43
+ Native GitHub records the merge and any bypass; the signed artifact receipt includes the PR and merging actor. This version does not export organization audit-log evidence or infer bypass reasons from review counts.