Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Release

on:
push:
tags: ["v*.*.*"]

Comment on lines +3 to +6

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe bundle exec rake release already handles this case by checking whether the version tag exists before attempting to create it. See the relevant implementation in Bundler's GemHelper:

  1. https://github.com/ruby/rubygems/blob/v3.6.9/bundler/lib/bundler/gem_helper.rb#L67
  2. https://github.com/ruby/rubygems/blob/v3.6.9/bundler/lib/bundler/gem_helper.rb#L76.

So a workflow triggered by an existing tag shouldn't fail simply because the tag is already present.

# see https://docs.github.com/en/actions/tutorials/authenticate-with-github_token#modifying-the-permissions-for-the-github_token
permissions: {}

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Code
# see https://github.com/actions/checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Ruby
# see https://github.com/ruby/setup-ruby
uses: ruby/setup-ruby@9eb537ca036ebaed86729dcb9309076e4c5c3b74 # v1.314.0
with:
ruby-version: '3.3.9'
bundler-cache: false
- name: Install Dependencies
run: bundle install
- name: Run the default task
run: bundle exec rake

rubygems_release:
name: RubyGems Release
if: github.repository == 'CycloneDX/cyclonedx-ruby-gem'
runs-on: ubuntu-latest
timeout-minutes: 10
needs: test
permissions:
contents: write # Required for `rake release` to perform git synchronization.
id-token: write # Required for Trusted Publishing authentication.
environment: release
steps:
- name: Checkout Code
# see https://github.com/actions/checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Ruby
# see https://github.com/ruby/setup-ruby
uses: ruby/setup-ruby@9eb537ca036ebaed86729dcb9309076e4c5c3b74 # v1.314.0
with:
ruby-version: '3.4' # Stable environment to package the gem for the supported ruby versions
bundler-cache: false
- name: Push gem to RubyGems
# see https://github.com/rubygems/release-gem
uses: rubygems/release-gem@6317d8d1f7e28c24d28f6eff169ea854948bd9f7 # v1.2.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a comment with the action's URL for easier docks lookup when maintaining.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in da61dd3. Thanks for the suggestion.

- name: Upload gem artifact
# see https://github.com/actions/upload-artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
with:
name: gem
path: pkg/*.gem
retention-days: 1

github_release:
name: GitHub Release
if: github.repository == 'CycloneDX/cyclonedx-ruby-gem'
runs-on: ubuntu-latest
timeout-minutes: 10
needs: rubygems_release
environment: release
# Needed for `gh release create` because GitHub Releases require write access
# to repository contents, which is not granted by default with GITHUB_TOKEN.
permissions:
contents: write

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a comment why this permission is needed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in e3c2541. Thanks for the suggestion.

steps:
- name: Checkout Code
# see https://github.com/actions/checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Download gem artifact
# see https://github.com/actions/download-artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: gem
path: pkg
- name: Create GitHub Release

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could the release have the gem as a release asset?
It would be ideal to use the one produced in the rubygems_release job - you could use https://docs.github.com/en/actions/concepts/workflows-and-actions/workflow-artifacts to transfer a file from one job to the other. (artifact lifetime of 1 day should be enough)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be okay with deferring this for now? I don't think it's necessary to include the built gem in the GitHub release since it'll already be published to RubyGems, and anyone can build it from the release assets. AFAIK, rubygems/release-gem does not produce a build artifact, so we'd need to add additional steps to build the gem and upload it separately.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just artifact pkg/*.gem at the end of the rubygems_release should work, right?

I mean, rubygems/release-gem does not do any magic, it just calls bundle exec rake release which will cause emitting files pkg/*.gem.
see https://github.com/rubygems/release-gem/blob/052cc82692552de3ef2b81fd670e41d13cba8092/action.yml#L60-L67

@extern-c extern-c Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rechecked the rubygems/release-gem behavior and think that the generated gem remains available after the release step, so it should be reusable as you suggested 👍. See commit 2af378b.

run: |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rework this to

run: >
  gh release create ...
  --verify-tag
  ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see 9e50640.

declare -a FLAGS=("--verify-tag" "--generate-notes")

# Determine release type from tag suffix
if [[ "$GITHUB_REF_NAME" =~ -(alpha|beta|rc|pre)[0-9.]*$ ]]; then
FLAGS+=("--prerelease")
fi

# Publish the release on GitHub with auto-generated release notes
gh release create "${FLAGS[@]}" -- "$GITHUB_REF_NAME" pkg/*.gem
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ Please file a bug if you notice a violation of semantic versioning.

### Added

- Semi-automated CI release workflow triggered by version tags (`vMAJOR.MINOR.PATCH` with optional pre-release suffixes) (#46)
- RubyGems publishing via `rubygems_release` using trusted publishing
- Automated GitHub Releases with generated release notes, including support for pre-releases
- `CONTRIBUTING.md` file to help people find their way to contributing
- `CHANGELOG.md` file to document notable changes in keep-a-changelog format
- `Cyclonedx::BomHelpers` module to house helper methods, replacing global methods
Expand Down
42 changes: 13 additions & 29 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,36 +88,20 @@ 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<VERSION>"` to commit the changes
5. Run `git push` to trigger the final CI pipeline before release, and merge PRs
#### Semi-automated process

1. Update the version number in `version.rb`, and ensure `CHANGELOG.md` reflects the changes.
2. Commit the changes:
```sh
git commit -am "🔖 Prepare release v<VERSION>"
```
3. Push the changes to trigger the final CI pipeline before the release.
- 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 name>-<version>.gem`
4. Create a git tag for the gem version. Prefix the tag with 'v'. For example, if the current [gem version](https://github.com/CycloneDX/cyclonedx-ruby-gem/blob/master/lib/cyclonedx/ruby/version.rb) is 1.2.0, the tag should be 'v1.2.0'. Pre-releases are supported. See the [release workflow](https://github.com/CycloneDX/cyclonedx-ruby-gem/blob/master/.github/workflows/release.yml) for more details. Push the tag to the repository to trigger the release workflow.

> [!WARNING]
> The gem version and git tag MUST match. A mismatch may cause `rake release` (invoked by `rubygems/release-gem`) to create and push a tag for the gem version, which could produce unexpected results.
5. Monitor the release workflow and verify that the release completes succesfully.

[📜src-gh]: https://github.com/CycloneDX/cyclonedx-ruby-gem
[🧪build]: https://github.com/CycloneDX/cyclonedx-ruby-gem/actions
Expand Down