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
47 changes: 44 additions & 3 deletions .github/workflows/_test-gh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ on:
- ".github/workflows/gh-release.yaml"

jobs:
# Test 1: Dry-run semantic release
test_semantic_release_dry_run:
uses: ./.github/workflows/gh-release.yaml
with:
use_semantic_release: true
dry_run: true

# Test 2: Verify outputs are properly exposed
test_outputs:
needs: test_semantic_release_dry_run
runs-on: ubuntu-latest
Expand Down Expand Up @@ -46,4 +44,47 @@ jobs:
with:
use_semantic_release: true
dry_run: true
tag_format: "fpp-v${version}"
tag_format: "foo-v${version}"

test_working_directory:
uses: ./.github/workflows/gh-release.yaml
with:
working_directory: tests/lambda/python
dry_run: true

test_working_directory_outputs:
needs: test_working_directory
runs-on: ubuntu-latest
steps:
- name: Display all outputs
run: |
echo "=== Working Directory Test Outputs ==="
echo "new_release_published: ${{ needs.test_working_directory.outputs.new_release_published }}"
echo "new_release_version: ${{ needs.test_working_directory.outputs.new_release_version }}"
echo "new_release_major_version: ${{ needs.test_working_directory.outputs.new_release_major_version }}"
echo "new_release_minor_version: ${{ needs.test_working_directory.outputs.new_release_minor_version }}"
echo "new_release_patch_version: ${{ needs.test_working_directory.outputs.new_release_patch_version }}"
echo "new_release_git_tag: ${{ needs.test_working_directory.outputs.new_release_git_tag }}"
echo "new_release_git_head: ${{ needs.test_working_directory.outputs.new_release_git_head }}"

- name: Validate first release
run: |
if [ "${{ needs.test_working_directory.outputs.new_release_published }}" != "true" ]; then
echo "❌ Expected new_release_published to be true"
exit 1
fi

if [ "${{ needs.test_working_directory.outputs.new_release_version }}" != "1.0.0" ]; then
echo "❌ Expected version to be 1.0.0 (first release), got: ${{ needs.test_working_directory.outputs.new_release_version }}"
exit 1
fi

if [ "${{ needs.test_working_directory.outputs.new_release_git_tag }}" != "python-lambda-v1.0.0" ]; then
echo "❌ Expected tag to be python-lambda-v1.0.0, got: ${{ needs.test_working_directory.outputs.new_release_git_tag }}"
exit 1
fi

echo "✅ Working directory test passed!"
echo " Version: ${{ needs.test_working_directory.outputs.new_release_version }}"
echo " Git tag: ${{ needs.test_working_directory.outputs.new_release_git_tag }}"
echo " Tag format from .releaserc.json was correctly applied"
8 changes: 6 additions & 2 deletions .github/workflows/gh-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ on:
type: string
required: false
tag_format:
description: "The format of the tag to release (for manual releases)."
description: "The format of the tag to release. If not provided, uses the tagFormat from .releaserc.json or semantic-release default (v${version})."
type: string
default: "v${version}"
required: false
use_semantic_release:
description: "Use semantic-release for automatic versioning and changelog generation"
type: boolean
default: true
working_directory:
description: "The working directory where the project is located (relative to the repository root)."
type: string
update_version_aliases:
description: "Automatically update version alias tags (e.g., v1 and v1.2) to point to the latest release."
type: boolean
Expand Down Expand Up @@ -118,6 +121,7 @@ jobs:
unset_gha_env: ${{ github.event_name == 'pull_request' }}
ci: ${{ github.event_name == 'pull_request' && false || true }}
tag_format: ${{ inputs.tag_format }}
working_directory: ${{ inputs.working_directory }}
branches: |
[
'main',
Expand Down
158 changes: 154 additions & 4 deletions docs/workflows/gh-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ This workflow creates a release based on the tag.
| name | description | type | required | default |
| --- | --- | --- | --- | --- |
| `tag` | <p>The tag to release (for manual releases). If not provided, uses semantic-release for automatic versioning.</p> | `string` | `false` | `""` |
| `tag_format` | <p>The format of the tag to release (for manual releases).</p> | `string` | `false` | `v${version}` |
| `tag_format` | <p>The format of the tag to release. If not provided, uses the tagFormat from .releaserc.json or semantic-release default (v${version}).</p> | `string` | `false` | `""` |
| `use_semantic_release` | <p>Use semantic-release for automatic versioning and changelog generation</p> | `boolean` | `false` | `true` |
| `working_directory` | <p>The working directory where the project is located (relative to the repository root).</p> | `string` | `false` | `""` |
| `update_version_aliases` | <p>Automatically update version alias tags (e.g., v1 and v1.2) to point to the latest release.</p> | `boolean` | `false` | `false` |
| `dry_run` | <p>Run in dry-run mode to preview the release without creating it.</p> | `boolean` | `false` | `false` |
| `app_id` | <p>GitHub App ID (for generating a token if using GitHub App authentication)</p> | `string` | `false` | `""` |
Expand Down Expand Up @@ -54,11 +55,11 @@ jobs:
# Default: ""

tag_format:
# The format of the tag to release (for manual releases).
# The format of the tag to release. If not provided, uses the tagFormat from .releaserc.json or semantic-release default (v${version}).
#
# Type: string
# Required: false
# Default: v${version}
# Default: ""

use_semantic_release:
# Use semantic-release for automatic versioning and changelog generation
Expand All @@ -67,6 +68,13 @@ jobs:
# Required: false
# Default: true

working_directory:
# The working directory where the project is located (relative to the repository root).
#
# Type: string
# Required: false
# Default: ""

update_version_aliases:
# Automatically update version alias tags (e.g., v1 and v1.2) to point to the latest release.
#
Expand All @@ -90,6 +98,148 @@ jobs:
```
<!-- action-docs-usage source=".github/workflows/gh-release.yaml" project="dnd-it/github-workflows/.github/workflows/gh-release.yaml" version="v2" -->

# Example
# Examples

## Basic Release

```yaml
jobs:
release:
uses: dnd-it/github-workflows/.github/workflows/gh-release.yaml@v3
with:
use_semantic_release: true
update_version_aliases: true
```

This will create a release using semantic-release with conventional commits and update version alias tags (e.g., `v1`, `v1.2`).

## Monorepo / Subdirectory Releases

For monorepo setups where you want to release individual packages or components with separate versioning, use the `working_directory` input:

```yaml
jobs:
release-lambda:
uses: dnd-it/github-workflows/.github/workflows/gh-release.yaml@v3
with:
working_directory: packages/lambda
use_semantic_release: true
```

### Subdirectory Configuration

Create a `.releaserc.json` file in your subdirectory (e.g., `packages/lambda/.releaserc.json`) with a custom `tagFormat`:

```json
{
"branches": [
"main"
],
"tagFormat": "lambda-v${version}",
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
{ "type": "revert", "release": "patch" }
]
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits"
}
],
"@semantic-release/github"
]
}
```

**Key points:**

1. **Custom Tag Format**: Use a unique `tagFormat` (e.g., `lambda-v${version}`) to avoid conflicts with main repo tags
2. **No Changelog/Git Plugins**: Omit `@semantic-release/changelog` and `@semantic-release/git` plugins to avoid conflicts. Let the main repository handle the changelog.
3. **GitHub Releases Only**: Subdirectory releases create GitHub releases with proper release notes but don't commit back to the repo

### Multiple Packages Example

```yaml
jobs:
release-api:
uses: dnd-it/github-workflows/.github/workflows/gh-release.yaml@v3
with:
working_directory: packages/api
use_semantic_release: true

