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
107 changes: 102 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Release
concurrency: ${{ github.workflow }}-${{ github.ref }}

on:
# For Craft's own releases (dogfooding)
workflow_dispatch:
inputs:
version:
Expand All @@ -11,35 +13,130 @@ on:
description: Force a release even when there are release-blockers (optional)
required: false

# For external repos to call this workflow
workflow_call:
inputs:
version:
description: Version to release (semver, bump type, or "auto")
type: string
required: false
force:
description: Force a release even when there are release-blockers
type: string
required: false
default: "false"
merge_target:
description: Target branch to merge into
type: string
required: false
blocker_label:
description: Label that blocks releases
type: string
required: false
default: "release-blocker"
publish_repo:
description: Repository for publish issues (owner/repo format)
type: string
required: false
git_user_name:
description: Git committer name
type: string
required: false
git_user_email:
description: Git committer email
type: string
required: false
path:
description: The path that Craft will run inside
type: string
required: false
default: "."
craft_config_from_merge_target:
description: Use the craft config from the merge target branch
type: string
required: false
default: "false"
outputs:
version:
description: The resolved version being released
value: ${{ jobs.release.outputs.version }}
branch:
description: The release branch name
value: ${{ jobs.release.outputs.branch }}
sha:
description: The commit SHA on the release branch
value: ${{ jobs.release.outputs.sha }}
previous_tag:
description: The tag before this release (for diff links)
value: ${{ jobs.release.outputs.previous_tag }}
changelog:
description: The changelog for this release
value: ${{ jobs.release.outputs.changelog }}

jobs:
# Build job only for Craft's own releases (dogfooding)
build:
if: github.event_name == 'workflow_dispatch'
name: Build
uses: ./.github/workflows/build.yml
permissions:
contents: read

release:
needs: build
needs: [build]
# Run if build succeeded OR was skipped (workflow_call case)
if: always() && (needs.build.result == 'success' || needs.build.result == 'skipped')
runs-on: ubuntu-latest
name: 'Release a new version'
permissions:
contents: write
outputs:
version: ${{ steps.craft-local.outputs.version || steps.craft-action.outputs.version }}
branch: ${{ steps.craft-local.outputs.branch || steps.craft-action.outputs.branch }}
sha: ${{ steps.craft-local.outputs.sha || steps.craft-action.outputs.sha }}
previous_tag: ${{ steps.craft-local.outputs.previous_tag || steps.craft-action.outputs.previous_tag }}
changelog: ${{ steps.craft-local.outputs.changelog || steps.craft-action.outputs.changelog }}
steps:
# For Craft repo: use the release bot token
- name: Get auth token
id: token
if: github.event_name == 'workflow_dispatch' && github.repository == 'getsentry/craft'
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
with:
app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }}
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3

- uses: actions/checkout@v4
with:
# Fetch all commits so we can determine previous version
token: ${{ steps.token.outputs.token }}
# Use release bot token for Craft repo, inherited token for external repos
token: ${{ steps.token.outputs.token || github.token }}
fetch-depth: 0
- name: Prepare release

