Monorepo Guide
When you have many skills in one repository, you may want each skill to have its own version and release independently. This guide shows how to set up release-please for independent versioning.
When to Use This Approach
Section titled “When to Use This Approach”Use independent versioning when:
- You have 5+ skills in one repository
- Skills evolve at different paces
- You want to avoid announcing unchanged skills
- You need semantic versioning per skill
Repository Structure
Section titled “Repository Structure”Directorymy-skills/
Directoryblog-editor/
- SKILL.md
- CHANGELOG.md (auto-generated)
- version.txt
Directorycode-reviewer/
- SKILL.md
- CHANGELOG.md
- version.txt
Directory.github/
Directoryworkflows/
- release.yml
- validate.yml
- release-please-config.json
- .release-please-manifest.json
-
Create release-please-config.json
Configure each skill as a separate package:
{"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json","separate-pull-requests": true,"include-component-in-tag": true,"include-v-in-tag": true,"tag-separator": "/","packages": {"blog-editor": {"release-type": "simple","component": "blog-editor"},"code-reviewer": {"release-type": "simple","component": "code-reviewer"}}}Key settings:
tag-separator: "/"produces tags likeblog-editor/v1.0.0separate-pull-requests: truecreates one PR per skillrelease-type: "simple"managesversion.txtandCHANGELOG.md
-
Create .release-please-manifest.json
Track current versions:
{"blog-editor": "1.0.0","code-reviewer": "1.0.0"} -
Add version.txt to each skill
The “simple” release type requires a
version.txtfile in each skill directory:1.0.0 -
Create release.yml workflow
This single workflow handles both release-please and publishing. We combine them because releases created with
GITHUB_TOKENdon’t trigger separate workflows.name: Releaseon:push:branches: [main]permissions:contents: writepull-requests: writeid-token: writejobs:release-please:runs-on: ubuntu-latestoutputs:releases_created: ${{ steps.release.outputs.releases_created }}paths_released: ${{ steps.release.outputs.paths_released }}steps:- uses: googleapis/release-please-action@v4id: releasewith:config-file: release-please-config.jsonmanifest-file: .release-please-manifest.jsonpublish:needs: release-pleaseif: ${{ needs.release-please.outputs.releases_created == 'true' }}runs-on: ubuntu-lateststrategy:matrix:path: ${{ fromJSON(needs.release-please.outputs.paths_released) }}steps:- uses: actions/checkout@v4- name: Get release infoid: inforun: |SKILL="${{ matrix.path }}"VERSION=$(jq -r '.["${{ matrix.path }}"]' .release-please-manifest.json)TAG="${SKILL}/v${VERSION}"echo "skill=$SKILL" >> $GITHUB_OUTPUTecho "version=$VERSION" >> $GITHUB_OUTPUTecho "tag=$TAG" >> $GITHUB_OUTPUT- name: Sync version to SKILL.mdrun: |sed -i "s/version: .*/version: ${{ steps.info.outputs.version }}/" \"${{ steps.info.outputs.skill }}/SKILL.md"- name: Pack skillrun: |SKILL="${{ steps.info.outputs.skill }}"VERSION="${{ steps.info.outputs.version }}"zip -r "${SKILL}-${VERSION}.skill" "$SKILL" \-x "*.git*" -x "*node_modules*"- name: Upload to releaseenv:GH_TOKEN: ${{ github.token }}run: |gh release upload "${{ steps.info.outputs.tag }}" *.skill --clobber- name: Get OIDC tokenid: oidcrun: |TOKEN=$(curl -sS \-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://www.mpak.dev" \| jq -r '.value')echo "::add-mask::$TOKEN"echo "token=$TOKEN" >> $GITHUB_OUTPUT- name: Announce to registryrun: |SKILL="${{ steps.info.outputs.skill }}"VERSION="${{ steps.info.outputs.version }}"BUNDLE="${SKILL}-${VERSION}.skill"OWNER=$(echo "${{ github.repository }}" | cut -d'/' -f1 | tr '[:upper:]' '[:lower:]')SCOPED_NAME="@${OWNER}/${SKILL}"SKILL_JSON=$(awk '/^---$/{if(f)exit;f=1;next}f' "$SKILL/SKILL.md" | yq -o=json '.')SHA256=$(sha256sum "$BUNDLE" | cut -d' ' -f1)SIZE=$(stat -c%s "$BUNDLE")PAYLOAD=$(jq -n \--arg name "$SCOPED_NAME" \--arg version "$VERSION" \--argjson skill "$SKILL_JSON" \--arg release_tag "${{ steps.info.outputs.tag }}" \--arg filename "$BUNDLE" \--arg sha256 "$SHA256" \--argjson size "$SIZE" \'{name: $name,version: $version,skill: $skill,release_tag: $release_tag,prerelease: false,artifact: { filename: $filename, sha256: $sha256, size: $size }}')curl -X POST "https://registry.mpak.dev/v1/skills/announce" \-H "Content-Type: application/json" \-H "Authorization: Bearer ${{ steps.oidc.outputs.token }}" \-d "$PAYLOAD"
How It Works
Section titled “How It Works”Commit to Release Flow
Section titled “Commit to Release Flow”- Make changes to
blog-editor/SKILL.md - Commit with conventional commit:
feat(blog-editor): add tone detection - Push to main
- Release-please creates PR: “chore(blog-editor): release 1.1.0”
- Merge the PR
- Release-please creates GitHub release with tag
blog-editor/v1.1.0 - Publish job packs, uploads bundle, and announces to mpak.dev
Conventional Commits
Section titled “Conventional Commits”Use conventional commits with scope matching skill names:
| Commit | Version Bump |
|---|---|
fix(blog-editor): typo in prompt | 1.0.0 to 1.0.1 |
feat(blog-editor): add tone analysis | 1.0.0 to 1.1.0 |
feat(blog-editor)!: new output format | 1.0.0 to 2.0.0 |
Without scope, the commit applies to all changed files:
feat: improve validation across skillsTag Format
Section titled “Tag Format”Tags follow the pattern: {skill-name}/v{version}
Examples:
blog-editor/v1.0.0code-reviewer/v2.1.3skill-author/v1.5.0
Migrating from Shared Versioning
Section titled “Migrating from Shared Versioning”If you currently use shared versioning (one tag like v1.0.0 for all skills):
-
Note current version of all skills (e.g.,
1.0.7) -
Create config files with current versions in the manifest
-
Add version.txt to each skill directory with current version
-
Create the release.yml workflow as shown above
-
First release after migration uses existing versions
-
Subsequent changes bump versions independently
Keeping validate.yml
Section titled “Keeping validate.yml”You can keep a separate validation workflow that runs on PRs:
name: Validate Skills
on: push: branches: [main] pull_request: branches: [main]
jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: NimbleBrainInc/skill-pack@v1 with: build: false upload: false announce: falseThis validates all skills on every PR without publishing.
Troubleshooting
Section titled “Troubleshooting”Release PR not created
Section titled “Release PR not created”- Ensure commits use conventional commit format
- Check that the commit touches files in a skill directory
- Verify
release-please-config.jsonlists the skill
Wrong skill released
Section titled “Wrong skill released”- The tag determines which skill is published
- Verify the tag format matches
skill-name/vX.Y.Z
Version mismatch
Section titled “Version mismatch”version.txtis the source of truth for release-pleaseSKILL.mdmetadata.version is synced at publish time- If they drift, the publish workflow corrects it
Publish job not running
Section titled “Publish job not running”- The
releases_createdoutput must betrue - Check workflow logs to see which skills were detected
- Verify the skill is in
paths_releasedoutput