Reusable workflow for TypeScript/Node.js semantic versioning and automated release management. Creates releases based on conventional commits with GPG signing, GitHub Packages authentication for private @lerianstudio dependencies, and monorepo support.
- Semantic versioning: Automatic version calculation from conventional commits
- GPG signing: Signed commits and tags for security
- GitHub App authentication: Higher rate limits and better security
- GitHub Packages support: Automatic
.npmrcconfiguration for private@lerianstudio/*dependencies - Clean package.json: Overwrites existing
package.jsonto avoid dependency resolution conflicts - Monorepo support: Detects changed paths and runs release for each changed app
- Branch strategy: Pre-configured for
main(production),develop(beta), andrelease-candidate(rc) - Dry-run mode: Test releases safely without creating tags or GitHub releases
- Backmerge support: Automatic backmerging of releases
- Slack notifications: Configurable release status notifications
The generic release.yml uses npm init -y which preserves the existing package.json. For TypeScript projects with private @lerianstudio/* dependencies, this causes npm install to attempt resolving all dependencies (including private ones), resulting in 401 Unauthorized errors during the release step.
typescript-release.yml solves this by:
- Configuring
.npmrcwith GitHub Packages authentication (whenGH_PAT_FOR_PACKAGESsecret is available) - Overwriting
package.jsonwith a minimal version that only contains what semantic-release needs - Removing
package-lock.jsonto prevent stale dependency resolution
name: Release
on:
push:
branches:
- develop
- release-candidate
- main
tags-ignore:
- '**'
paths-ignore:
- '**.md'
- 'docs/**'
- 'LICENSE'
- '.gitignore'
permissions:
contents: write
pull-requests: write
jobs:
release:
name: Create Release
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/typescript-release.yml@v1.0.0
secrets: inheritjobs:
release:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/typescript-release.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
node_version: "22"
secrets: inheritjobs:
release:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/typescript-release.yml@v1.0.0
with:
dry_run: true
secrets: inheritThis runs semantic-release without creating tags, GitHub releases, or pushing commits. Useful for validating the release configuration on a new branch or after workflow changes.
jobs:
release:
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/typescript-release.yml@v1.0.0
with:
filter_paths: |
apps/api
apps/worker
packages/shared
path_level: "2"
secrets: inheritname: Release Pipeline
on:
push:
branches: [develop, release-candidate, main]
tags-ignore: ['**']
paths-ignore:
- '**.md'
- 'docs/**'
- 'LICENSE'
permissions:
contents: write
pull-requests: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
release:
needs: test
uses: LerianStudio/github-actions-shared-workflows/.github/workflows/typescript-release.yml@v1.0.0
with:
runner_type: "blacksmith-4vcpu-ubuntu-2404"
node_version: "20"
secrets: inherit| Input | Type | Default | Required | Description |
|---|---|---|---|---|
semantic_version |
string | 23.0.8 |
No | Semantic release version to use |
runner_type |
string | blacksmith-4vcpu-ubuntu-2404 |
No | GitHub runner type |
node_version |
string | 20 |
No | Node.js version to use |
filter_paths |
string | '' |
No | Newline-separated list of path prefixes for monorepo filtering |
path_level |
string | 2 |
No | Limits the path to the first N segments (e.g., 2 → apps/agent) |
dry_run |
boolean | false |
No | Run semantic-release in dry-run mode (no tags/releases created) |
| Secret | Description |
|---|---|
LERIAN_STUDIO_MIDAZ_PUSH_BOT_APP_ID |
GitHub App ID for authentication |
LERIAN_STUDIO_MIDAZ_PUSH_BOT_PRIVATE_KEY |
GitHub App private key |
LERIAN_CI_CD_USER_GPG_KEY |
GPG private key for signing commits |
LERIAN_CI_CD_USER_GPG_KEY_PASSWORD |
GPG key passphrase |
LERIAN_CI_CD_USER_NAME |
Git committer name |
LERIAN_CI_CD_USER_EMAIL |
Git committer email |
SLACK_WEBHOOK_URL |
Slack webhook for notifications |
| Secret | Description |
|---|---|
GH_PAT_FOR_PACKAGES |
Personal Access Token with read:packages scope for GitHub Packages. When provided, configures .npmrc for @lerianstudio scoped packages. |
| Output | Description |
|---|---|
gpg_fingerprint |
GPG key fingerprint used for signing |
The workflow generates a .releaserc.json with pre-configured branch settings:
Commits to develop branch create beta pre-releases:
- Version:
v1.2.3-beta.1 - Pre-release: Yes
- Use case: Development testing
Commits to release-candidate branch create RC pre-releases:
- Version:
v1.2.3-rc.1 - Pre-release: Yes
- Use case: Staging/UAT testing
Commits to main branch create production releases:
- Version:
v1.2.3 - Pre-release: No
- Use case: Production deployment
Determines which apps need releases:
- Single app mode (default): When
filter_pathsis empty, releases from repository root - Monorepo mode: When
filter_pathsis provided, detects changed paths and builds a matrix
Also skips releases for [skip ci] and changelog update commits.
Runs semantic-release for each app in the matrix:
- Create GitHub App token
- Checkout repository with full history
- Sync with remote branch
- Import GPG key for commit signing
- Setup Node.js
- Configure
.npmrcfor GitHub Packages (conditional) - Initialize clean
package.jsonand.releaserc.json - Install semantic-release plugins
- Run semantic-release
Sends Slack notification with release status. Skipped when no changes are detected or when the commit triggers a skip.
| Plugin | Version | Description |
|---|---|---|
@semantic-release/exec |
7.1.0 |
Execute custom scripts during release |
conventional-changelog-conventionalcommits |
7.0.2 |
Conventional commits support |
@saithodev/semantic-release-backmerge |
4.0.1 |
Automatic backmerging of releases |
The workflow uses conventional commits to determine version bumps:
feat!: remove deprecated API endpoint
BREAKING CHANGE: The /api/v1/old endpoint has been removed
Version: 1.0.0 → 2.0.0
feat: add user authentication
Version: 1.0.0 → 1.1.0
fix: resolve memory leak in transaction processor
Version: 1.0.0 → 1.0.1
docs: update API documentation
chore: update dependencies
refactor: simplify authentication logic
No version bump, but included in changelog.
Issue: npm install fails with 401 when resolving @lerianstudio/* packages
Solutions:
- Ensure
GH_PAT_FOR_PACKAGESsecret is configured in the repository - Verify the PAT has
read:packagesscope - Check that the token has access to the
@lerianstudioorganization packages
Issue: Workflow runs but no release is created
Solutions:
- Check commit messages follow conventional commits format
- Verify you're pushing to a configured branch (
main,develop, orrelease-candidate) - Check if version already exists as a tag
- Review semantic-release logs in the workflow run
- Try running with
dry_run: trueto see what semantic-release would do
Issue: A production tag (e.g., v1.0.0) was created from the develop branch instead of a beta tag
Solutions:
- This workflow generates
.releaserc.jsonautomatically with correct branch config — ensure you're not overriding it with a local.releasercfile in your repository - Verify the workflow version you're using has the branch prerelease configuration
Issue: Cannot sign commits with GPG key
Solutions:
- Verify GPG key is valid and hasn't expired
- Check passphrase is correct
- Verify key format is ASCII armored
Issue: Workflow is skipped unexpectedly
Solutions:
- Check if commit message contains
[skip ci] - Check if commit message matches
chore(release): Update CHANGELOGs - For monorepo mode, verify files were changed in the configured
filter_paths
| Feature | release.yml |
typescript-release.yml |
|---|---|---|
.npmrc setup |
No | Yes (conditional on GH_PAT_FOR_PACKAGES) |
package.json init |
npm init -y (preserves existing) |
Clean overwrite (minimal JSON) |
| Node.js version | Hardcoded 20 |
Configurable via node_version input |
| Dry-run mode | Not available | Available via dry_run input |
.releaserc.json |
Not generated | Auto-generated with branch strategy |
| Plugin versions | Unpinned | Pinned for reproducibility |
- Release Workflow - Generic release workflow (Go/other languages)
- Go Release - Go-specific release with GoReleaser
- TypeScript CI - TypeScript continuous integration
Last Updated: 2026-03-04 Version: 1.0.0