Skip to content
Merged
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
61 changes: 61 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
name: Release action

"on":
push:
branches: [main]

permissions:
contents: write

jobs:
checks:
name: Checks
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Code checkout
uses: actions/checkout@v6.0.3

- name: Set up Ruby
uses: ruby/setup-ruby@v1.313.0
with:
ruby-version: "3.3"

- name: Install ShellCheck
run: |
sudo apt-get update
sudo apt-get install --yes shellcheck

- name: Install actionlint
run: |
go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
echo "${HOME}/go/bin" >> "${GITHUB_PATH}"

- name: Run checks
run: bash script/check

release:
name: Release
needs: checks
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v6.0.3
with:
fetch-depth: 0

- name: Bump version and push tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dry_run: false

- name: Create a GitHub release
uses: ncipollo/release-action@v1.21.0
with:
tag: ${{ steps.tag_version.outputs.new_tag }}
name: Release ${{ steps.tag_version.outputs.new_tag }}
body: ${{ steps.tag_version.outputs.changelog }}
34 changes: 34 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
name: Pull Request

"on":
pull_request:

permissions:
contents: read

jobs:
checks:
name: Checks
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v6.0.3

- name: Set up Ruby
uses: ruby/setup-ruby@v1.313.0
with:
ruby-version: "3.3"

- name: Install ShellCheck
run: |
sudo apt-get update
sudo apt-get install --yes shellcheck

- name: Install actionlint
run: |
go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
echo "${HOME}/go/bin" >> "${GITHUB_PATH}"

- name: Run checks
run: bash script/check
101 changes: 88 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,118 @@
# publish-gem-to-github
# publish-ruby-gem

Release your Ruby Gem to GitHub Packages, RubyGems.org, and Gem.coop with ease. This action is a fast, lightweight composite action that defaults to **Ruby 4.0**.
Release Ruby gems to GitHub Packages, RubyGems.org, Gem.coop, Forgejo, and Gitea from a single fast composite action.

## Why use this?

- **Fast:** Composite action runs directly on the host, avoiding Docker overhead.
- **Multi-Registry:** Support for GitHub Packages, RubyGems.org, and Gem.coop.
- **Simple:** Just add your API keys to enable pushing to each registry.
- **Modern:** Defaults to Ruby 4.0 but supports any version via input.
This is the preferred Actionshub path for publishing Ruby gems. It runs directly on the GitHub runner, masks supplied tokens, fails when build or push commands fail, and writes a short publish summary to the job summary.

## Usage

```yaml
name: release

on:
push:
branches: [main]

permissions:
contents: read
packages: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6.0.3

- name: Build and publish to multiple registries
uses: actionshub/publish-ruby-gem@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
rubygems_token: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
gemcoop_token: ${{ secrets.GEMCOOP_AUTH_TOKEN }}
forgejo_token: ${{ secrets.FORGEJO_TOKEN }}
forgejo_url: https://forgejo.example.com
gitea_token: ${{ secrets.GITEA_TOKEN }}
gitea_url: https://gitea.example.com
```

Only provide tokens for the registries you want to publish to.

### RubyGems.org only

```yaml
- name: Build and publish to RubyGems.org
uses: actionshub/publish-ruby-gem@main
with:
rubygems_token: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
```

### GitHub Packages only

```yaml
permissions:
contents: read
packages: write

steps:
- name: Build and publish to GitHub Packages
uses: actionshub/publish-ruby-gem@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
```

### Forgejo only

```yaml
- name: Build and publish to Forgejo
uses: actionshub/publish-ruby-gem@main
with:
forgejo_token: ${{ secrets.FORGEJO_TOKEN }}
forgejo_url: https://forgejo.example.com
forgejo_owner: my-org
```

Forgejo publishes to `https://forgejo.example.com/api/packages/{owner}/rubygems`.

### Gitea only

