From 02a1c995ee19f2bf8601667fc1c8f97659f60578 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 24 Oct 2026 21:21:52 +1300 Subject: [PATCH] Adopt reviewed GitHub releases. --- .github/release-rules/checks.json | 61 ++++++++++++++ .github/release-rules/history.json | 22 +++++ .github/release-rules/reviews.json | 37 +++++++++ .github/release-rules/tags.json | 22 +++++ .github/workflows/release-prepare.yaml | 52 ++++++++++++ .github/workflows/release-publish.yaml | 102 ++++++++++++++++++++++++ .github/workflows/release-validate.yaml | 32 ++++++++ bake.rb | 10 +-- config/release.yaml | 18 +++++ gems.rb | 5 +- protocol-websocket.gemspec | 3 - readme.md | 14 ++-- release.cert | 28 ------- releases.md | 4 + 14 files changed, 362 insertions(+), 48 deletions(-) create mode 100644 .github/release-rules/checks.json create mode 100644 .github/release-rules/history.json create mode 100644 .github/release-rules/reviews.json create mode 100644 .github/release-rules/tags.json create mode 100644 .github/workflows/release-prepare.yaml create mode 100644 .github/workflows/release-publish.yaml create mode 100644 .github/workflows/release-validate.yaml create mode 100644 config/release.yaml delete mode 100644 release.cert diff --git a/.github/release-rules/checks.json b/.github/release-rules/checks.json new file mode 100644 index 0000000..cd4f2fe --- /dev/null +++ b/.github/release-rules/checks.json @@ -0,0 +1,61 @@ +{ + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main" + ], + "exclude": [] + } + }, + "name": "Gem release checks", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "pull_request" + } + ], + "rules": [ + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": true, + "do_not_enforce_on_create": false, + "required_status_checks": [ + { + "context": "3.3 on ubuntu" + }, + { + "context": "3.3 on macos" + }, + { + "context": "3.4 on ubuntu" + }, + { + "context": "3.4 on macos" + }, + { + "context": "4.0 on ubuntu" + }, + { + "context": "4.0 on macos" + }, + { + "context": "check" + }, + { + "context": "ruby on ubuntu" + }, + { + "context": "ruby on macos" + }, + { + "context": "Release validation" + } + ] + } + } + ] +} diff --git a/.github/release-rules/history.json b/.github/release-rules/history.json new file mode 100644 index 0000000..e6f4af1 --- /dev/null +++ b/.github/release-rules/history.json @@ -0,0 +1,22 @@ +{ + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main" + ], + "exclude": [] + } + }, + "name": "Gem release history", + "bypass_actors": [], + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + } + ] +} diff --git a/.github/release-rules/reviews.json b/.github/release-rules/reviews.json new file mode 100644 index 0000000..b36fd66 --- /dev/null +++ b/.github/release-rules/reviews.json @@ -0,0 +1,37 @@ +{ + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main" + ], + "exclude": [] + } + }, + "name": "Gem release reviews", + "bypass_actors": [ + { + "actor_id": 5, + "actor_type": "RepositoryRole", + "bypass_mode": "pull_request" + } + ], + "rules": [ + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 2, + "dismiss_stale_reviews_on_push": true, + "require_last_push_approval": true, + "required_review_thread_resolution": true, + "require_code_owner_review": false, + "allowed_merge_methods": [ + "merge", + "squash", + "rebase" + ] + } + } + ] +} diff --git a/.github/release-rules/tags.json b/.github/release-rules/tags.json new file mode 100644 index 0000000..8f7a46f --- /dev/null +++ b/.github/release-rules/tags.json @@ -0,0 +1,22 @@ +{ + "name": "Gem release tags", + "target": "tag", + "enforcement": "active", + "bypass_actors": [], + "conditions": { + "ref_name": { + "include": [ + "refs/tags/v*" + ], + "exclude": [] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + } + ] +} diff --git a/.github/workflows/release-prepare.yaml b/.github/workflows/release-prepare.yaml new file mode 100644 index 0000000..1dc024f --- /dev/null +++ b/.github/workflows/release-prepare.yaml @@ -0,0 +1,52 @@ +name: Prepare release + +on: + workflow_dispatch: + inputs: + bump: + description: Version increment + required: true + type: choice + options: [patch, minor, major] + refresh: + description: Preserve and regenerate an existing release branch + type: boolean + default: false + +permissions: + contents: write + pull-requests: write + +concurrency: + group: release-prepare + cancel-in-progress: false + +env: + BUNDLE_WITH: maintenance + +jobs: + prepare: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + ref: main + fetch-depth: 0 + persist-credentials: false + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + - name: Create release PR + env: + GITHUB_TOKEN: ${{ github.token }} + BUMP: ${{ inputs.bump }} + REFRESH: ${{ inputs.refresh }} + run: | + # GitHub.com's shared Actions bot ID; this identity is not for GitHub Enterprise Server. + # https://github.com/actions/checkout#push-a-commit-using-the-built-in-token + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + case "$BUMP" in patch|minor|major) ;; *) exit 1 ;; esac + bundle exec bake "gem:github:release:$BUMP" "refresh=$REFRESH" diff --git a/.github/workflows/release-publish.yaml b/.github/workflows/release-publish.yaml new file mode 100644 index 0000000..d8a3a86 --- /dev/null +++ b/.github/workflows/release-publish.yaml @@ -0,0 +1,102 @@ +name: Publish release + +# Inspect the exact pushed commit and publish only a validated, merged release PR. +on: + push: + branches: ["main"] + +permissions: + contents: read + pull-requests: read + +env: + BUNDLE_WITH: maintenance + +jobs: + inspect: + runs-on: ubuntu-latest + outputs: + release: ${{ steps.inspect.outputs.release }} + commit: ${{ steps.inspect.outputs.commit }} + pull_request: ${{ steps.inspect.outputs.pull_request }} + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + - id: inspect + env: + GITHUB_TOKEN: ${{ github.token }} + RELEASE_COMMIT: ${{ github.sha }} + run: bundle exec bake gem:github:release:resolve + + publish: + needs: inspect + if: needs.inspect.outputs.release == 'true' + runs-on: ubuntu-latest + environment: rubygems + concurrency: + group: release-publish + cancel-in-progress: false + queue: max + env: + RELEASE_PR: ${{ needs.inspect.outputs.pull_request }} + permissions: + contents: write + pull-requests: read + actions: read + id-token: write + attestations: write + steps: + - uses: actions/checkout@v7 + with: + # Inspection verified this merged commit belongs to the default branch. + ref: ${{ needs.inspect.outputs.commit }} + fetch-depth: 0 + persist-credentials: false + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + rubygems: '4.0.21' + bundler-cache: true + - name: Build or restore artifact + id: build + env: + GITHUB_TOKEN: ${{ github.token }} + run: bundle exec bake gem:github:release:build + - name: Sign RubyGems attestation + if: steps.build.outputs.restored != 'true' + env: + PACKAGE: ${{ steps.build.outputs.package }} + run: gem exec sigstore-cli:0.2.3 sign "$PACKAGE" --bundle "$PACKAGE.sigstore.json" + - name: Attest gem and release receipt + if: steps.build.outputs.restored != 'true' + id: attest + uses: actions/attest@v4 + with: + subject-path: | + ${{ steps.build.outputs.package }} + pkg/release.json + - name: Retain provenance bundle + if: steps.build.outputs.restored != 'true' + env: + ATTESTATION_BUNDLE: ${{ steps.attest.outputs.bundle-path }} + run: cp "$ATTESTATION_BUNDLE" pkg/provenance.sigstore.json + - name: Preserve release before upload + if: steps.build.outputs.restored != 'true' + uses: actions/upload-artifact@v7 + with: + name: ${{ steps.build.outputs.artifact }} + path: pkg/ + if-no-files-found: error + retention-days: 90 + - uses: rubygems/configure-rubygems-credentials@main + - name: Verify, publish, and finalize + env: + GITHUB_TOKEN: ${{ github.token }} + run: bundle exec bake gem:github:release:publish diff --git a/.github/workflows/release-validate.yaml b/.github/workflows/release-validate.yaml new file mode 100644 index 0000000..5d6c9f3 --- /dev/null +++ b/.github/workflows/release-validate.yaml @@ -0,0 +1,32 @@ +name: Validate release + +on: + pull_request: + branches: ["main"] + +permissions: + contents: read + +env: + BUNDLE_WITH: maintenance + +jobs: + validate: + name: Release validation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.4" + bundler-cache: true + - name: Regenerate release content + env: + RELEASE_BASE: ${{ github.event.pull_request.base.sha }} + run: bundle exec bake gem:github:release:validate "base=$RELEASE_BASE" + - name: Build unsigned package + run: bundle exec bake gem:build signing_key=false diff --git a/bake.rb b/bake.rb index 9441211..abdb0ea 100644 --- a/bake.rb +++ b/bake.rb @@ -3,17 +3,11 @@ # Released under the MIT License. # Copyright, 2025-2026, by Samuel Williams. -# Update the project documentation with the new version number. +# Update copyrights and project documentation for the new version. # # @parameter version [String] The new version number. def after_gem_release_version_increment(version) + context["modernize:license"].call context["releases:update"].call(version) context["utopia:project:update"].call end - -# Create a GitHub release for the given tag. -# -# @parameter tag [String] The tag to create a release for. -def after_gem_release(tag:, **options) - context["releases:github:release"].call(tag) -end diff --git a/config/release.yaml b/config/release.yaml new file mode 100644 index 0000000..2adc5e0 --- /dev/null +++ b/config/release.yaml @@ -0,0 +1,18 @@ +--- +schema: 1 +repository: socketry/protocol-websocket +branch: main +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 +- Release validation +approvals: 2 +ruby: '3.4' +environment: rubygems diff --git a/gems.rb b/gems.rb index e48ac1e..f6ce7ec 100644 --- a/gems.rb +++ b/gems.rb @@ -10,9 +10,8 @@ gemspec group :maintenance, optional: true do - gem "bake-gem" - gem "bake-modernize" - gem "bake-releases" + gem "bake-gem-github", "~> 0.7.0" + gem "bake-modernize", "~> 0.66" gem "agent-context" diff --git a/protocol-websocket.gemspec b/protocol-websocket.gemspec index a6d5bf4..18e8008 100644 --- a/protocol-websocket.gemspec +++ b/protocol-websocket.gemspec @@ -10,9 +10,6 @@ Gem::Specification.new do |spec| spec.authors = ["Samuel Williams", "William T. Nelson", "Aurora Nockert", "Soumya", "Olle Jonsson", "Taleh Zaliyev"] spec.license = "MIT" - spec.cert_chain = ["release.cert"] - spec.signing_key = File.expand_path("~/.gem/release.pem") - spec.homepage = "https://github.com/socketry/protocol-websocket" spec.metadata = { diff --git a/readme.md b/readme.md index 33288b2..d1757d2 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,10 @@ Please see the [project documentation](https://socketry.github.io/protocol-webso Please see the [project releases](https://socketry.github.io/protocol-websocket/releases/index) for all releases. +### Unreleased + + - Adopt reviewed GitHub release pull requests, RubyGems Trusted Publishing, and release attestations instead of legacy certificate signing. + ### v0.21.2 - Fix JSON 3 compatibility by applying `symbolize_names` only when parsing incoming messages. `Coder::JSON` is now a module with fixed parsing and generation behavior; pass a different coder to customize serialization. @@ -53,10 +57,6 @@ Please see the [project releases](https://socketry.github.io/protocol-websocket/ - Add `PingMessage` alongside `TextMessage` and `BinaryMessage` for a consistent message interface. - Remove `JSONMessage` (use application-level encoding instead). -### v0.17.0 - - - Introduce `#close_write` and `#shutdown` methods on `Connection` for more precise connection lifecycle control. - ## Contributing We welcome contributions to this project. @@ -77,12 +77,14 @@ $ bundle exec sus ### Making Releases -To make a new release: +To prepare a release pull request: ``` bash -$ bundle exec bake gem:release:patch # or minor or major +bundle exec bake gem:github:release:patch # or minor or major ``` +See [bake-gem-github](https://github.com/socketry/bake-gem-github) for setup and publishing details. + ### Developer Certificate of Origin In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed. diff --git a/release.cert b/release.cert deleted file mode 100644 index d98e595..0000000 --- a/release.cert +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11 -ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK -CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz -MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd -MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj -bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB -igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2 -9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW -sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE -e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN -XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss -RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn -tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM -zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW -xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O -BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs -aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs -aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE -cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl -xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/ -c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp -8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws -JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP -eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt -Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8 -voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg= ------END CERTIFICATE----- diff --git a/releases.md b/releases.md index 7521a49..d381ca1 100644 --- a/releases.md +++ b/releases.md @@ -1,5 +1,9 @@ # Releases +## Unreleased + + - Adopt reviewed GitHub release pull requests, RubyGems Trusted Publishing, and release attestations instead of legacy certificate signing. + ## v0.21.2 - Fix JSON 3 compatibility by applying `symbolize_names` only when parsing incoming messages. `Coder::JSON` is now a module with fixed parsing and generation behavior; pass a different coder to customize serialization.