release-frontend:
uses: dnd-it/github-workflows/.github/workflows/gh-release.yaml@v3
with:
working_directory: packages/frontend
use_semantic_release: true
```

Each package will have its own:
- Version numbering (e.g., `api-v1.2.0`, `frontend-v2.1.0`)
- GitHub release with generated release notes
- Independent release cadence

## Manual Release

```yaml
jobs:
release:
uses: dnd-it/github-workflows/.github/workflows/gh-release.yaml@v3
with:
use_semantic_release: false
tag: v1.0.0
```

## Custom Tag Format (Override)

You can override the `tagFormat` from `.releaserc.json` using the workflow input:

```yaml
jobs:
release:
uses: dnd-it/github-workflows/.github/workflows/gh-release.yaml@v3
with:
tag_format: "release-v${version}"
use_semantic_release: true
```

**Note**: If you don't provide `tag_format` as a workflow input, the workflow will use the `tagFormat` defined in your `.releaserc.json` file.

# FAQ

## How does `working_directory` work?

The `working_directory` input tells semantic-release to run from a specific subdirectory. Semantic-release will:
- Look for `.releaserc.json` in that directory
- Use paths relative to that directory
- Analyze all repository commits (not filtered by directory)

## Why remove changelog and git plugins from subdirectory configs?

When multiple releases write to the same repository, having multiple semantic-release instances commit changes can cause conflicts. Best practice:
- **Main repo**: Uses `@semantic-release/changelog` and `@semantic-release/git` to maintain `/CHANGELOG.md`
- **Subdirectories**: Only use `@semantic-release/github` to create releases

## Can I filter commits by directory for subdirectory releases?

Semantic-release doesn't have built-in path filtering. All commits in the repository history will be analyzed for version bumps and included in release notes. Consider using:
- **Commit scopes**: Use conventional commit scopes (e.g., `feat(api):`, `fix(frontend):`) to organize release notes
- **Monorepo plugins**: Third-party plugins like `semantic-release-monorepo` can filter by path, but add complexity

## What happens on the first subdirectory release?

The first release in a subdirectory will:
- Start at version `1.0.0` (since no tags with that `tagFormat` exist)
- Include all repository commits in the release notes
- Create a GitHub release with tag like `mypackage-v1.0.0`

Subsequent releases will only include commits since the last release with that tag format.
47 changes: 47 additions & 0 deletions tests/lambda/python/.releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"branches": [
"main"
],
"tagFormat": "python-lambda-v${version}",
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{ "type": "feat", "release": "minor" },
{ "type": "fix", "release": "patch" },
{ "type": "perf", "release": "patch" },
{ "type": "revert", "release": "patch" },
{ "type": "docs", "release": false },
{ "type": "style", "release": false },
{ "type": "chore", "release": false },
{ "type": "refactor", "release": false },
{ "type": "test", "release": false },
{ "type": "build", "release": false },
{ "type": "ci", "release": false }
],
"parserOpts": {
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"]
}
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits",
"presetConfig": {
"types": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "perf", "section": "Performance Improvements" },
{ "type": "revert", "section": "Reverts" },
{ "type": "docs", "section": "Documentation", "hidden": false },
{ "type": "refactor", "section": "Code Refactoring", "hidden": false }
]
}
}
],
"@semantic-release/github"
]
}