From 376d06200bc559aec4e82eb74d05759ee6aedd45 Mon Sep 17 00:00:00 2001 From: Samuel <15628653+swibrow@users.noreply.github.com> Date: Tue, 18 Nov 2025 06:32:50 +0100 Subject: [PATCH] feat: add working directory to release workflow --- .github/workflows/_test-gh.yaml | 47 ++++++++- .github/workflows/gh-release.yaml | 8 +- docs/workflows/gh-release.md | 158 +++++++++++++++++++++++++++- tests/lambda/python/.releaserc.json | 47 +++++++++ 4 files changed, 251 insertions(+), 9 deletions(-) create mode 100644 tests/lambda/python/.releaserc.json diff --git a/.github/workflows/_test-gh.yaml b/.github/workflows/_test-gh.yaml index fc4c1140..ea840898 100644 --- a/.github/workflows/_test-gh.yaml +++ b/.github/workflows/_test-gh.yaml @@ -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 @@ -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" diff --git a/.github/workflows/gh-release.yaml b/.github/workflows/gh-release.yaml index aa401e58..1356bf8c 100644 --- a/.github/workflows/gh-release.yaml +++ b/.github/workflows/gh-release.yaml @@ -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 @@ -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', diff --git a/docs/workflows/gh-release.md b/docs/workflows/gh-release.md index f44e8640..6b389353 100644 --- a/docs/workflows/gh-release.md +++ b/docs/workflows/gh-release.md @@ -16,8 +16,9 @@ This workflow creates a release based on the tag. | name | description | type | required | default | | --- | --- | --- | --- | --- | | `tag` |

The tag to release (for manual releases). If not provided, uses semantic-release for automatic versioning.

| `string` | `false` | `""` | -| `tag_format` |

The format of the tag to release (for manual releases).

| `string` | `false` | `v${version}` | +| `tag_format` |

The format of the tag to release. If not provided, uses the tagFormat from .releaserc.json or semantic-release default (v${version}).

| `string` | `false` | `""` | | `use_semantic_release` |

Use semantic-release for automatic versioning and changelog generation

| `boolean` | `false` | `true` | +| `working_directory` |

The working directory where the project is located (relative to the repository root).

| `string` | `false` | `""` | | `update_version_aliases` |

Automatically update version alias tags (e.g., v1 and v1.2) to point to the latest release.

| `boolean` | `false` | `false` | | `dry_run` |

Run in dry-run mode to preview the release without creating it.

| `boolean` | `false` | `false` | | `app_id` |

GitHub App ID (for generating a token if using GitHub App authentication)

| `string` | `false` | `""` | @@ -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 @@ -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. # @@ -90,6 +98,148 @@ jobs: ``` -# 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. diff --git a/tests/lambda/python/.releaserc.json b/tests/lambda/python/.releaserc.json new file mode 100644 index 00000000..65cbe380 --- /dev/null +++ b/tests/lambda/python/.releaserc.json @@ -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" + ] +}