```yaml
- name: Build and publish to Gitea
uses: actionshub/publish-ruby-gem@main
with:
gitea_token: ${{ secrets.GITEA_TOKEN }}
gitea_url: https://gitea.example.com
gitea_owner: my-org
```

Gitea publishes to `https://gitea.example.com/api/packages/{owner}/rubygems`.

## Inputs

| Input | Description | Default |
|-------|-------------|---------|
| `github_token` | Token for GitHub package registry. | N/A |
| `github_token` | Token for GitHub Packages. | N/A |
| `rubygems_token` | API key for RubyGems.org. | N/A |
| `gemcoop_token` | API key for Gem.coop. | N/A |
| `forgejo_token` | Personal access token for a Forgejo RubyGems package registry. | N/A |
| `forgejo_url` | Base URL for the Forgejo instance. Required when `forgejo_token` is set. | N/A |
| `forgejo_owner` | User or organization account for the Forgejo package. | `owner`, then `${GITHUB_REPOSITORY_OWNER}` |
| `gitea_token` | Personal access token for a Gitea RubyGems package registry. | N/A |
| `gitea_url` | Base URL for the Gitea instance. Required when `gitea_token` is set. | N/A |
| `gitea_owner` | User or organization account for the Gitea package. | `owner`, then `${GITHUB_REPOSITORY_OWNER}` |
| `token` | Alias for `github_token`. | N/A |
| `owner` | Name of the user or organization account for GitHub Packages. | `${GITHUB_REPOSITORY_OWNER}` |
| `working-directory` | Path to switch to before building the Gem. | `.` |
| `ruby-version` | Ruby version to use for build/push. | `4.0` |
| `owner` | User or organization account for GitHub Packages. | `${GITHUB_REPOSITORY_OWNER}` |
| `working-directory` | Path to switch to before building the gem. | `.` |
| `ruby-version` | Ruby version to use for build and push. | `4.0` |

## Behavior

- At least one registry token is required.
- Forgejo and Gitea require their matching `*_url` input when their token is set.
- The action builds top-level `*.gemspec` files in the working directory.
- Only gems built during the current action run are pushed.
- Failed builds and failed pushes stop the action with a GitHub Actions error annotation.
- Token values are masked before any publish work starts.

## Credits

Expand Down
105 changes: 33 additions & 72 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "Publish Gem to GitHub Packages"
description: "Builds and publishes Ruby gems to GitHub Packages"
name: "Publish Ruby Gem"
description: "Builds and publishes Ruby gems to GitHub Packages, RubyGems.org, Gem.coop, Forgejo, and Gitea"
author: "Dan Webb"
branding:
color: green
Expand All @@ -17,6 +17,24 @@ inputs:
gemcoop_token:
description: "API key for gem.coop."
required: false
forgejo_token:
description: "Personal access token for a Forgejo RubyGems package registry."
required: false
forgejo_url:
description: "Base URL for the Forgejo instance, for example https://forgejo.example.com."
required: false
forgejo_owner:
description: "User or organization account that owns the Forgejo package. Defaults to owner or GITHUB_REPOSITORY_OWNER."
required: false
gitea_token:
description: "Personal access token for a Gitea RubyGems package registry."
required: false
gitea_url:
description: "Base URL for the Gitea instance, for example https://gitea.example.com."
required: false
gitea_owner:
description: "User or organization account that owns the Gitea package. Defaults to owner or GITHUB_REPOSITORY_OWNER."
required: false
owner:
description: "Name of the user or organization account that owns the repository containing your project. Defaults to $GITHUB_REPOSITORY_OWNER"
required: false
Expand All @@ -33,78 +51,21 @@ runs:
using: "composite"
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@v1.313.0
with:
ruby-version: ${{ inputs.ruby-version }}

- shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
git config --global --add safe.directory "${GITHUB_WORKSPACE}"

# Collect tokens
GITHUB_TOKEN="${{ inputs.github_token || inputs.token }}"
RUBYGEMS_TOKEN="${{ inputs.rubygems_token }}"
GEMCOOP_TOKEN="${{ inputs.gemcoop_token }}"

