From 233cad373ad2c058c36f9735e1eb0034dd1c6e26 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:00:31 +0000 Subject: [PATCH 01/24] Initial plan From a1e89e1d7530c80c94cc5a385785f5bb1c3f52a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:11:07 +0000 Subject: [PATCH 02/24] feat: add automated release pipeline with GitHub Actions Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/workflows/release.yml | 95 +++++++++++++++++++++++++++++++++++ .gitignore | 1 + CONTRIBUTING.md | 27 +++++++++- 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e71e7d9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,95 @@ +name: Release + +on: + push: + tags: + - 'v*' + +# see https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token +permissions: + contents: write + packages: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Determine if prerelease + id: prerelease + run: | + if [[ "${{ steps.version.outputs.VERSION }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "PRERELEASE=false" >> $GITHUB_OUTPUT + else + echo "PRERELEASE=true" >> $GITHUB_OUTPUT + fi + + - name: Run tests + run: bundle exec rake test + + - name: Build gem + run: bundle exec rake build + + - name: Generate checksum + run: bundle exec rake build:checksum + + - name: List build artifacts + run: ls -lh pkg/ checksums/ + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: gem-package + path: | + pkg/*.gem + checksums/*.sha512 + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + files: | + pkg/*.gem + checksums/*.sha512 + prerelease: ${{ steps.prerelease.outputs.PRERELEASE }} + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish: + needs: build + runs-on: ubuntu-latest + if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + + - name: Build gem + run: bundle exec rake build + + - name: Publish to RubyGems + run: | + mkdir -p ~/.gem + cat << EOF > ~/.gem/credentials + --- + :rubygems_api_key: ${RUBYGEMS_API_KEY} + EOF + chmod 0600 ~/.gem/credentials + bundle exec gem push pkg/*.gem + env: + RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} diff --git a/.gitignore b/.gitignore index 9aec75a..b3b2e38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Build Artifacts /pkg/ /tmp/ +/checksums/ *.gem # Bundler diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7157a53..c81197f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,9 +88,32 @@ Made with [contributors-img][🖐contrib-rocks]. ### To release a new version: -#### Automated process +#### Automated process (Recommended) -Coming Soon! +The repository has a GitHub Actions workflow that automates the release process: + +1. Run `bin/setup && bin/rake` as a "test, coverage, & linting" sanity check +2. Update the version number in `version.rb`, and ensure `CHANGELOG.md` reflects changes +3. Run `bin/setup && bin/rake` again as a secondary check, and to update `Gemfile.lock` +4. Run `git commit -am "🔖 Prepare release v"` to commit the changes +5. Run `git push` to trigger the final CI pipeline before release, and merge PRs + - NOTE: Remember to [check the build][🧪build]. +6. Run `export GIT_TRUNK_BRANCH_NAME="$(git remote show origin | grep 'HEAD branch' | cut -d ' ' -f5)" && echo $GIT_TRUNK_BRANCH_NAME` +7. Run `git checkout $GIT_TRUNK_BRANCH_NAME` +8. Run `git pull origin $GIT_TRUNK_BRANCH_NAME` to ensure latest trunk code +9. Create and push a git tag for the version: + - For stable releases: `git tag v` (e.g., `v1.2.0`) + - For prereleases: `git tag v-` (e.g., `v1.3.0-alpha.1`, `v1.3.0-beta.2`, `v1.3.0-rc.1`) +10. Run `git push origin v` to push the tag and trigger the release workflow + +The workflow will: +- Run all tests to ensure code quality +- Build the gem package +- Generate SHA-512 checksums +- Create a GitHub Release with the gem and checksums as artifacts +- Publish the gem to RubyGems.org (requires `RUBYGEMS_API_KEY` secret to be configured) + +**Note:** Prereleases are automatically detected based on the version tag format. Any tag that includes a prerelease identifier (alpha, beta, rc, etc.) will be marked as a prerelease on GitHub. #### Manual process From 4ea43c4c6dca7647bbbec2cfe9a5e34325335c24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:12:41 +0000 Subject: [PATCH 03/24] feat: add version verification and secret validation to release workflow Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/RELEASE.md | 92 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 18 +++++++ 2 files changed, 110 insertions(+) create mode 100644 .github/RELEASE.md diff --git a/.github/RELEASE.md b/.github/RELEASE.md new file mode 100644 index 0000000..ccd63e3 --- /dev/null +++ b/.github/RELEASE.md @@ -0,0 +1,92 @@ +# Release Process + +This repository uses an automated release pipeline via GitHub Actions. + +## How It Works + +The release workflow (`.github/workflows/release.yml`) is triggered when a version tag is pushed to the repository. The workflow: + +1. **Runs tests** - Ensures all tests pass before releasing +2. **Builds the gem** - Creates the `.gem` package using `bundle exec rake build` +3. **Generates checksums** - Creates SHA-512 checksums for the gem package +4. **Creates GitHub Release** - Publishes a release on GitHub with the gem and checksums as artifacts +5. **Publishes to RubyGems** - Automatically pushes the gem to [rubygems.org](https://rubygems.org) + +## Triggering a Release + +### For Stable Releases + +1. Update the version in `lib/cyclonedx/ruby/version.rb` +2. Update `CHANGELOG.md` with the changes +3. Commit the changes: `git commit -am "🔖 Prepare release v1.2.0"` +4. Create and push a tag: `git tag v1.2.0 && git push origin v1.2.0` + +### For Prereleases + +Prereleases follow the same process but use a tag with a prerelease identifier: + +- Alpha: `git tag v1.3.0-alpha.1 && git push origin v1.3.0-alpha.1` +- Beta: `git tag v1.3.0-beta.1 && git push origin v1.3.0-beta.1` +- Release Candidate: `git tag v1.3.0-rc.1 && git push origin v1.3.0-rc.1` + +Prereleases are automatically detected by the workflow and marked as "prerelease" on GitHub. + +## Version Tag Format + +- **Stable releases**: `v..` (e.g., `v1.2.0`) +- **Prereleases**: `v..-` (e.g., `v1.3.0-alpha.1`) + +The version in the tag must match the version in `lib/cyclonedx/ruby/version.rb`. + +## Required Secrets + +The workflow requires the following GitHub repository secret to be configured: + +### `RUBYGEMS_API_KEY` + +This is your RubyGems API key for publishing gems. To set it up: + +1. Generate an API key on [rubygems.org](https://rubygems.org/profile/edit): + - Go to your profile → Edit Profile → API Keys + - Create a new API key with "Push rubygem" scope + +2. Add it to GitHub repository secrets: + - Go to repository Settings → Secrets and variables → Actions + - Click "New repository secret" + - Name: `RUBYGEMS_API_KEY` + - Value: Your RubyGems API key + - Click "Add secret" + +**Note**: The `publish` job only runs on the official repository (`CycloneDX/cyclonedx-ruby-gem`) to prevent accidental publishes from forks. + +## Release Artifacts + +Each release includes the following artifacts: + +1. **Gem Package** (`cyclonedx-ruby-.gem`) - The built Ruby gem +2. **SHA-512 Checksum** (`cyclonedx-ruby-.gem.sha512`) - Checksum for verification + +These artifacts are attached to the GitHub Release and can be downloaded for verification. + +## Monitoring Releases + +- **GitHub Actions**: Check the [Actions tab](https://github.com/CycloneDX/cyclonedx-ruby-gem/actions) for workflow runs +- **GitHub Releases**: View all releases in the [Releases section](https://github.com/CycloneDX/cyclonedx-ruby-gem/releases) +- **RubyGems**: Check [rubygems.org/gems/cyclonedx-ruby](https://rubygems.org/gems/cyclonedx-ruby) for published versions + +## Troubleshooting + +### Release workflow fails on tests + +The workflow will not create a release if tests fail. Fix the failing tests and push a new commit, then create the tag again. + +### Gem fails to publish to RubyGems + +Check that: +- The `RUBYGEMS_API_KEY` secret is set correctly +- The API key has the "Push rubygem" permission +- The gem version doesn't already exist on RubyGems (versions cannot be overwritten) + +### Prerelease not detected correctly + +Ensure your tag follows the format `v-` where `` contains a hyphen or dot (e.g., `alpha.1`, `beta-2`, `rc.1`). diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e71e7d9..ea6bb12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,17 @@ jobs: id: version run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + - name: Verify version matches code + run: | + TAG_VERSION="${{ steps.version.outputs.VERSION }}" + CODE_VERSION=$(ruby -r ./lib/cyclonedx/ruby/version.rb -e 'puts Cyclonedx::Ruby::VERSION') + echo "Tag version: $TAG_VERSION" + echo "Code version: $CODE_VERSION" + if [ "$TAG_VERSION" != "$CODE_VERSION" ]; then + echo "::error::Version mismatch! Tag is v$TAG_VERSION but code version is $CODE_VERSION" + exit 1 + fi + - name: Determine if prerelease id: prerelease run: | @@ -79,6 +90,13 @@ jobs: ruby-version: '3.3' bundler-cache: true + - name: Check for RubyGems API key + run: | + if [ -z "${{ secrets.RUBYGEMS_API_KEY }}" ]; then + echo "::error::RUBYGEMS_API_KEY secret is not set" + exit 1 + fi + - name: Build gem run: bundle exec rake build From 0c0c9356003876bc556303f468b51e7ac71dc689 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:16:54 +0000 Subject: [PATCH 04/24] fix: improve prerelease detection and version verification in workflow Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/RELEASE.md | 2 +- .github/workflows/release.yml | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/RELEASE.md b/.github/RELEASE.md index ccd63e3..a26a0ab 100644 --- a/.github/RELEASE.md +++ b/.github/RELEASE.md @@ -89,4 +89,4 @@ Check that: ### Prerelease not detected correctly -Ensure your tag follows the format `v-` where `` contains a hyphen or dot (e.g., `alpha.1`, `beta-2`, `rc.1`). +The workflow detects prereleases by checking if the version matches the exact pattern `MAJOR.MINOR.PATCH` (e.g., `1.2.3`). Any version that includes additional characters after the patch version (e.g., `1.2.3-alpha.1`, `1.2.3.rc1`, `1.2.3-beta`) is automatically marked as a prerelease. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea6bb12..7a3c6a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: - name: Verify version matches code run: | TAG_VERSION="${{ steps.version.outputs.VERSION }}" - CODE_VERSION=$(ruby -r ./lib/cyclonedx/ruby/version.rb -e 'puts Cyclonedx::Ruby::VERSION') + CODE_VERSION=$(bundle exec ruby -r ./lib/cyclonedx/ruby/version.rb -e 'puts Cyclonedx::Ruby::VERSION') echo "Tag version: $TAG_VERSION" echo "Code version: $CODE_VERSION" if [ "$TAG_VERSION" != "$CODE_VERSION" ]; then @@ -40,10 +40,16 @@ jobs: - name: Determine if prerelease id: prerelease run: | - if [[ "${{ steps.version.outputs.VERSION }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + VERSION="${{ steps.version.outputs.VERSION }}" + # A version is a prerelease if it contains anything after the patch version + # e.g., 1.2.3-alpha.1, 1.2.3.rc1, 1.2.3-beta are all prereleases + # but 1.2.3 is a stable release + if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "PRERELEASE=false" >> $GITHUB_OUTPUT + echo "Detected stable release: $VERSION" else echo "PRERELEASE=true" >> $GITHUB_OUTPUT + echo "Detected prerelease: $VERSION" fi - name: Run tests From eba52c8b695db5082dbe506566cf5e78d6fd64de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:46:19 +0000 Subject: [PATCH 05/24] refactor: move permissions to job level for least privilege Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/workflows/release.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a3c6a9..ede396b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,14 +5,12 @@ on: tags: - 'v*' -# see https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token -permissions: - contents: write - packages: write - jobs: build: runs-on: ubuntu-latest + # see https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token + permissions: + contents: write steps: - uses: actions/checkout@v4 From fd75af461c2e3dffeb3bc820754818e460ddb81f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:53:55 +0000 Subject: [PATCH 06/24] security: add workflow-level permissions lock and disable bundler cache Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/workflows/release.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ede396b..b849f4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,14 @@ name: Release +run-name: Release ${{ github.ref_name }} + on: push: tags: - 'v*' +permissions: {} + jobs: build: runs-on: ubuntu-latest @@ -18,7 +22,10 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' - bundler-cache: true + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 - name: Extract version from tag id: version @@ -92,7 +99,10 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' - bundler-cache: true + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 - name: Check for RubyGems API key run: | From 96a34a4f7c4f31ae163bd6940e1d71d1e777ab16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:04:34 +0000 Subject: [PATCH 07/24] refactor: split workflow into separate jobs for better modularity Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/workflows/release.yml | 71 +++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b849f4f..931d9aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,11 +10,11 @@ on: permissions: {} jobs: - build: + validate: runs-on: ubuntu-latest - # see https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token - permissions: - contents: write + outputs: + version: ${{ steps.version.outputs.VERSION }} + prerelease: ${{ steps.prerelease.outputs.PRERELEASE }} steps: - uses: actions/checkout@v4 @@ -56,9 +56,39 @@ jobs: echo "PRERELEASE=true" >> $GITHUB_OUTPUT echo "Detected prerelease: $VERSION" fi + + test: + needs: validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 - name: Run tests run: bundle exec rake test + + build: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 - name: Build gem run: bundle exec rake build @@ -76,20 +106,32 @@ jobs: path: | pkg/*.gem checksums/*.sha512 + + release-github: + needs: [validate, build] + runs-on: ubuntu-latest + # GitHub release creation requires contents:write to create releases + permissions: + contents: write + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: gem-package - name: Create GitHub Release uses: softprops/action-gh-release@v1 with: files: | - pkg/*.gem - checksums/*.sha512 - prerelease: ${{ steps.prerelease.outputs.PRERELEASE }} + *.gem + *.sha512 + prerelease: ${{ needs.validate.outputs.prerelease }} generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - publish: - needs: build + release-rubygems: + needs: [validate, build] runs-on: ubuntu-latest if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' steps: @@ -101,9 +143,6 @@ jobs: ruby-version: '3.3' bundler-cache: false - - name: Install dependencies - run: bundle install --jobs 4 --retry 3 - - name: Check for RubyGems API key run: | if [ -z "${{ secrets.RUBYGEMS_API_KEY }}" ]; then @@ -111,8 +150,10 @@ jobs: exit 1 fi - - name: Build gem - run: bundle exec rake build + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: gem-package - name: Publish to RubyGems run: | @@ -122,6 +163,6 @@ jobs: :rubygems_api_key: ${RUBYGEMS_API_KEY} EOF chmod 0600 ~/.gem/credentials - bundle exec gem push pkg/*.gem + gem push *.gem env: RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} From a1f64830e3f07e5fda33ebd761847bd48d6ef52f Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 4 Dec 2025 09:17:58 +0100 Subject: [PATCH 08/24] Update .github/workflows/release.yml Co-authored-by: Olle Jonsson Signed-off-by: Jan Kowalleck --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 931d9aa..8af6723 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,7 +79,7 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 From f851a5c538af113a5cd3bdac242f4dd898428a64 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 4 Dec 2025 09:18:09 +0100 Subject: [PATCH 09/24] Update .github/workflows/release.yml Co-authored-by: Olle Jonsson Signed-off-by: Jan Kowalleck --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8af6723..34a3695 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: version: ${{ steps.version.outputs.VERSION }} prerelease: ${{ steps.prerelease.outputs.PRERELEASE }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 From 5a952909983db373fb99e17cb754e1ba4683df6c Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 4 Dec 2025 09:18:28 +0100 Subject: [PATCH 10/24] Update .github/workflows/release.yml Co-authored-by: Olle Jonsson Signed-off-by: Jan Kowalleck --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34a3695..7eaf0f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,7 @@ jobs: needs: validate runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Ruby uses: ruby/setup-ruby@v1 From 5fc0fd392bd29719bf2cfd1d5e491098db3eed66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:21:00 +0000 Subject: [PATCH 11/24] refactor: replace 3rd party release action with GitHub CLI Co-authored-by: jkowalleck <2765863+jkowalleck@users.noreply.github.com> --- .github/workflows/release.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7eaf0f3..cd2aca5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -120,15 +120,18 @@ jobs: name: gem-package - name: Create GitHub Release - uses: softprops/action-gh-release@v1 - with: - files: | - *.gem - *.sha512 - prerelease: ${{ needs.validate.outputs.prerelease }} - generate_release_notes: true + run: | + PRERELEASE_FLAG="" + if [ "${{ needs.validate.outputs.prerelease }}" = "true" ]; then + PRERELEASE_FLAG="--prerelease" + fi + gh release create "${{ github.ref_name }}" \ + *.gem *.sha512 \ + --title "${{ github.ref_name }}" \ + --generate-notes \ + $PRERELEASE_FLAG env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} release-rubygems: needs: [validate, build] From 7b6238e2ea09de4e8adf874df562b0281d0243ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:32:11 +0000 Subject: [PATCH 12/24] Remove Gemfile.lock from repository tracking --- Gemfile.lock | 196 --------------------------------------------------- 1 file changed, 196 deletions(-) delete mode 100644 Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 1fe1cca..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,196 +0,0 @@ -PATH - remote: . - specs: - cyclonedx-ruby (1.2.0) - activesupport (~> 7.0) - json (~> 2.6) - nokogiri (~> 1.15) - ostruct (~> 0.5.5) - rest-client (~> 2.0) - -GEM - remote: https://rubygems.org/ - specs: - activesupport (7.2.3) - base64 - benchmark (>= 0.3) - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - logger (>= 1.4.2) - minitest (>= 5.1) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - aruba (2.3.2) - bundler (>= 1.17, < 3.0) - contracts (>= 0.16.0, < 0.18.0) - cucumber (>= 8.0, < 11.0) - rspec-expectations (>= 3.4, < 5.0) - thor (~> 1.0) - ast (2.4.3) - base64 (0.3.0) - benchmark (0.5.0) - bigdecimal (3.3.1) - builder (3.3.0) - concurrent-ruby (1.3.5) - connection_pool (2.5.4) - contracts (0.17.2) - cucumber (10.1.1) - base64 (~> 0.2) - builder (~> 3.2) - cucumber-ci-environment (> 9, < 11) - cucumber-core (> 15, < 17) - cucumber-cucumber-expressions (> 17, < 19) - cucumber-html-formatter (> 20.3, < 22) - diff-lcs (~> 1.5) - logger (~> 1.6) - mini_mime (~> 1.1) - multi_test (~> 1.1) - sys-uname (~> 1.3) - cucumber-ci-environment (10.0.1) - cucumber-core (15.3.0) - cucumber-gherkin (> 27, < 35) - cucumber-messages (> 26, < 30) - cucumber-tag-expressions (> 5, < 9) - cucumber-cucumber-expressions (18.0.1) - bigdecimal - cucumber-gherkin (34.0.0) - cucumber-messages (> 25, < 29) - cucumber-html-formatter (21.15.1) - cucumber-messages (> 19, < 28) - cucumber-messages (27.2.0) - cucumber-tag-expressions (8.0.0) - diff-lcs (1.6.2) - docile (1.4.1) - domain_name (0.6.20240107) - drb (2.2.3) - ffi (1.17.2) - ffi (1.17.2-aarch64-linux-gnu) - ffi (1.17.2-aarch64-linux-musl) - ffi (1.17.2-arm-linux-gnu) - ffi (1.17.2-arm-linux-musl) - ffi (1.17.2-arm64-darwin) - ffi (1.17.2-x86_64-darwin) - ffi (1.17.2-x86_64-linux-gnu) - ffi (1.17.2-x86_64-linux-musl) - http-accept (1.7.0) - http-cookie (1.1.0) - domain_name (~> 0.5) - i18n (1.14.7) - concurrent-ruby (~> 1.0) - json (2.15.2) - language_server-protocol (3.17.0.5) - lint_roller (1.1.0) - logger (1.7.0) - memoist3 (1.0.0) - mime-types (3.7.0) - logger - mime-types-data (~> 3.2025, >= 3.2025.0507) - mime-types-data (3.2025.0924) - mini_mime (1.1.5) - minitest (5.26.0) - multi_test (1.1.0) - netrc (0.11.0) - nokogiri (1.18.10-aarch64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-aarch64-linux-musl) - racc (~> 1.4) - nokogiri (1.18.10-arm-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-arm-linux-musl) - racc (~> 1.4) - nokogiri (1.18.10-arm64-darwin) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.10-x86_64-linux-musl) - racc (~> 1.4) - ostruct (0.5.5) - parallel (1.27.0) - parser (3.3.10.0) - ast (~> 2.4.1) - racc - prism (1.6.0) - racc (1.8.1) - rainbow (3.1.1) - rake (13.3.1) - regexp_parser (2.11.3) - rest-client (2.1.0) - http-accept (>= 1.7.0, < 2.0) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 4.0) - netrc (~> 0.8) - rspec (3.13.2) - rspec-core (~> 3.13.0) - rspec-expectations (~> 3.13.0) - rspec-mocks (~> 3.13.0) - rspec-core (3.13.6) - rspec-support (~> 3.13.0) - rspec-expectations (3.13.5) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-mocks (3.13.6) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.13.0) - rspec-support (3.13.6) - rubocop (1.81.6) - json (~> 2.3) - language_server-protocol (~> 3.17.0.2) - lint_roller (~> 1.1.0) - parallel (~> 1.10) - parser (>= 3.3.0.2) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.47.1, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.47.1) - parser (>= 3.3.7.2) - prism (~> 1.4) - ruby-progressbar (1.13.0) - securerandom (0.4.1) - simplecov (0.22.0) - docile (~> 1.1) - simplecov-html (~> 0.11) - simplecov_json_formatter (~> 0.1) - simplecov-html (0.13.2) - simplecov_json_formatter (0.1.4) - stone_checksums (1.0.3) - version_gem (~> 1.1, >= 1.1.9) - sys-uname (1.4.1) - ffi (~> 1.1) - memoist3 (~> 1.0.0) - thor (1.4.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - unicode-display_width (3.2.0) - unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) - version_gem (1.1.9) - -PLATFORMS - aarch64-linux-gnu - aarch64-linux-musl - arm-linux-gnu - arm-linux-musl - arm64-darwin - x86_64-darwin - x86_64-linux-gnu - x86_64-linux-musl - -DEPENDENCIES - aruba (~> 2.2) - cucumber (~> 10.0) - cyclonedx-ruby! - rake (~> 13) - rspec (~> 3.12) - rubocop (~> 1.54) - simplecov (~> 0.22.0) - stone_checksums (~> 1.0, >= 1.0.3) - -BUNDLED WITH - 2.7.2 From a1cc25f024614342fd1c74f022319ce7aafaa5e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:39:55 +0000 Subject: [PATCH 13/24] Enable RubyGems trusted publishing --- .github/RELEASE.md | 31 +++++++++++++------------------ .github/workflows/release.yml | 26 +++++--------------------- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/.github/RELEASE.md b/.github/RELEASE.md index a26a0ab..4d65807 100644 --- a/.github/RELEASE.md +++ b/.github/RELEASE.md @@ -38,26 +38,21 @@ Prereleases are automatically detected by the workflow and marked as "prerelease The version in the tag must match the version in `lib/cyclonedx/ruby/version.rb`. -## Required Secrets +## RubyGems Trusted Publishing Setup -The workflow requires the following GitHub repository secret to be configured: +The release workflow uses RubyGems trusted publishing via GitHub Actions OIDC. +No `RUBYGEMS_API_KEY` repository secret is required. -### `RUBYGEMS_API_KEY` +Before the first release, configure a trusted publisher for `cyclonedx-ruby` on [rubygems.org](https://rubygems.org): -This is your RubyGems API key for publishing gems. To set it up: +1. Open the gem's trusted publishing settings on RubyGems.org. +2. Add a GitHub Actions trusted publisher for this repository: + - Owner: `CycloneDX` + - Repository: `cyclonedx-ruby-gem` + - Workflow file: `.github/workflows/release.yml` +3. Save the publisher configuration on RubyGems.org. -1. Generate an API key on [rubygems.org](https://rubygems.org/profile/edit): - - Go to your profile → Edit Profile → API Keys - - Create a new API key with "Push rubygem" scope - -2. Add it to GitHub repository secrets: - - Go to repository Settings → Secrets and variables → Actions - - Click "New repository secret" - - Name: `RUBYGEMS_API_KEY` - - Value: Your RubyGems API key - - Click "Add secret" - -**Note**: The `publish` job only runs on the official repository (`CycloneDX/cyclonedx-ruby-gem`) to prevent accidental publishes from forks. +**Note**: The RubyGems publishing job only runs on the official repository (`CycloneDX/cyclonedx-ruby-gem`) to prevent accidental publishes from forks. ## Release Artifacts @@ -83,8 +78,8 @@ The workflow will not create a release if tests fail. Fix the failing tests and ### Gem fails to publish to RubyGems Check that: -- The `RUBYGEMS_API_KEY` secret is set correctly -- The API key has the "Push rubygem" permission +- Trusted publishing is configured for `CycloneDX/cyclonedx-ruby-gem` on RubyGems.org +- The workflow has permission to request an OIDC token - The gem version doesn't already exist on RubyGems (versions cannot be overwritten) ### Prerelease not detected correctly diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd2aca5..51e9996 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -137,35 +137,19 @@ jobs: needs: [validate, build] runs-on: ubuntu-latest if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' + permissions: + id-token: write steps: - - uses: actions/checkout@v4 - - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' bundler-cache: false - - - name: Check for RubyGems API key - run: | - if [ -z "${{ secrets.RUBYGEMS_API_KEY }}" ]; then - echo "::error::RUBYGEMS_API_KEY secret is not set" - exit 1 - fi - + - name: Download build artifacts uses: actions/download-artifact@v4 with: name: gem-package - + - name: Publish to RubyGems - run: | - mkdir -p ~/.gem - cat << EOF > ~/.gem/credentials - --- - :rubygems_api_key: ${RUBYGEMS_API_KEY} - EOF - chmod 0600 ~/.gem/credentials - gem push *.gem - env: - RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }} + run: gem push *.gem From 44f31e23fb4fc111de379374e4883484d010fba9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:40:32 +0000 Subject: [PATCH 14/24] Clarify trusted publishing documentation --- .github/RELEASE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/RELEASE.md b/.github/RELEASE.md index 4d65807..cd8ec88 100644 --- a/.github/RELEASE.md +++ b/.github/RELEASE.md @@ -43,7 +43,7 @@ The version in the tag must match the version in `lib/cyclonedx/ruby/version.rb` The release workflow uses RubyGems trusted publishing via GitHub Actions OIDC. No `RUBYGEMS_API_KEY` repository secret is required. -Before the first release, configure a trusted publisher for `cyclonedx-ruby` on [rubygems.org](https://rubygems.org): +Before the first release, configure a trusted publisher for the `cyclonedx-ruby` gem on [rubygems.org](https://rubygems.org) and point it at this repository (`CycloneDX/cyclonedx-ruby-gem`): 1. Open the gem's trusted publishing settings on RubyGems.org. 2. Add a GitHub Actions trusted publisher for this repository: From 2459eb0fefd893c75975d2e47cbb113489ef6cfb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:48:14 +0000 Subject: [PATCH 15/24] Update release workflow for trusted publishing and job dependency --- .github/RELEASE.md | 6 +++-- .github/workflows/release.yml | 47 +++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/.github/RELEASE.md b/.github/RELEASE.md index cd8ec88..1c28694 100644 --- a/.github/RELEASE.md +++ b/.github/RELEASE.md @@ -9,8 +9,8 @@ The release workflow (`.github/workflows/release.yml`) is triggered when a versi 1. **Runs tests** - Ensures all tests pass before releasing 2. **Builds the gem** - Creates the `.gem` package using `bundle exec rake build` 3. **Generates checksums** - Creates SHA-512 checksums for the gem package -4. **Creates GitHub Release** - Publishes a release on GitHub with the gem and checksums as artifacts -5. **Publishes to RubyGems** - Automatically pushes the gem to [rubygems.org](https://rubygems.org) +4. **Publishes to RubyGems** - Uses RubyGems trusted publishing (OIDC) and pushes the gem to [rubygems.org](https://rubygems.org) +5. **Creates GitHub Release** - Publishes a release on GitHub with the gem and checksums as artifacts, only after RubyGems publish succeeds ## Triggering a Release @@ -82,6 +82,8 @@ Check that: - The workflow has permission to request an OIDC token - The gem version doesn't already exist on RubyGems (versions cannot be overwritten) +If RubyGems publishing fails, the GitHub Release job will not run. + ### Prerelease not detected correctly The workflow detects prereleases by checking if the version matches the exact pattern `MAJOR.MINOR.PATCH` (e.g., `1.2.3`). Any version that includes additional characters after the patch version (e.g., `1.2.3-alpha.1`, `1.2.3.rc1`, `1.2.3-beta`) is automatically marked as a prerelease. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 51e9996..1ed8117 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,9 +107,33 @@ jobs: pkg/*.gem checksums/*.sha512 - release-github: + release-rubygems: needs: [validate, build] runs-on: ubuntu-latest + if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' + permissions: + id-token: write + steps: + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: gem-package + + - name: Configure RubyGems credentials + uses: rubygems/configure-rubygems-credentials@v1 + + - name: Publish to RubyGems + run: gem push *.gem + + release-github: + needs: [validate, build, release-rubygems] + runs-on: ubuntu-latest # GitHub release creation requires contents:write to create releases permissions: contents: write @@ -132,24 +156,3 @@ jobs: $PRERELEASE_FLAG env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - release-rubygems: - needs: [validate, build] - runs-on: ubuntu-latest - if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' - permissions: - id-token: write - steps: - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.3' - bundler-cache: false - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: gem-package - - - name: Publish to RubyGems - run: gem push *.gem From 187650fd119c91529028e025d4df19968b09d340 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:52:16 +0000 Subject: [PATCH 16/24] Update CONTRIBUTING with automated release process --- CONTRIBUTING.md | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7157a53..a2e75a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,36 +88,18 @@ Made with [contributors-img][🖐contrib-rocks]. ### To release a new version: -#### Automated process - -Coming Soon! - -#### Manual process - -1. Run `bin/setup && bin/rake` as a "test, coverage, & linting" sanity check -2. Update the version number in `version.rb`, and ensure `CHANGELOG.md` reflects changes -3. Run `bin/setup && bin/rake` again as a secondary check, and to update `Gemfile.lock` -4. Run `git commit -am "🔖 Prepare release v"` to commit the changes -5. Run `git push` to trigger the final CI pipeline before release, and merge PRs - - NOTE: Remember to [check the build][🧪build]. -6. Run `export GIT_TRUNK_BRANCH_NAME="$(git remote show origin | grep 'HEAD branch' | cut -d ' ' -f5)" && echo $GIT_TRUNK_BRANCH_NAME` -7. Run `git checkout $GIT_TRUNK_BRANCH_NAME` -8. Run `git pull origin $GIT_TRUNK_BRANCH_NAME` to ensure latest trunk code -9. Optional for older Bundler (< 2.7.0): Set `SOURCE_DATE_EPOCH` so `rake build` and `rake release` use the same timestamp and generate the same checksums - - If your Bundler is >= 2.7.0, you can skip this; builds are reproducible by default. - - Run `export SOURCE_DATE_EPOCH=$EPOCHSECONDS && echo $SOURCE_DATE_EPOCH` - - If the echo above has no output, then it didn't work. - - Note: `zsh/datetime` module is needed, if running `zsh`. - - In older versions of `bash` you can use `date +%s` instead, i.e. `export SOURCE_DATE_EPOCH=$(date +%s) && echo $SOURCE_DATE_EPOCH` -10. Run `bundle exec rake build` -11. Run `bundle exec rake release` which will create a git tag for the version, - push git commits and tags, and push the `.gem` file to the gem host configured in the gemspec. -12. Run `bin/gem_checksums` (more context [1][🔒️rubygems-checksums-pr], [2][🔒️rubygems-guides-pr]) - to create SHA-256 and SHA-512 checksums. This functionality is provided by the `stone_checksums` - [gem][💎stone_checksums]. - - The script automatically commits but does not push the checksums -13. Sanity check the SHA256, comparing with the output from the `bin/gem_checksums` command: - - `sha256sum pkg/-.gem` +#### Automated release process + +Releases are automated via `.github/workflows/release.yml` and are triggered by pushing a version tag (`v*`). + +1. Update the version in `lib/cyclonedx/ruby/version.rb` and update `CHANGELOG.md`. +2. Run `bin/setup && bin/rake` locally as a sanity check. +3. Commit and merge the release preparation changes. +4. Create and push the release tag (for example, `v1.2.3`). +5. Monitor the release workflow in [GitHub Actions][🧪build]. + +The workflow validates the version, runs tests, builds the gem, generates checksums, publishes the gem to RubyGems via trusted publishing (OIDC), and then creates the GitHub release. +The `release-github` job depends on `release-rubygems`, so a GitHub release is only created after a successful RubyGems publish. [📜src-gh]: https://github.com/CycloneDX/cyclonedx-ruby-gem [🧪build]: https://github.com/CycloneDX/cyclonedx-ruby-gem/actions From 6fccf2f8393ada625348a15381bed42d9d499567 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:58:05 +0000 Subject: [PATCH 17/24] Update gem release workflow to RubyGems trusted publishing pattern --- .github/workflows/release.yml | 162 ++++------------------------------ 1 file changed, 16 insertions(+), 146 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ed8117..92e0e54 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,158 +1,28 @@ -name: Release - -run-name: Release ${{ github.ref_name }} +name: Push gem on: push: tags: - - 'v*' - -permissions: {} + - "v*" jobs: - validate: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.VERSION }} - prerelease: ${{ steps.prerelease.outputs.PRERELEASE }} - steps: - - uses: actions/checkout@v6 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.3' - bundler-cache: false - - - name: Install dependencies - run: bundle install --jobs 4 --retry 3 - - - name: Extract version from tag - id: version - run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - - - name: Verify version matches code - run: | - TAG_VERSION="${{ steps.version.outputs.VERSION }}" - CODE_VERSION=$(bundle exec ruby -r ./lib/cyclonedx/ruby/version.rb -e 'puts Cyclonedx::Ruby::VERSION') - echo "Tag version: $TAG_VERSION" - echo "Code version: $CODE_VERSION" - if [ "$TAG_VERSION" != "$CODE_VERSION" ]; then - echo "::error::Version mismatch! Tag is v$TAG_VERSION but code version is $CODE_VERSION" - exit 1 - fi - - - name: Determine if prerelease - id: prerelease - run: | - VERSION="${{ steps.version.outputs.VERSION }}" - # A version is a prerelease if it contains anything after the patch version - # e.g., 1.2.3-alpha.1, 1.2.3.rc1, 1.2.3-beta are all prereleases - # but 1.2.3 is a stable release - if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "PRERELEASE=false" >> $GITHUB_OUTPUT - echo "Detected stable release: $VERSION" - else - echo "PRERELEASE=true" >> $GITHUB_OUTPUT - echo "Detected prerelease: $VERSION" - fi - - test: - needs: validate - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.3' - bundler-cache: false - - - name: Install dependencies - run: bundle install --jobs 4 --retry 3 - - - name: Run tests - run: bundle exec rake test - - build: - needs: test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.3' - bundler-cache: false - - - name: Install dependencies - run: bundle install --jobs 4 --retry 3 - - - name: Build gem - run: bundle exec rake build - - - name: Generate checksum - run: bundle exec rake build:checksum - - - name: List build artifacts - run: ls -lh pkg/ checksums/ - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: gem-package - path: | - pkg/*.gem - checksums/*.sha512 - - release-rubygems: - needs: [validate, build] + push: runs-on: ubuntu-latest - if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' + permissions: + contents: write id-token: write - steps: - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.3' - bundler-cache: false - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: gem-package - - name: Configure RubyGems credentials - uses: rubygems/configure-rubygems-credentials@v1 + environment: release - - name: Publish to RubyGems - run: gem push *.gem - - release-github: - needs: [validate, build, release-rubygems] - runs-on: ubuntu-latest - # GitHub release creation requires contents:write to create releases - permissions: - contents: write steps: - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: gem-package - - - name: Create GitHub Release - run: | - PRERELEASE_FLAG="" - if [ "${{ needs.validate.outputs.prerelease }}" = "true" ]; then - PRERELEASE_FLAG="--prerelease" - fi - gh release create "${{ github.ref_name }}" \ - *.gem *.sha512 \ - --title "${{ github.ref_name }}" \ - --generate-notes \ - $PRERELEASE_FLAG - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ruby + + - uses: rubygems/release-gem@v1 From 1f9577aa05a9bc0a037c1250a246d07620f1f63c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 07:58:42 +0000 Subject: [PATCH 18/24] Document required release environment in workflow --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 92e0e54..c1c44f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,7 @@ jobs: contents: write id-token: write + # If you configured a GitHub environment on RubyGems, you must use it here. environment: release steps: From 2f751db211067ac4e3c92557d4ecc98a9cfcfc1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 12:14:32 +0000 Subject: [PATCH 19/24] Restore full release pipeline and RubyGems trusted publish job --- .github/workflows/release.yml | 155 +++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1c44f1..6770135 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,29 +1,156 @@ -name: Push gem +name: Release + +run-name: Release ${{ github.ref_name }} on: push: tags: - - "v*" + - 'v*' + +permissions: {} jobs: - push: + validate: runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.VERSION }} + prerelease: ${{ steps.prerelease.outputs.PRERELEASE }} + steps: + - uses: actions/checkout@v5 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Verify version matches code + run: | + TAG_VERSION="${{ steps.version.outputs.VERSION }}" + CODE_VERSION=$(bundle exec ruby -r ./lib/cyclonedx/ruby/version.rb -e 'puts Cyclonedx::Ruby::VERSION') + echo "Tag version: $TAG_VERSION" + echo "Code version: $CODE_VERSION" + if [ "$TAG_VERSION" != "$CODE_VERSION" ]; then + echo "::error::Version mismatch! Tag is v$TAG_VERSION but code version is $CODE_VERSION" + exit 1 + fi + + - name: Determine if prerelease + id: prerelease + run: | + VERSION="${{ steps.version.outputs.VERSION }}" + # A version is a prerelease if it contains anything after the patch version + # e.g., 1.2.3-alpha.1, 1.2.3.rc1, 1.2.3-beta are all prereleases + # but 1.2.3 is a stable release + if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "PRERELEASE=false" >> $GITHUB_OUTPUT + echo "Detected stable release: $VERSION" + else + echo "PRERELEASE=true" >> $GITHUB_OUTPUT + echo "Detected prerelease: $VERSION" + fi + test: + needs: validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + + - name: Run tests + run: bundle exec rake test + + build: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Install dependencies + run: bundle install --jobs 4 --retry 3 + + - name: Build gem + run: bundle exec rake build + + - name: Generate checksum + run: bundle exec rake build:checksum + + - name: List build artifacts + run: ls -lh pkg/ checksums/ + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: gem-package + path: | + pkg/*.gem + checksums/*.sha512 + + release-rubygems: + needs: [validate, build] + runs-on: ubuntu-latest + if: github.repository == 'CycloneDX/cyclonedx-ruby-gem' permissions: contents: write id-token: write - # If you configured a GitHub environment on RubyGems, you must use it here. environment: release + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ruby + + - uses: rubygems/release-gem@v1 + release-github: + needs: [validate, build, release-rubygems] + runs-on: ubuntu-latest + # GitHub release creation requires contents:write to create releases + permissions: + contents: write steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - ruby-version: ruby - - - uses: rubygems/release-gem@v1 + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: gem-package + + - name: Create GitHub Release + run: | + PRERELEASE_FLAG="" + if [ "${{ needs.validate.outputs.prerelease }}" = "true" ]; then + PRERELEASE_FLAG="--prerelease" + fi + gh release create "${{ github.ref_name }}" \ + *.gem *.sha512 \ + --title "${{ github.ref_name }}" \ + --generate-notes \ + $PRERELEASE_FLAG + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3341a14ab9c190fcad76d281567dddb2a8660e02 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 12:15:10 +0000 Subject: [PATCH 20/24] Fix GitHub release artifact paths in workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6770135..e30e498 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -148,7 +148,7 @@ jobs: PRERELEASE_FLAG="--prerelease" fi gh release create "${{ github.ref_name }}" \ - *.gem *.sha512 \ + pkg/*.gem checksums/*.sha512 \ --title "${{ github.ref_name }}" \ --generate-notes \ $PRERELEASE_FLAG From 819daa2e2664838b44e6a0a5c59efacbcc2ee153 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 12:15:43 +0000 Subject: [PATCH 21/24] Make GitHub release artifact discovery path-agnostic --- .github/workflows/release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e30e498..ffafea9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -144,11 +144,17 @@ jobs: - name: Create GitHub Release run: | PRERELEASE_FLAG="" + GEM_FILES=$(find . -maxdepth 3 -type f -name '*.gem') + CHECKSUM_FILES=$(find . -maxdepth 3 -type f -name '*.sha512') + if [ -z "$GEM_FILES" ] || [ -z "$CHECKSUM_FILES" ]; then + echo "::error::Expected .gem and .sha512 files from build artifact download" + exit 1 + fi if [ "${{ needs.validate.outputs.prerelease }}" = "true" ]; then PRERELEASE_FLAG="--prerelease" fi gh release create "${{ github.ref_name }}" \ - pkg/*.gem checksums/*.sha512 \ + $GEM_FILES $CHECKSUM_FILES \ --title "${{ github.ref_name }}" \ --generate-notes \ $PRERELEASE_FLAG From 5585ee505f5e36ecb3d2cd955a6bc223d2c5b41c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 12:16:13 +0000 Subject: [PATCH 22/24] Harden GitHub release asset collection command --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffafea9..db5c0f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -144,9 +144,9 @@ jobs: - name: Create GitHub Release run: | PRERELEASE_FLAG="" - GEM_FILES=$(find . -maxdepth 3 -type f -name '*.gem') - CHECKSUM_FILES=$(find . -maxdepth 3 -type f -name '*.sha512') - if [ -z "$GEM_FILES" ] || [ -z "$CHECKSUM_FILES" ]; then + mapfile -t GEM_FILES < <(find . -type f -name '*.gem') + mapfile -t CHECKSUM_FILES < <(find . -type f -name '*.sha512') + if [ "${#GEM_FILES[@]}" -eq 0 ] || [ "${#CHECKSUM_FILES[@]}" -eq 0 ]; then echo "::error::Expected .gem and .sha512 files from build artifact download" exit 1 fi @@ -154,7 +154,7 @@ jobs: PRERELEASE_FLAG="--prerelease" fi gh release create "${{ github.ref_name }}" \ - $GEM_FILES $CHECKSUM_FILES \ + "${GEM_FILES[@]}" "${CHECKSUM_FILES[@]}" \ --title "${{ github.ref_name }}" \ --generate-notes \ $PRERELEASE_FLAG From 1aa20e7e670409c0f02b85ef424e15e0fe81875d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 12:16:41 +0000 Subject: [PATCH 23/24] Scope release asset lookup to downloaded artifact path --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db5c0f9..7d329f7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -140,12 +140,13 @@ jobs: uses: actions/download-artifact@v4 with: name: gem-package + path: release-assets - name: Create GitHub Release run: | PRERELEASE_FLAG="" - mapfile -t GEM_FILES < <(find . -type f -name '*.gem') - mapfile -t CHECKSUM_FILES < <(find . -type f -name '*.sha512') + mapfile -t GEM_FILES < <(find release-assets -type f -name '*.gem') + mapfile -t CHECKSUM_FILES < <(find release-assets -type f -name '*.sha512') if [ "${#GEM_FILES[@]}" -eq 0 ] || [ "${#CHECKSUM_FILES[@]}" -eq 0 ]; then echo "::error::Expected .gem and .sha512 files from build artifact download" exit 1 From 35584f79e5c904deb1fccc12581cc3941693ae0e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 12:17:08 +0000 Subject: [PATCH 24/24] Require single gem and checksum artifact for GitHub release --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d329f7..fc94cd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -147,8 +147,8 @@ jobs: PRERELEASE_FLAG="" mapfile -t GEM_FILES < <(find release-assets -type f -name '*.gem') mapfile -t CHECKSUM_FILES < <(find release-assets -type f -name '*.sha512') - if [ "${#GEM_FILES[@]}" -eq 0 ] || [ "${#CHECKSUM_FILES[@]}" -eq 0 ]; then - echo "::error::Expected .gem and .sha512 files from build artifact download" + if [ "${#GEM_FILES[@]}" -ne 1 ] || [ "${#CHECKSUM_FILES[@]}" -ne 1 ]; then + echo "::error::Expected exactly one .gem and one .sha512 from build artifact download" exit 1 fi if [ "${{ needs.validate.outputs.prerelease }}" = "true" ]; then