Skip to content
Draft
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
92 changes: 92 additions & 0 deletions .github/RELEASE.md
Original file line number Diff line number Diff line change
@@ -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<MAJOR>.<MINOR>.<PATCH>` (e.g., `v1.2.0`)
- **Prereleases**: `v<MAJOR>.<MINOR>.<PATCH>-<PRERELEASE>` (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-<version>.gem`) - The built Ruby gem
2. **SHA-512 Checksum** (`cyclonedx-ruby-<version>.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

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.
168 changes: 168 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
name: Release

run-name: Release ${{ github.ref_name }}

on:
push:
tags:
- 'v*'

permissions: {}

jobs:
validate:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.VERSION }}
prerelease: ${{ steps.prerelease.outputs.PRERELEASE }}
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: Extract version from tag
Copy link
Member

Choose a reason for hiding this comment

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

@copilot move the version and determination prerelease detection to an own job

Copy link
Author

Choose a reason for hiding this comment

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

Moved version extraction and prerelease detection to dedicated validate job in commit 96a34a4. This job outputs version and prerelease for downstream jobs to consume.

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@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

- 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-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
Copy link
Member

Choose a reason for hiding this comment

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

move the "generate github release" to an own job...
publishing a release is nice to have.
maybe run this in parallel to rubygems release, if this is state of the art.

Copy link
Author

Choose a reason for hiding this comment

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

Moved GitHub release to dedicated release-github job in commit 96a34a4. Both release-github and release-rubygems now run in parallel (both depend on validate and build) for faster completion.

uses: softprops/action-gh-release@v1
with:
files: |
*.gem
*.sha512
prerelease: ${{ needs.validate.outputs.prerelease }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release-rubygems:
needs: [validate, 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: 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 }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Build Artifacts
/pkg/
/tmp/
/checksums/
*.gem

# Bundler
Expand Down
27 changes: 25 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<VERSION>"` 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<VERSION>` (e.g., `v1.2.0`)
- For prereleases: `git tag v<VERSION>-<PRERELEASE>` (e.g., `v1.3.0-alpha.1`, `v1.3.0-beta.2`, `v1.3.0-rc.1`)
10. Run `git push origin v<VERSION>` 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

Expand Down