From 0d3e40f8b36a946537f99f0e0b26980b520ffcc3 Mon Sep 17 00:00:00 2001 From: Lily Dayton Date: Wed, 25 Mar 2026 13:31:08 +0200 Subject: [PATCH 1/6] Add release workflow and skill Release workflow triggers on version tags, runs checks, transpiles parable.py and parable.js via Tongues, and publishes them as GitHub release assets. Skill automates the release PR process. --- .claude/skills/release/SKILL.md | 38 +++++++++++++++ .github/workflows/release.yml | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 .claude/skills/release/SKILL.md create mode 100644 .github/workflows/release.yml diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md new file mode 100644 index 00000000..5b9b21cf --- /dev/null +++ b/.claude/skills/release/SKILL.md @@ -0,0 +1,38 @@ +--- +name: release +description: Create a release PR with version bump and changelog +disable-model-invocation: true +argument-hint: [version] +--- + +Create a PR with branch name `release/v$ARGUMENTS` containing only these changes: + +1. Update version in `pyproject.toml` + +No other changes—no refactors, no fixes, no documentation updates. + +## Changelog + +Generate release notes from commits since the last tag (or all commits if no tags): +``` +git log $(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD --oneline +``` + +Focus on what matters to users: +- New features and capabilities +- Breaking changes or behavior changes +- Group all bug fixes as "Various bug fixes" (don't itemize) +- Omit internal refactors, test changes, and CI updates + +Put the changelog in the PR body. The workflow extracts it for the GitHub release. + +Run `just check` before pushing. PR title: `Release v$ARGUMENTS` + +## After merge + +Tag, push, and clean up: +``` +git checkout main && git pull && git tag v$ARGUMENTS && git push --tags && git push origin --delete release/v$ARGUMENTS +``` + +The tag triggers a workflow that creates the GitHub release with transpiled binaries (parable.py, parable.js). diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..fb50a0d2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,83 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT + + - name: Verify version matches pyproject.toml + run: | + toml_version=$(grep -oP 'version = "\K[^"]+' pyproject.toml) + if [ "$toml_version" != "${{ steps.version.outputs.version }}" ]; then + echo "Tag version (${{ steps.version.outputs.version }}) does not match pyproject.toml ($toml_version)" + exit 1 + fi + + - name: Get release notes from PR + id: notes + env: + GH_TOKEN: ${{ github.token }} + run: | + body=$(gh pr list --state merged --head "release/v${{ steps.version.outputs.version }}" --json body --jq '.[0].body // empty') + if [ -z "$body" ]; then + echo "No release PR found, using auto-generated notes" + echo "auto=true" >> $GITHUB_OUTPUT + else + echo "body<> $GITHUB_OUTPUT + echo "$body" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + fi + + - uses: astral-sh/setup-uv@v4 + + - uses: extractions/setup-just@v2 + + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Install Tongues + env: + TONGUES_VERSION: v0.2.4 + run: | + mkdir -p "$HOME/.local/lib/tongues/lib" "$HOME/.local/lib/tongues/bin" "$HOME/.local/bin" + curl -sSfL "https://github.com/ldayton/Tongues/releases/download/${TONGUES_VERSION}/tongues.js" \ + -o "$HOME/.local/lib/tongues/lib/tongues.js" + curl -sSfL "https://raw.githubusercontent.com/ldayton/Tongues/${TONGUES_VERSION}/tongues/bin/tongues.js" \ + -o "$HOME/.local/lib/tongues/bin/tongues.js" + printf '#!/bin/sh\nexec node %s/.local/lib/tongues/bin/tongues.js "$@"\n' "$HOME" > "$HOME/.local/bin/tongues" + chmod +x "$HOME/.local/bin/tongues" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Run checks + run: just check + + - name: Transpile release binaries + run: | + mkdir -p .out + tongues --target python -o .out/parable.py src/parable.py + tongues --target javascript -o .out/parable.js src/parable.py + echo 'module.exports = { parse, ParseError, MatchedPairError };' >> .out/parable.js + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + body: ${{ steps.notes.outputs.body }} + generate_release_notes: ${{ steps.notes.outputs.auto == 'true' }} + files: | + .out/parable.py + .out/parable.js From 9d3e6992af8c5d7d4ab9c003eaaa68bd1051b5d0 Mon Sep 17 00:00:00 2001 From: Lily Dayton Date: Wed, 25 Mar 2026 13:31:28 +0200 Subject: [PATCH 2/6] Fix skill argument-hint type --- .claude/skills/release/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index 5b9b21cf..8527febe 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -2,7 +2,7 @@ name: release description: Create a release PR with version bump and changelog disable-model-invocation: true -argument-hint: [version] +argument-hint: version --- Create a PR with branch name `release/v$ARGUMENTS` containing only these changes: From 976186ba2dbb4b7292e7485582097ac79b23e71a Mon Sep 17 00:00:00 2001 From: Lily Dayton Date: Wed, 25 Mar 2026 13:34:09 +0200 Subject: [PATCH 3/6] Fix release: transpile Python through Tongues, sync version check in skill --- .claude/skills/release/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index 8527febe..bc182d6c 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -8,6 +8,7 @@ argument-hint: version Create a PR with branch name `release/v$ARGUMENTS` containing only these changes: 1. Update version in `pyproject.toml` +2. Ensure `TONGUES_VERSION` matches between `.github/workflows/ci.yml` and `.github/workflows/release.yml` No other changes—no refactors, no fixes, no documentation updates. From b35fb0a8a4b9818f502793cdebead57a916e17d4 Mon Sep 17 00:00:00 2001 From: Lily Dayton Date: Wed, 25 Mar 2026 13:37:15 +0200 Subject: [PATCH 4/6] Release skill: track README version column and Tongues version sync --- .claude/skills/release/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index bc182d6c..fa9b8dc8 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -9,6 +9,7 @@ Create a PR with branch name `release/v$ARGUMENTS` containing only these changes 1. Update version in `pyproject.toml` 2. Ensure `TONGUES_VERSION` matches between `.github/workflows/ci.yml` and `.github/workflows/release.yml` +3. Add a new version column to the Releases table in `README.md` with download links (keep the previous version column) No other changes—no refactors, no fixes, no documentation updates. From 2db2ea0176e7b329f87937c539e3589ee8ea6906 Mon Sep 17 00:00:00 2001 From: Lily Dayton Date: Wed, 25 Mar 2026 13:37:54 +0200 Subject: [PATCH 5/6] README: add v0.1.0 download links to releases table --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d02df96c..b2b6f211 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ Parse bash exactly as bash does. One file, zero dependencies, in your language. All releases, even Python, run through the transpiler. Current languages: -| Language | Min Version | -| ---------- | ----------------- | -| Javascript | Node.js 21 | -| Python | CPython/Pypy 3.12 | +| Language | Min Version | v0.1.0 | +| ---------- | ----------------- | ------------------------------------------------------------------------------------ | +| Javascript | Node.js 21 | [parable.js](https://github.com/ldayton/Parable/releases/download/v0.1.0/parable.js) | +| Python | CPython/Pypy 3.12 | [parable.py](https://github.com/ldayton/Parable/releases/download/v0.1.0/parable.py) | Output code quality is a work in progress. Currently the transpiler prioritizes correctness over readability; generated code may not yet match hand-written idioms, yet. From 919e894902cb819dc47e594073a01f2ad1b1fa9f Mon Sep 17 00:00:00 2001 From: Lily Dayton Date: Wed, 25 Mar 2026 13:39:49 +0200 Subject: [PATCH 6/6] README: add caveats about output quality and fuzzer edge cases --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2b6f211..eedc819e 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ All releases, even Python, run through the transpiler. Current languages: | Javascript | Node.js 21 | [parable.js](https://github.com/ldayton/Parable/releases/download/v0.1.0/parable.js) | | Python | CPython/Pypy 3.12 | [parable.py](https://github.com/ldayton/Parable/releases/download/v0.1.0/parable.py) | -Output code quality is a work in progress. Currently the transpiler prioritizes correctness over readability; generated code may not yet match hand-written idioms, yet. +Caveats: output code quality is a work in progress. Currently the transpiler prioritizes correctness over readability; generated code may not yet match hand-written idioms, yet. Additionally, the project's fuzzer still finds obscure edge cases where Parable differs from GNU Bash. While Parable matches bash for essentially 100% of real world examples, we're still shaking out remaining theoretical differences. ## Why Parable?