# For Craft's own releases: use local action (dogfooding)
- name: Prepare release (dogfooding)
if: github.event_name == 'workflow_dispatch'
id: craft-local
uses: ./
env:
GITHUB_TOKEN: ${{ steps.token.outputs.token }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: When workflow_dispatch is run on a fork, the token generation step is skipped, but the craft-local step still tries to use its output, resulting in an empty GITHUB_TOKEN.
Severity: HIGH | Confidence: High

🔍 Detailed Analysis

When the workflow is triggered via workflow_dispatch on a fork of the getsentry/craft repository, the token generation step is skipped. This is because its condition if: github.event_name == 'workflow_dispatch' && github.repository == 'getsentry/craft' evaluates to false on a fork. The subsequent craft-local step, however, is still executed and attempts to use the output from the skipped step via ${{ steps.token.outputs.token }}. In GitHub Actions, referencing an output from a skipped step results in an empty string. This causes the craft action to run with an empty GITHUB_TOKEN, leading to authentication failures and GitHub API errors. This bug prevents contributors from successfully testing the workflow on their forks.

💡 Suggested Fix

Add a condition to the craft-local step to ensure it only runs when the token generation step has also run. The condition should mirror the one on the token step: if: github.event_name == 'workflow_dispatch' && github.repository == 'getsentry/craft'.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: .github/workflows/release.yml#L121

Potential issue: When the workflow is triggered via `workflow_dispatch` on a fork of the
`getsentry/craft` repository, the token generation step is skipped. This is because its
condition `if: github.event_name == 'workflow_dispatch' && github.repository ==
'getsentry/craft'` evaluates to false on a fork. The subsequent `craft-local` step,
however, is still executed and attempts to use the output from the skipped step via `${{
steps.token.outputs.token }}`. In GitHub Actions, referencing an output from a skipped
step results in an empty string. This causes the `craft` action to run with an empty
`GITHUB_TOKEN`, leading to authentication failures and GitHub API errors. This bug
prevents contributors from successfully testing the workflow on their forks.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 7958081

with:
version: ${{ github.event.inputs.version }}
force: ${{ github.event.inputs.force }}

# For external repos: use published action
- name: Prepare release
if: github.event_name == 'workflow_call'
id: craft-action
uses: getsentry/craft@v2
env:
GITHUB_TOKEN: ${{ github.token }}
with:
version: ${{ inputs.version }}
force: ${{ inputs.force }}
merge_target: ${{ inputs.merge_target }}
blocker_label: ${{ inputs.blocker_label }}
publish_repo: ${{ inputs.publish_repo }}
git_user_name: ${{ inputs.git_user_name }}
git_user_email: ${{ inputs.git_user_email }}
path: ${{ inputs.path }}
craft_config_from_merge_target: ${{ inputs.craft_config_from_merge_target }}
92 changes: 59 additions & 33 deletions docs/src/content/docs/github-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ Craft provides GitHub Actions for automating releases and previewing changelog e

For a real-world example of using Craft's GitHub Actions, see the [getsentry/publish](https://github.com/getsentry/publish) repository.

## Prepare Release Action
## Prepare Release

The main Craft action automates the `craft prepare` workflow in GitHub Actions. It creates a release branch, updates the changelog, and opens a publish request issue.
Craft offers two ways to automate releases in GitHub Actions:

### Basic Usage
| Option | Best For | Flexibility |
|--------|----------|-------------|
| **Reusable Workflow** | Quick setup, standard release flow | Low - runs as a complete job |
| **Composite Action** | Custom workflows, pre/post steps | High - composable with other steps |

### Option 1: Reusable Workflow (Recommended)

The simplest way to set up Craft releases. Call the workflow and let it handle everything:

```yaml
name: Release
Expand All @@ -24,19 +31,13 @@ on:

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: getsentry/craft@v2
with:
version: ${{ github.event.inputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: getsentry/craft/.github/workflows/release.yml@v2
with:
version: ${{ inputs.version }}
secrets: inherit
```

### Inputs
#### Workflow Inputs

| Input | Description | Default |
|-------|-------------|---------|
Expand All @@ -50,7 +51,7 @@ jobs:
| `path` | The path that Craft will run inside. | `.` |
| `craft_config_from_merge_target` | Use the craft config from the merge target branch. | `false` |

### Outputs
#### Workflow Outputs

| Output | Description |
|--------|-------------|
Expand All @@ -60,15 +61,18 @@ jobs:
| `previous_tag` | The tag before this release (for diff links) |
| `changelog` | The changelog for this release |

### Auto-versioning Example
### Option 2: Composite Action

When using auto-versioning, Craft analyzes conventional commits to determine the version bump:
Use the action directly when you need to add custom steps before or after the release, or integrate Craft into a more complex workflow:

```yaml
name: Auto Release
name: Release
on:
schedule:
- cron: '0 10 * * 1' # Every Monday at 10 AM
workflow_dispatch:
inputs:
version:
description: 'Version to release (or "auto")'
required: false

jobs:
release:
Expand All @@ -77,11 +81,39 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0

# Custom pre-release steps
- run: echo "Running pre-release checks..."

- uses: getsentry/craft@v2
with:
version: auto
version: ${{ github.event.inputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Custom post-release steps
- run: echo "Release prepared!"
```

The action accepts the same inputs and produces the same outputs as the reusable workflow.

### Auto-versioning Example

When using auto-versioning, Craft analyzes conventional commits to determine the version bump. This works with both the workflow and the action:

```yaml
# Using the reusable workflow
name: Auto Release
on:
schedule:
- cron: '0 10 * * 1' # Every Monday at 10 AM

jobs:
release:
uses: getsentry/craft/.github/workflows/release.yml@v2
with:
version: auto
secrets: inherit
```

## Changelog Preview (Reusable Workflow)
Expand Down Expand Up @@ -208,9 +240,9 @@ PRs with the `skip-changelog` label or from excluded authors will not appear in

## Tips

### Combining Both Actions
### Combining Both Workflows

You can use both the changelog preview and release actions together for a complete release workflow. See the [getsentry/publish](https://github.com/getsentry/publish) repository for a real-world example.
You can use both the changelog preview and release workflows together for a complete release flow:

```yaml
# .github/workflows/changelog-preview.yml
Expand All @@ -237,14 +269,8 @@ on:

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: getsentry/craft@v2
with:
version: ${{ github.event.inputs.version || 'auto' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: getsentry/craft/.github/workflows/release.yml@v2
with:
version: ${{ inputs.version || 'auto' }}
secrets: inherit
```
Loading