# Check if at least one token is provided
if [ -z "$GITHUB_TOKEN" ] && [ -z "$RUBYGEMS_TOKEN" ] && [ -z "$GEMCOOP_TOKEN" ]; then
echo "Error: No API keys found. Please provide at least one of: token, github_token, rubygems_token, or gemcoop_token."
exit 2
fi

# Determine owner for GitHub Packages
OWNER="${{ inputs.owner }}"
if [ -z "$OWNER" ]; then
OWNER="${GITHUB_REPOSITORY_OWNER}"
fi

echo "Setting up access to RubyGems"
mkdir -p ~/.gem
touch ~/.gem/credentials
chmod 600 ~/.gem/credentials

# Build credentials file
# We start with an empty file and append each configured host
: > ~/.gem/credentials

if [ -n "$RUBYGEMS_TOKEN" ]; then
echo "Configuring RubyGems.org"
echo ":rubygems_api_key: ${RUBYGEMS_TOKEN}" >> ~/.gem/credentials
fi

if [ -n "$GITHUB_TOKEN" ]; then
echo "Configuring GitHub Packages"
echo ":github: Bearer ${GITHUB_TOKEN}" >> ~/.gem/credentials
fi

if [ -n "$GEMCOOP_TOKEN" ]; then
echo "Configuring Gem.coop"
# Assuming standard token usage for gem.coop
echo ":gemcoop: ${GEMCOOP_TOKEN}" >> ~/.gem/credentials
fi

echo "Building gem(s)"
find . -name '*.gemspec' -maxdepth 1 -exec gem build {} \;

# Push to each configured registry
for gem in *.gem; do
if [ -f "$gem" ]; then

if [ -n "$RUBYGEMS_TOKEN" ]; then
echo "Pushing $gem to RubyGems.org"
gem push "$gem"
fi

if [ -n "$GITHUB_TOKEN" ]; then
echo "Pushing $gem to GitHub Packages host https://rubygems.pkg.github.com/${OWNER}"
gem push "$gem" --key "github" --host "https://rubygems.pkg.github.com/${OWNER}"
fi

if [ -n "$GEMCOOP_TOKEN" ]; then
echo "Pushing $gem to Gem.coop"
gem push "$gem" --key "gemcoop" --host "https://gem.coop"
fi

fi
done
env:
PUBLISH_GEM_GITHUB_TOKEN: ${{ inputs.github_token || inputs.token }}
PUBLISH_GEM_RUBYGEMS_TOKEN: ${{ inputs.rubygems_token }}
PUBLISH_GEM_GEMCOOP_TOKEN: ${{ inputs.gemcoop_token }}
PUBLISH_GEM_OWNER: ${{ inputs.owner }}
PUBLISH_GEM_FORGEJO_TOKEN: ${{ inputs.forgejo_token }}
PUBLISH_GEM_FORGEJO_URL: ${{ inputs.forgejo_url }}
PUBLISH_GEM_FORGEJO_OWNER: ${{ inputs.forgejo_owner }}
PUBLISH_GEM_GITEA_TOKEN: ${{ inputs.gitea_token }}
PUBLISH_GEM_GITEA_URL: ${{ inputs.gitea_url }}
PUBLISH_GEM_GITEA_OWNER: ${{ inputs.gitea_owner }}
run: "${GITHUB_ACTION_PATH}/script/publish"
13 changes: 13 additions & 0 deletions script/check
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

set -euo pipefail

cd "$(dirname "${BASH_SOURCE[0]}")/.."

shell_scripts=(script/publish test/publish_test.sh script/check)

bash -n "${shell_scripts[@]}"
shellcheck "${shell_scripts[@]}"
ruby -e 'require "yaml"; YAML.load_file("action.yml")'
actionlint
bash test/publish_test.sh
Loading