From 9793762c788de74a61f5c5ad4ad6235c1d56cdb1 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:29:44 -0500 Subject: [PATCH 01/32] bump version to 1.0.0 in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ade00a1..21811ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@llm-exe/github-action", - "version": "0.0.0", + "version": "1.0.0", "private": true, "description": "GitHub Action for running llm-exe executors in workflows.", "main": "dist/index.js", From 91f446018c38817369234b6989a6f7f25f855606 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:29:56 -0500 Subject: [PATCH 02/32] add releasing guidelines to RELEASING.md --- RELEASING.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 RELEASING.md diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..e127aab --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,51 @@ +# Releasing + +This Action is released with the same draft-then-publish flow as the +[`llm-exe`](https://github.com/llm-exe/llm-exe) SDK. Consumers pin a floating +major (`uses: llm-exe/github-action@v1`) or minor (`@v1.4`) tag and automatically +get the latest patch; the immutable `vX.Y.Z` tag is also available for exact pins. + +## What ships + +A GitHub Action runs the committed `dist/index.js`, not `src/`. CI rebuilds the +bundle on every PR and fails if `dist/` drifts from source, so the bundle must be +built and committed before a release. Build it with `npm run build`. + +## Versioning + +`package.json` `version` is the single source of truth. The floating tags follow: + +- `v1.4.5` immutable, one per release, gets a GitHub Release. +- `v1.4` moves to the latest `v1.4.x`. +- `v1` moves to the latest `v1.x.x`. + +Pre-releases (`v2.0.0-beta.0`) get an immutable tag and a GitHub pre-release, but +never move the floating `v2` / `v2.0` tags. + +## Steps + +1. Open a PR into `main` that bumps `package.json` `version` to the next version + and includes a freshly built `dist/` (`npm run build`). +2. On the PR, two gates must pass: + - **CI / Tests** runs `npm run verify` on Node 20 and 22 and checks `dist/` is in sync. + - **Release / Check Semver** confirms `package.json` version is greater than the + latest stable `vX.Y.Z` tag. +3. Merge the PR. **Release / Create Draft** builds a draft GitHub Release tagged + `vX.Y.Z` with cleaned, auto-generated notes. +4. Review the draft release notes, then click **Publish**. +5. **Release / Publish** checks out the released tag, re-verifies `dist/`, and + re-points the floating `v1` and `v1.4` tags to the released commit. If anything + fails, the release is reverted to draft. + +## First release + +There are no `vX.Y.Z` tags yet (only a legacy `v1`), so the semver check treats the +latest stable version as `0.0.0`. Set `package.json` `version` to your first +release (for example `1.0.0`) in step 1. Publishing it creates `v1.0.0` and moves +the existing `v1` tag onto that commit. + +## Local fallback + +`npm run release -- v1.4.5` (see `scripts/release.sh`) builds, commits, tags, and +pushes from your machine. It does not create a GitHub Release or move the floating +tags, so prefer the workflow flow above for normal releases. From f81ed7454fb0c8a4c2dd123834549b0569ac9f1a Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:30:13 -0500 Subject: [PATCH 03/32] add auto-merge workflow for pull requests from development to main --- .github/workflows/auto-merge-main-pr.yml | 88 ++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/auto-merge-main-pr.yml diff --git a/.github/workflows/auto-merge-main-pr.yml b/.github/workflows/auto-merge-main-pr.yml new file mode 100644 index 0000000..9c5cd51 --- /dev/null +++ b/.github/workflows/auto-merge-main-pr.yml @@ -0,0 +1,88 @@ +name: Release / Auto Merge + +on: + workflow_run: + workflows: + - "Release / Check Semver" + types: + - completed + pull_request: + types: + - ready_for_review + - synchronize + branches: + - main + +permissions: + id-token: write + checks: write + contents: write + pull-requests: write + actions: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + auto-merge: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'development' || github.event_name == 'pull_request' }} + + steps: + - uses: actions/checkout@v4 + + - name: Generate bot token + id: bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Get PR number for development to main + run: | + PR_INFO=$(gh pr list --base main --head development --state open --json number,isDraft --jq '.[] | select(.isDraft == false) | .number') + echo "PR_NUMBER=$PR_INFO" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Wait for all checks to complete + if: env.PR_NUMBER != '' + run: | + MAX_ATTEMPTS=10 + ATTEMPT=0 + while [[ $ATTEMPT -lt $MAX_ATTEMPTS ]]; do + CHECKS_IN_PROGRESS=$(gh pr checks ${{ env.PR_NUMBER }} --json name,state --jq '[.[] | select(.name != "auto-merge") | select(.state == "IN_PROGRESS")] | length') + if [[ "$CHECKS_IN_PROGRESS" -eq "0" ]]; then + echo "All checks complete." + break + fi + echo "Checks still in progress... attempt $ATTEMPT/$MAX_ATTEMPTS" + ATTEMPT=$((ATTEMPT + 1)) + sleep 30 + done + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Verify all checks passed + if: env.PR_NUMBER != '' + run: | + FAILED_CHECKS=$(gh pr checks ${{ env.PR_NUMBER }} --json name,state --jq '[.[] | select(.name != "auto-merge") | select(.state == "FAILURE")] | length') + if [[ "$FAILED_CHECKS" -gt "0" ]]; then + echo "Some checks failed — aborting auto-merge." + exit 1 + fi + echo "All checks passed." + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Merge PR to main + if: env.PR_NUMBER != '' + run: | + gh pr merge ${{ env.PR_NUMBER }} --merge --admin --repo ${{ github.repository }} + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} From 3b0403c0ba2a811d0e8fc29a0822794ea5230d5c Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:31:17 -0500 Subject: [PATCH 04/32] add semantic versioning check workflow for pull requests --- .../workflows/check-semantic-versioning.yml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/check-semantic-versioning.yml diff --git a/.github/workflows/check-semantic-versioning.yml b/.github/workflows/check-semantic-versioning.yml new file mode 100644 index 0000000..42e5649 --- /dev/null +++ b/.github/workflows/check-semantic-versioning.yml @@ -0,0 +1,61 @@ +name: Release / Check Semver + +on: + pull_request: + branches: + - main + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + enforce-semantic-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Fetch all tags + run: git fetch --tags + + - name: Get latest stable release tag + id: get_latest_tag + run: | + LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) + if [ -z "$LATEST_TAG" ]; then + LATEST_TAG="v0.0.0" + fi + # Remove the "v" prefix + LATEST_TAG=${LATEST_TAG#v} + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + + - name: Get version from package.json + id: get_package_version + run: | + PACKAGE_VERSION=$(jq -r '.version' package.json) + echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV + + - name: Compare versions + run: | + LATEST_TAG=${{ env.LATEST_TAG }} + PACKAGE_VERSION=${{ env.PACKAGE_VERSION }} + + # Convert versions to comparable numbers + convert_version() { + echo "$1" | awk -F. '{ printf("%d%03d%03d", $1,$2,$3); }' + } + + LATEST_TAG_NUM=$(convert_version $LATEST_TAG) + PACKAGE_VERSION_NUM=$(convert_version $PACKAGE_VERSION) + + if [ "$PACKAGE_VERSION_NUM" -le "$LATEST_TAG_NUM" ]; then + echo "Version in package.json ($PACKAGE_VERSION) is not greater than the latest release tag ($LATEST_TAG), please update it to match the next release." + exit 1 + fi + + - name: Success message + if: success() + run: echo "Version in package.json ($PACKAGE_VERSION) is greater than the latest release tag ($LATEST_TAG). Ready to merge to main!" From 81b5d1dd93f982d4b5c816c47e6fff84718ea876 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:31:25 -0500 Subject: [PATCH 05/32] add workflow to create and manage draft releases on GitHub --- .github/workflows/create-draft-release.yml | 121 +++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 .github/workflows/create-draft-release.yml diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml new file mode 100644 index 0000000..33d5627 --- /dev/null +++ b/.github/workflows/create-draft-release.yml @@ -0,0 +1,121 @@ +name: Release / Create Draft + +on: + workflow_dispatch: + pull_request: + types: + - closed + branches: + - main + +permissions: + contents: write + +jobs: + update-draft-releases: + name: "Delete old drafts and create new draft release" + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' + + steps: + - uses: actions/checkout@v4 + + - name: Get all draft releases + id: get_drafts + run: | + gh api repos/${{ github.repository }}/releases \ + | jq '.[] | select(.draft == true) | .id' > release_ids.txt + + - name: Delete old draft releases + run: | + while read release_id; do + echo "Deleting draft release with ID: $release_id" + gh api -X DELETE repos/${{ github.repository }}/releases/$release_id + done < release_ids.txt + + - name: Determine next semantic version for release + run: | + PACKAGE_VERSION=$(jq -r '.version' package.json) + if [ -z "$PACKAGE_VERSION" ] || [ "$PACKAGE_VERSION" = "null" ]; then + echo "No version found in package.json" + exit 1 + fi + + # Validate MAJOR.MINOR.PATCH with optional semver pre-release suffix (e.g. -beta.0, -rc.1) + if [[ ! $PACKAGE_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version format ($PACKAGE_VERSION). Expected MAJOR.MINOR.PATCH or MAJOR.MINOR.PATCH-PRERELEASE" + exit 1 + fi + + # Preserve the full version (including any -beta.X / -rc.X suffix) in the tag + NEW_VERSION="v${PACKAGE_VERSION}" + echo "NEW_VERSION=${NEW_VERSION}" >> $GITHUB_ENV + + # A pre-release suffix (anything after a "-") makes this a GitHub pre-release + if [[ "$PACKAGE_VERSION" == *-* ]]; then + echo "IS_PRERELEASE=true" >> $GITHUB_ENV + else + echo "IS_PRERELEASE=false" >> $GITHUB_ENV + fi + + - name: Create Draft Release on GitHub + id: create_release + run: | + response=$(curl -s -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + https://api.github.com/repos/${{ github.repository }}/releases \ + -d '{ + "tag_name": "'"${NEW_VERSION}"'", + "target_commitish": "main", + "draft": true, + "prerelease": '"${IS_PRERELEASE}"', + "make_latest": "'"$([ "${IS_PRERELEASE}" = "true" ] && echo false || echo legacy)"'", + "generate_release_notes": true + }') + + # Check if release creation succeeded + if echo "$response" | jq -e '.id' > /dev/null; then + release_url=$(echo $response | jq -r '.html_url') + release_id=$(echo $response | jq -r '.id') + body=$(echo "$response" | jq -r '.body') + + echo "release_url=${release_url}" >> $GITHUB_OUTPUT + echo "release_id=${release_id}" >> $GITHUB_OUTPUT + echo "$body" > release_body.txt + + echo "Draft release created: ${release_url}" + else + echo "Failed to create draft release" + echo "$response" | jq '.' + exit 1 + fi + + - name: Clean up release notes + id: clean_release_notes + run: | + # Remove automation-related commits (chore: bump version, Draft PR for release, Bump Version on PR to Main, docs: sync) and remove "by @username in" from PR references + CLEANED_BODY=$(sed '/chore: bump version/Id; /Draft PR for release/Id; /Bump Version on PR to Main/Id; /docs: sync/Id' release_body.txt | sed -E 's/ by @[^ ]+ in/ /g') + echo "$CLEANED_BODY" > cleaned_body.txt + echo "Cleaned release notes:" + cat cleaned_body.txt + + - name: Update Draft Release with cleaned notes + run: | + CLEANED_BODY=$(jq -Rs '.' < cleaned_body.txt) + response=$(curl -s -X PATCH \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.release_id }} \ + -d "{\"tag_name\": \"${NEW_VERSION}\", \"target_commitish\": \"main\", \"body\": ${CLEANED_BODY}}") + + if echo "$response" | jq -e '.id' > /dev/null; then + echo "Release notes updated successfully" + echo "Draft release ready: ${{ steps.create_release.outputs.release_url }}" + else + echo "Warning: Failed to update release notes" + echo "$response" | jq '.' + fi From 040b55b7e33335549f177232bd9b177d9740d954 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:31:33 -0500 Subject: [PATCH 06/32] add workflow for managing draft pull requests and version bumping --- .github/workflows/draft-main-pr.yml | 253 ++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 .github/workflows/draft-main-pr.yml diff --git a/.github/workflows/draft-main-pr.yml b/.github/workflows/draft-main-pr.yml new file mode 100644 index 0000000..739c143 --- /dev/null +++ b/.github/workflows/draft-main-pr.yml @@ -0,0 +1,253 @@ +name: Release / Draft PR + +on: + pull_request: + types: + - closed + branches: + - development + + release: + types: [published] + +permissions: + id-token: write + contents: write + pull-requests: write + +jobs: + draft-dev-to-main-pr: + if: ${{ github.event_name == 'release' || !(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'development' && github.event.pull_request.head.ref == 'bump-version-branch') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate bot token + id: bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Get the latest release version + id: latest_release + run: | + LATEST_VERSION=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) + if [ -z "$LATEST_VERSION" ]; then + LATEST_VERSION="v0.0.0" + fi + LATEST_VERSION=${LATEST_VERSION#v} + echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_ENV + + - name: Get package.json version + run: | + package_version=$(jq -r '.version' < package.json) + echo "Version in package.json: $package_version" + echo "PACKAGE_VERSION=$package_version" >> $GITHUB_ENV + + - name: Check version comparison + run: | + LATEST_VERSION=${{ env.LATEST_VERSION }} + PACKAGE_VERSION=${{ env.PACKAGE_VERSION }} + + if [[ "$PACKAGE_VERSION" == *-* ]]; then + echo "Pre-release version detected ($PACKAGE_VERSION) — skipping auto-bump." + echo "CURRENT_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV + echo "BUMP_VERSION=false" >> $GITHUB_ENV + echo "IS_PRERELEASE=true" >> $GITHUB_ENV + exit 0 + fi + echo "IS_PRERELEASE=false" >> $GITHUB_ENV + + convert_version() { + echo "$1" | awk -F. '{ printf("%d%03d%03d", $1,$2,$3); }' + } + + LATEST_VERSION_NUM=$(convert_version $LATEST_VERSION) + PACKAGE_VERSION_NUM=$(convert_version $PACKAGE_VERSION) + + if [ "$PACKAGE_VERSION_NUM" -le "$LATEST_VERSION_NUM" ]; then + echo "Version in package.json ($PACKAGE_VERSION) is not greater than latest tag ($LATEST_VERSION)." + echo "CURRENT_VERSION=$LATEST_VERSION" >> $GITHUB_ENV + echo "BUMP_VERSION=true" >> $GITHUB_ENV + else + echo "Version in package.json ($PACKAGE_VERSION) is greater than latest tag ($LATEST_VERSION)." + echo "CURRENT_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV + echo "BUMP_VERSION=false" >> $GITHUB_ENV + fi + + - name: Determine next semantic version + run: | + LATEST_TAG=${{ env.CURRENT_VERSION }} + if [ -z "$LATEST_TAG" ]; then + LATEST_TAG="v${{ env.PACKAGE_VERSION }}" + fi + + if [ "${{ env.IS_PRERELEASE }}" = "true" ]; then + echo "NEW_VERSION=v${LATEST_TAG}" >> $GITHUB_ENV + exit 0 + fi + + if [[ $LATEST_TAG =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(.*)$ ]]; then + MAJOR=${BASH_REMATCH[1]} + MINOR=${BASH_REMATCH[2]} + PATCH=${BASH_REMATCH[3]} + else + echo "Invalid version format. Expected format: MAJOR.MINOR.PATCH" + exit 1 + fi + + if [ "${{ env.BUMP_VERSION }}" == 'false' ]; then + PATCH=$PATCH + else + PATCH=$((PATCH + 1)) + fi + echo "NEW_VERSION=v${MAJOR}.${MINOR}.${PATCH}" >> $GITHUB_ENV + + - name: Check if PR already exists + run: | + PR_URL=$(gh pr list --base main --head development --json url --jq '.[0].url') + if [ -z "$PR_URL" ]; then + echo "PR_EXISTS=false" >> $GITHUB_ENV + else + echo "PR_EXISTS=true" >> $GITHUB_ENV + echo "PR_URL=$PR_URL" >> $GITHUB_ENV + fi + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Get merged PR titles for release + run: | + echo "Fetching branches..." + git fetch origin main development + + MERGE_BASE=$(git merge-base origin/main origin/development) + echo "Merge base: $MERGE_BASE" + + PR_NUMBERS=$(git log $MERGE_BASE..origin/development --merges --pretty=format:"%s" | grep -oP 'Merge pull request #\K[0-9]+') || true + + PR_TITLES="" + if [ -n "$PR_NUMBERS" ]; then + while IFS= read -r pr_num; do + if [ -n "$pr_num" ]; then + if pr_info=$(gh pr view "$pr_num" --json number,title --jq '"- #\(.number): \(.title)"' 2>/dev/null); then + PR_TITLES="${PR_TITLES}${pr_info}\n" + fi + fi + done <<< "$PR_NUMBERS" + fi + + if [ -n "$PR_TITLES" ]; then + PR_COUNT=$(echo -e "$PR_TITLES" | grep -c "^- #" || echo "0") + else + PR_COUNT=0 + fi + + echo "PR_TITLES<> $GITHUB_ENV + echo -e "$PR_TITLES" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + echo "PR_COUNT=$PR_COUNT" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Check for new commits between branches + run: | + if git rev-list --count origin/main..origin/development | grep -q '^0$'; then + echo "No new commits to create a PR." + echo "NEW_COMMITS=false" >> $GITHUB_ENV + else + echo "NEW_COMMITS=true" >> $GITHUB_ENV + fi + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Bump version number (patch increment) + if: env.BUMP_VERSION == 'true' && env.NEW_COMMITS == 'true' + run: | + LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) + if [ -z "$LATEST_TAG" ]; then + LATEST_TAG="v0.0.0" + fi + + if [[ $LATEST_TAG =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(.*)$ ]]; then + MAJOR=${BASH_REMATCH[1]} + MINOR=${BASH_REMATCH[2]} + PATCH=${BASH_REMATCH[3]} + else + echo "Invalid version format." + exit 1 + fi + PATCH=$((PATCH + 1)) + NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}" + + jq --arg v "$NEW_VERSION" '.version = $v' package.json > package.tmp && mv package.tmp package.json + echo "Updated version in package.json to $NEW_VERSION" + + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add package.json + git commit -m "chore: bump version number on PR to main" + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Create or update bump-version branch + if: env.BUMP_VERSION == 'true' && env.NEW_COMMITS == 'true' + run: | + git checkout -b bump-version-branch || git checkout bump-version-branch + git pull origin bump-version-branch || echo "Branch does not exist remotely." + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Push bump-version branch + if: env.BUMP_VERSION == 'true' && env.NEW_COMMITS == 'true' + run: | + git push origin bump-version-branch || (echo "Retrying with force push..." && git push --force origin bump-version-branch) + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Create bump-version PR and auto-merge + if: env.BUMP_VERSION == 'true' && env.NEW_COMMITS == 'true' + run: | + gh pr create --title "Bump Version on PR to Main" --body "This PR bumps the version number" --base development --head bump-version-branch + PR_NUMBER=$(gh pr list --state open --head bump-version-branch --json number --jq '.[0].number') + gh pr merge $PR_NUMBER --admin --squash --delete-branch + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Create or update draft PR to main + if: env.NEW_COMMITS == 'true' + run: | + if [ "$PR_COUNT" -gt 0 ]; then + PR_BODY=$'## Changes in this release:\n\n'"${PR_TITLES}"$'\n\nThis release includes '"${PR_COUNT}"$' merged pull request(s) from the development branch.' + else + PR_BODY=$'## Changes in this release:\n\nThis release includes changes from the development branch.' + fi + + if [ ${#PR_BODY} -gt 65000 ]; then + PR_BODY="${PR_BODY:0:65000}"$'\n\n... (truncated due to length)' + fi + + if [ "$PR_EXISTS" = "true" ]; then + echo "Updating existing PR: $PR_URL" + gh pr edit "$PR_URL" --title "Draft PR for release version $NEW_VERSION" --body "$PR_BODY" + else + echo "Creating new draft PR..." + gh pr create \ + --base main \ + --head development \ + --title "Draft PR for release version $NEW_VERSION" \ + --body "$PR_BODY" \ + --draft + fi + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} From ddd44af4ae4c0dfd7c8809898a61634af5c2660b Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:31:39 -0500 Subject: [PATCH 07/32] add workflow for release publishing with verification and draft reversion --- .github/workflows/publish-release.yml | 153 ++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 .github/workflows/publish-release.yml diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..60226dc --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,153 @@ +name: Release / Publish + +on: + workflow_dispatch: + release: + types: + - published + +permissions: + contents: write + +jobs: + check-release-branch: + runs-on: ubuntu-latest + steps: + - name: Check if release is from main branch + if: github.event_name == 'release' && github.event.action == 'published' + run: | + BRANCH=$(jq -r .release.target_commitish "$GITHUB_EVENT_PATH") + if [ "$BRANCH" != "main" ]; then + echo "Releases should only be created from the main branch. This release is from $BRANCH." + exit 1 + fi + + publish-action: + name: Verify dist and promote floating tags + if: github.event_name == 'workflow_dispatch' || github.event_name == 'release' + needs: check-release-branch + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve release tag and prerelease flag + id: meta + run: | + if [ "${{ github.event_name }}" = "release" ]; then + TAG="${{ github.event.release.tag_name }}" + PRE="${{ github.event.release.prerelease }}" + else + TAG=$(gh release view --json tagName --jq .tagName) + PRE=$(gh release view --json isPrerelease --jq .isPrerelease) + fi + if [ -z "$TAG" ] || [ "$TAG" = "null" ]; then + echo "Could not resolve a release tag." + exit 1 + fi + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "prerelease=$PRE" >> $GITHUB_OUTPUT + echo "Resolved tag=$TAG prerelease=$PRE" + + - name: Checkout released tag + run: git checkout --detach "${{ steps.meta.outputs.tag }}" + + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Verify dist matches source at released tag + run: | + npm run build + if ! git diff --quiet -- dist/; then + echo "::error::dist/ at ${{ steps.meta.outputs.tag }} does not match a clean build from src/." + git --no-pager diff --stat -- dist/ + exit 1 + fi + echo "dist/ verified for ${{ steps.meta.outputs.tag }}." + + - name: Promote floating major/minor tags + if: steps.meta.outputs.prerelease != 'true' + run: | + set -euo pipefail + TAG="${{ steps.meta.outputs.tag }}" + SHA=$(git rev-parse HEAD) + VER="${TAG#v}" + + # Only stable MAJOR.MINOR.PATCH releases move the floating tags. Pre-releases + # (e.g. v2.0.0-beta.0) keep their immutable tag but must never hijack @v2. + if [[ ! "$VER" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Non-stable version $TAG: leaving floating tags unchanged." + exit 0 + fi + + MAJOR="${VER%%.*}" + REST="${VER#*.}" + MINOR="${REST%%.*}" + + move_ref() { + local ref="$1" + if gh api "repos/$REPO/git/refs/tags/$ref" >/dev/null 2>&1; then + gh api -X PATCH "repos/$REPO/git/refs/tags/$ref" -f sha="$SHA" -F force=true >/dev/null + echo "Moved $ref -> $SHA" + else + gh api -X POST "repos/$REPO/git/refs" -f ref="refs/tags/$ref" -f sha="$SHA" >/dev/null + echo "Created $ref -> $SHA" + fi + } + + move_ref "v$MAJOR" + move_ref "v$MAJOR.$MINOR" + echo "Consumers on @v$MAJOR and @v$MAJOR.$MINOR now resolve to $TAG." + + - name: Prerelease note + if: steps.meta.outputs.prerelease == 'true' + run: echo "Prerelease ${{ steps.meta.outputs.tag }} published; floating tags left unchanged." + + # Reverts the GitHub release back to draft if dist verification or tag promotion + # fails, so a broken release is never left published. + revert-to-draft: + if: always() && github.event_name == 'release' && needs.publish-action.result == 'failure' + needs: [publish-action] + name: Revert Release to Draft + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Revert release to draft + run: | + RELEASE_ID=$(jq -r .release.id "$GITHUB_EVENT_PATH") + ORIGINAL_BODY=$(jq -r .release.body "$GITHUB_EVENT_PATH") + RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + + { + echo "> [!WARNING]" + echo "> **Release Publishing Failed**" + echo "> This release was automatically reverted to draft because dist verification or tag promotion failed." + echo "> Review the [workflow logs](${RUN_URL}) and fix the issue before publishing again." + echo "---" + echo "$ORIGINAL_BODY" + } > release_body.txt + + BODY_JSON=$(jq -Rs '.' < release_body.txt) + response=$(curl -s -X PATCH \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${GH_TOKEN}" \ + https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID \ + -d "{\"draft\": true, \"body\": ${BODY_JSON}}") + if echo "$response" | jq -e '.id' > /dev/null; then + echo "Release reverted to draft with original notes preserved" + else + echo "Warning: Failed to revert release" + echo "$response" | jq '.' + exit 1 + fi From f928cd1c2f8aee2600d9e78408761984b8523723 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:31:56 -0500 Subject: [PATCH 08/32] add CI workflow for running tests and verifying distribution consistency --- .github/workflows/tests.yml | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..2dec65f --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,68 @@ +name: CI / Tests + +on: + workflow_dispatch: + pull_request: + branches: ["main", "development"] + push: + branches: ["main"] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [20.x, 22.x] + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Verify (format, typecheck, test) + run: npm run verify + + dist: + name: Verify dist is in sync with source + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # The Action runs the committed dist/index.js, not src/. This job rebuilds the + # bundle and fails if the committed output drifts from source, so a release can + # never ship a stale bundle. + - name: Use Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Rebuild bundle + run: npm run build + + - name: Fail if committed dist/ is stale + run: | + if ! git diff --quiet -- dist/; then + echo "::error::dist/ is out of date with src/. Run 'npm run build' and commit the result." + git --no-pager diff --stat -- dist/ + exit 1 + fi + echo "dist/ matches a clean build from src/." From 99eda5660f90e0c1ece6e113c144ab5829e831a2 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:35:51 -0500 Subject: [PATCH 09/32] add workflow for automated PR review and approval process --- .github/workflows/agent-review-pr.yml | 194 ++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 .github/workflows/agent-review-pr.yml diff --git a/.github/workflows/agent-review-pr.yml b/.github/workflows/agent-review-pr.yml new file mode 100644 index 0000000..d346923 --- /dev/null +++ b/.github/workflows/agent-review-pr.yml @@ -0,0 +1,194 @@ +name: Agent / Review PR + +on: + pull_request: + types: [opened, synchronize] + branches: [main, development] + + workflow_dispatch: + inputs: + pr_number: + description: "PR number to review" + required: true + type: string + base_ref: + description: "Base branch of the PR (e.g. development)" + required: true + type: string + head_ref: + description: "Head branch of the PR" + required: true + type: string + +permissions: + contents: read + pull-requests: write + issues: write + id-token: write + +jobs: + tests: + if: github.base_ref == 'development' || (github.event_name == 'workflow_dispatch' && inputs.base_ref == 'development') + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + node-version: [20.x, 22.x] + + steps: + - uses: actions/checkout@v4 + + - name: Checkout PR (workflow_dispatch) + if: github.event_name == 'workflow_dispatch' + run: gh pr checkout ${{ inputs.pr_number }} + env: + GH_TOKEN: ${{ github.token }} + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Verify (format, typecheck, test) + run: npm run verify + + review: + if: (github.base_ref == 'development' && github.event.action == 'opened') || (github.event_name == 'workflow_dispatch' && inputs.base_ref == 'development') + runs-on: ubuntu-latest + timeout-minutes: 15 + outputs: + verdict: ${{ steps.verdict.outputs.verdict }} + + steps: + - name: Generate review bot token + id: review-bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.LLM_EXE_REVIEW_BOT_APP_ID }} + private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }} + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.review-bot-token.outputs.token }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Review PR + env: + PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} + HEAD_REF: ${{ github.event.pull_request.head.ref || inputs.head_ref }} + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + github_token: ${{ steps.review-bot-token.outputs.token }} + display_report: "false" + allowed_bots: "llm-exe-bot[bot]" + prompt: | + You are a senior engineer reviewing PR #${{ env.PR_NUMBER }} on the llm-exe GitHub Action repository. + + This repo is a GitHub Action wrapper around the llm-exe SDK. It runs committed dist/index.js + (not src/) so every code change must be accompanied by a rebuilt dist/. The action exposes + inputs/outputs defined in action.yml and is consumed by other GitHub workflows via + `uses: llm-exe/github-action@v1`. + + ## Your task + + 1. Read CLAUDE.md for project context. + 2. Fetch the PR diff: `gh pr diff ${{ env.PR_NUMBER }}` + 3. Read any changed source files in full. + 4. Check that dist/ was rebuilt if src/ changed: look for dist/ changes alongside src/ changes. + 5. Review for: correctness, security, type safety, action.yml contract changes, and dist freshness. + + ## Verdict + + After your review, post a PR comment with your findings. Then write your verdict to + /tmp/review-verdict.txt — one word only: + - `approve` — code is correct, dist is fresh (if src changed), no blocking issues + - `request-changes` — there are issues that must be fixed before merging + - `comment` — minor notes only, no blocking issues but not ready to formally approve + + If the branch name starts with `agent/` note that this is bot-authored code — hold it + to the same standards but expect mechanical patterns. + claude_args: | + --allowedTools "Bash,Read,Glob,Grep,WebFetch" + --max-turns 30 + --model ${{ vars.ANTHROPIC_OPUS_LATEST || 'claude-opus-4-6' }} + + - name: Read verdict + id: verdict + if: always() + run: | + if [ -f /tmp/review-verdict.txt ]; then + v=$(cat /tmp/review-verdict.txt | tr -d '[:space:]') + else + v="no-verdict" + fi + echo "verdict=$v" >> "$GITHUB_OUTPUT" + + decide: + needs: [tests, review] + if: always() && (github.base_ref == 'development' || (github.event_name == 'workflow_dispatch' && inputs.base_ref == 'development')) + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Generate review bot token + id: review-bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.LLM_EXE_REVIEW_BOT_APP_ID }} + private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }} + + - name: Generate bot token + id: bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Approve or skip + env: + GH_TOKEN: ${{ steps.review-bot-token.outputs.token }} + BOT_TOKEN: ${{ steps.bot-token.outputs.token }} + PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} + HEAD_REF: ${{ github.event.pull_request.head.ref || inputs.head_ref }} + REPOSITORY: ${{ github.repository }} + run: | + verdict="${{ needs.review.outputs.verdict }}" + tests_result="${{ needs.tests.result }}" + + echo "Review verdict : $verdict" + echo "Tests result : $tests_result" + + if [ "$verdict" = "approve" ] && [ "$tests_result" = "success" ]; then + gh pr review "$PR_NUMBER" \ + --approve \ + --body "Approved by reviewer agent (tests passing)." \ + --repo "$REPOSITORY" + + if echo "$HEAD_REF" | grep -q '^agent/'; then + IS_DRAFT=$(gh pr view "$PR_NUMBER" \ + --json isDraft --jq '.isDraft' \ + --repo "$REPOSITORY") + if [ "$IS_DRAFT" = "true" ]; then + GH_TOKEN="$BOT_TOKEN" gh pr ready "$PR_NUMBER" --repo "$REPOSITORY" + echo "PR marked as ready for review." + fi + fi + + echo "PR #$PR_NUMBER approved." + else + echo "No approval submitted: verdict='$verdict', tests='$tests_result'." + fi From cc02a26698dcdd8d42b5e7b40b850ba31f65cfa9 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 15:36:07 -0500 Subject: [PATCH 10/32] add workflow for bot response to issue comments --- .github/workflows/bot-respond.yml | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 .github/workflows/bot-respond.yml diff --git a/.github/workflows/bot-respond.yml b/.github/workflows/bot-respond.yml new file mode 100644 index 0000000..9cfbd1f --- /dev/null +++ b/.github/workflows/bot-respond.yml @@ -0,0 +1,114 @@ +name: Agent / Bot Respond + +on: + issue_comment: + types: [created] + +permissions: + contents: write + issues: write + pull-requests: write + id-token: write + +jobs: + respond: + if: | + contains(github.event.comment.body, '@llm-exe-bot') && + github.event.comment.user.login != 'llm-exe-bot[bot]' && + ( + github.event.comment.author_association == 'OWNER' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'COLLABORATOR' + ) + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Generate bot token + id: bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Configure git + run: | + git config --global user.name "llm-exe-bot[bot]" + git config --global user.email "${{ secrets.APP_ID }}+llm-exe-bot[bot]@users.noreply.github.com" + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ steps.bot-token.outputs.token }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Respond + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + github_token: ${{ steps.bot-token.outputs.token }} + prompt: | + You are llm-exe-bot, a helpful assistant for the llm-exe GitHub Action repository. + You've been mentioned in a GitHub issue or PR comment by a maintainer or collaborator. + + Read CLAUDE.md for project context. This repo is a GitHub Action wrapper around the + llm-exe SDK. It runs committed dist/index.js (not src/) so code changes must include + a rebuilt dist/. + + ## Determine what's being asked + + Read the comment that mentioned you carefully. Decide which of the following applies: + + ### 1. PR review requested + If the maintainer wants you to review a pull request — any phrasing like "review this", + "re-review", "take another look", "check the PR", "can you review", etc. — AND the + comment is on a pull request: + - Fetch the PR's base and head branch: + `gh pr view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json baseRefName,headRefName` + - Dispatch the review pipeline: + `gh workflow run agent-review-pr.yml \ + --repo ${{ github.repository }} \ + -f pr_number="${{ github.event.issue.number }}" \ + -f base_ref="" \ + -f head_ref=""` + - Post a brief acknowledgment, e.g.: + "Review pipeline started — tests + agent review + approval will run shortly." + - Stop here. Do not also do a manual review. + + ### 2. Answer questions (read-only) + If the maintainer is asking a question or wants your opinion on something: + - Read any relevant source code + - Use `gh pr diff` or `gh pr view` to understand PR context + - Run `npm run verify` if needed to check the current state + - Reply with a concise, specific answer + + ### 3. Make changes (write mode) + If the maintainer is asking you to fix something, revise a PR, address review feedback, + or make code changes: + - If you're on a PR, check out the PR branch: `gh pr checkout ` + - Read the PR diff and any feedback + - Make the requested changes + - If you changed src/, rebuild dist/: `npm run build` + - Run `npm run verify` — everything must pass + - Commit with a descriptive message (do NOT add Co-Authored-By lines) + - Push to the existing PR branch + - Reply with a summary of what you changed + + ## Rules + - ONLY make changes when the maintainer explicitly asks you to + - If the request is ambiguous, ask for clarification — don't guess + - Stay scoped to what's asked. Don't refactor unrelated things. + - Do NOT create new PRs or branches — work on the existing PR branch + - Do NOT close issues or PRs unless explicitly told to + - Be conversational and concise. Reference actual code (file paths, line numbers) when relevant. + claude_args: | + --allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch" + --max-turns 90 + --model ${{ vars.ANTHROPIC_OPUS_LATEST || 'claude-opus-4-6' }} From 83eb6c2dbb56867d117099b8ac17b5348ea3a047 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 16:35:24 -0500 Subject: [PATCH 11/32] add workflow for updating open PRs targeting development --- .../workflows/update-prs-with-development.yml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/update-prs-with-development.yml diff --git a/.github/workflows/update-prs-with-development.yml b/.github/workflows/update-prs-with-development.yml new file mode 100644 index 0000000..2e67cef --- /dev/null +++ b/.github/workflows/update-prs-with-development.yml @@ -0,0 +1,52 @@ +name: CI / Update PRs + +on: + # push: + # branches: [development] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-prs: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Generate bot token + id: bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - uses: actions/checkout@v4 + with: + token: ${{ steps.bot-token.outputs.token }} + + - name: Update open PRs targeting development + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + run: | + PR_NUMBERS=$(gh pr list --base development --state open --json number --jq '.[].number') + + if [ -z "$PR_NUMBERS" ]; then + echo "No open PRs targeting development." + exit 0 + fi + + for PR in $PR_NUMBERS; do + IS_DRAFT=$(gh pr view "$PR" --json isDraft --jq '.isDraft') + if [ "$IS_DRAFT" = "true" ]; then + echo "PR #$PR is a draft - skipping." + continue + fi + echo "Attempting to update PR #$PR..." + if gh pr update-branch "$PR" 2>&1; then + echo "PR #$PR updated successfully." + else + echo "PR #$PR skipped (conflicts or already up to date)." + fi + done From eb43e43abe66f4261e05981936346b3b85654127 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 16:51:58 -0500 Subject: [PATCH 12/32] refactor RELEASING.md for improved formatting and readability --- RELEASING.md | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index e127aab..74b26f5 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,15 +1,10 @@ # Releasing -This Action is released with the same draft-then-publish flow as the -[`llm-exe`](https://github.com/llm-exe/llm-exe) SDK. Consumers pin a floating -major (`uses: llm-exe/github-action@v1`) or minor (`@v1.4`) tag and automatically -get the latest patch; the immutable `vX.Y.Z` tag is also available for exact pins. +This Action is released with the same draft-then-publish flow as the [`llm-exe`](https://github.com/llm-exe/llm-exe) SDK. Consumers pin a floating major (`uses: llm-exe/github-action@v1`) or minor (`@v1.4`) tag and automatically get the latest patch; the immutable `vX.Y.Z` tag is also available for exact pins. ## What ships -A GitHub Action runs the committed `dist/index.js`, not `src/`. CI rebuilds the -bundle on every PR and fails if `dist/` drifts from source, so the bundle must be -built and committed before a release. Build it with `npm run build`. +A GitHub Action runs the committed `dist/index.js`, not `src/`. CI rebuilds the bundle on every PR and fails if `dist/` drifts from source, so the bundle must be built and committed before a release. Build it with `npm run build`. ## Versioning @@ -19,33 +14,22 @@ built and committed before a release. Build it with `npm run build`. - `v1.4` moves to the latest `v1.4.x`. - `v1` moves to the latest `v1.x.x`. -Pre-releases (`v2.0.0-beta.0`) get an immutable tag and a GitHub pre-release, but -never move the floating `v2` / `v2.0` tags. +Pre-releases (`v2.0.0-beta.0`) get an immutable tag and a GitHub pre-release, but never move the floating `v2` / `v2.0` tags. ## Steps -1. Open a PR into `main` that bumps `package.json` `version` to the next version - and includes a freshly built `dist/` (`npm run build`). +1. Open a PR into `main` that bumps `package.json` `version` to the next version and includes a freshly built `dist/` (`npm run build`). 2. On the PR, two gates must pass: - **CI / Tests** runs `npm run verify` on Node 20 and 22 and checks `dist/` is in sync. - - **Release / Check Semver** confirms `package.json` version is greater than the - latest stable `vX.Y.Z` tag. -3. Merge the PR. **Release / Create Draft** builds a draft GitHub Release tagged - `vX.Y.Z` with cleaned, auto-generated notes. + - **Release / Check Semver** confirms `package.json` version is greater than the latest stable `vX.Y.Z` tag. +3. Merge the PR. **Release / Create Draft** builds a draft GitHub Release tagged `vX.Y.Z` with cleaned, auto-generated notes. 4. Review the draft release notes, then click **Publish**. -5. **Release / Publish** checks out the released tag, re-verifies `dist/`, and - re-points the floating `v1` and `v1.4` tags to the released commit. If anything - fails, the release is reverted to draft. +5. **Release / Publish** checks out the released tag, re-verifies `dist/`, and re-points the floating `v1` and `v1.4` tags to the released commit. If anything fails, the release is reverted to draft. ## First release -There are no `vX.Y.Z` tags yet (only a legacy `v1`), so the semver check treats the -latest stable version as `0.0.0`. Set `package.json` `version` to your first -release (for example `1.0.0`) in step 1. Publishing it creates `v1.0.0` and moves -the existing `v1` tag onto that commit. +There are no `vX.Y.Z` tags yet (only a legacy `v1`), so the semver check treats the latest stable version as `0.0.0`. Set `package.json` `version` to your first release (for example `1.0.0`) in step 1. Publishing it creates `v1.0.0` and moves the existing `v1` tag onto that commit. ## Local fallback -`npm run release -- v1.4.5` (see `scripts/release.sh`) builds, commits, tags, and -pushes from your machine. It does not create a GitHub Release or move the floating -tags, so prefer the workflow flow above for normal releases. +`npm run release -- v1.4.5` (see `scripts/release.sh`) builds, commits, tags, and pushes from your machine. It does not create a GitHub Release or move the floating tags, so prefer the workflow flow above for normal releases. From 5252bcd270868c42d818afb5f6bd13b866c5ea17 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Mon, 8 Jun 2026 16:52:05 -0500 Subject: [PATCH 13/32] refactor create-draft-release workflow for improved clarity and error handling --- .github/workflows/create-draft-release.yml | 196 ++++++++++----------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml index 33d5627..84d37a2 100644 --- a/.github/workflows/create-draft-release.yml +++ b/.github/workflows/create-draft-release.yml @@ -21,101 +21,101 @@ jobs: if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' steps: - - uses: actions/checkout@v4 - - - name: Get all draft releases - id: get_drafts - run: | - gh api repos/${{ github.repository }}/releases \ - | jq '.[] | select(.draft == true) | .id' > release_ids.txt - - - name: Delete old draft releases - run: | - while read release_id; do - echo "Deleting draft release with ID: $release_id" - gh api -X DELETE repos/${{ github.repository }}/releases/$release_id - done < release_ids.txt - - - name: Determine next semantic version for release - run: | - PACKAGE_VERSION=$(jq -r '.version' package.json) - if [ -z "$PACKAGE_VERSION" ] || [ "$PACKAGE_VERSION" = "null" ]; then - echo "No version found in package.json" - exit 1 - fi - - # Validate MAJOR.MINOR.PATCH with optional semver pre-release suffix (e.g. -beta.0, -rc.1) - if [[ ! $PACKAGE_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then - echo "Invalid version format ($PACKAGE_VERSION). Expected MAJOR.MINOR.PATCH or MAJOR.MINOR.PATCH-PRERELEASE" - exit 1 - fi - - # Preserve the full version (including any -beta.X / -rc.X suffix) in the tag - NEW_VERSION="v${PACKAGE_VERSION}" - echo "NEW_VERSION=${NEW_VERSION}" >> $GITHUB_ENV - - # A pre-release suffix (anything after a "-") makes this a GitHub pre-release - if [[ "$PACKAGE_VERSION" == *-* ]]; then - echo "IS_PRERELEASE=true" >> $GITHUB_ENV - else - echo "IS_PRERELEASE=false" >> $GITHUB_ENV - fi - - - name: Create Draft Release on GitHub - id: create_release - run: | - response=$(curl -s -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - https://api.github.com/repos/${{ github.repository }}/releases \ - -d '{ - "tag_name": "'"${NEW_VERSION}"'", - "target_commitish": "main", - "draft": true, - "prerelease": '"${IS_PRERELEASE}"', - "make_latest": "'"$([ "${IS_PRERELEASE}" = "true" ] && echo false || echo legacy)"'", - "generate_release_notes": true - }') - - # Check if release creation succeeded - if echo "$response" | jq -e '.id' > /dev/null; then - release_url=$(echo $response | jq -r '.html_url') - release_id=$(echo $response | jq -r '.id') - body=$(echo "$response" | jq -r '.body') - - echo "release_url=${release_url}" >> $GITHUB_OUTPUT - echo "release_id=${release_id}" >> $GITHUB_OUTPUT - echo "$body" > release_body.txt - - echo "Draft release created: ${release_url}" - else - echo "Failed to create draft release" - echo "$response" | jq '.' - exit 1 - fi - - - name: Clean up release notes - id: clean_release_notes - run: | - # Remove automation-related commits (chore: bump version, Draft PR for release, Bump Version on PR to Main, docs: sync) and remove "by @username in" from PR references - CLEANED_BODY=$(sed '/chore: bump version/Id; /Draft PR for release/Id; /Bump Version on PR to Main/Id; /docs: sync/Id' release_body.txt | sed -E 's/ by @[^ ]+ in/ /g') - echo "$CLEANED_BODY" > cleaned_body.txt - echo "Cleaned release notes:" - cat cleaned_body.txt - - - name: Update Draft Release with cleaned notes - run: | - CLEANED_BODY=$(jq -Rs '.' < cleaned_body.txt) - response=$(curl -s -X PATCH \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.release_id }} \ - -d "{\"tag_name\": \"${NEW_VERSION}\", \"target_commitish\": \"main\", \"body\": ${CLEANED_BODY}}") - - if echo "$response" | jq -e '.id' > /dev/null; then - echo "Release notes updated successfully" - echo "Draft release ready: ${{ steps.create_release.outputs.release_url }}" - else - echo "Warning: Failed to update release notes" - echo "$response" | jq '.' - fi + - uses: actions/checkout@v4 + + - name: Get all draft releases + id: get_drafts + run: | + gh api repos/${{ github.repository }}/releases \ + | jq '.[] | select(.draft == true) | .id' > release_ids.txt + + - name: Delete old draft releases + run: | + while read release_id; do + echo "Deleting draft release with ID: $release_id" + gh api -X DELETE repos/${{ github.repository }}/releases/$release_id + done < release_ids.txt + + - name: Determine next semantic version for release + run: | + PACKAGE_VERSION=$(jq -r '.version' package.json) + if [ -z "$PACKAGE_VERSION" ] || [ "$PACKAGE_VERSION" = "null" ]; then + echo "No version found in package.json" + exit 1 + fi + + # Validate MAJOR.MINOR.PATCH with optional semver pre-release suffix (e.g. -beta.0, -rc.1) + if [[ ! $PACKAGE_VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version format ($PACKAGE_VERSION). Expected MAJOR.MINOR.PATCH or MAJOR.MINOR.PATCH-PRERELEASE" + exit 1 + fi + + # Preserve the full version (including any -beta.X / -rc.X suffix) in the tag + NEW_VERSION="v${PACKAGE_VERSION}" + echo "NEW_VERSION=${NEW_VERSION}" >> $GITHUB_ENV + + # A pre-release suffix (anything after a "-") makes this a GitHub pre-release + if [[ "$PACKAGE_VERSION" == *-* ]]; then + echo "IS_PRERELEASE=true" >> $GITHUB_ENV + else + echo "IS_PRERELEASE=false" >> $GITHUB_ENV + fi + + - name: Create Draft Release on GitHub + id: create_release + run: | + response=$(curl -s -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + https://api.github.com/repos/${{ github.repository }}/releases \ + -d '{ + "tag_name": "'"${NEW_VERSION}"'", + "target_commitish": "main", + "draft": true, + "prerelease": '"${IS_PRERELEASE}"', + "make_latest": "'"$([ "${IS_PRERELEASE}" = "true" ] && echo false || echo legacy)"'", + "generate_release_notes": true + }') + + # Check if release creation succeeded + if echo "$response" | jq -e '.id' > /dev/null; then + release_url=$(echo $response | jq -r '.html_url') + release_id=$(echo $response | jq -r '.id') + body=$(echo "$response" | jq -r '.body') + + echo "release_url=${release_url}" >> $GITHUB_OUTPUT + echo "release_id=${release_id}" >> $GITHUB_OUTPUT + echo "$body" > release_body.txt + + echo "Draft release created: ${release_url}" + else + echo "Failed to create draft release" + echo "$response" | jq '.' + exit 1 + fi + + - name: Clean up release notes + id: clean_release_notes + run: | + # Remove automation-related commits (chore: bump version, Draft PR for release, Bump Version on PR to Main, docs: sync) and remove "by @username in" from PR references + CLEANED_BODY=$(sed '/chore: bump version/Id; /Draft PR for release/Id; /Bump Version on PR to Main/Id; /docs: sync/Id' release_body.txt | sed -E 's/ by @[^ ]+ in/ /g') + echo "$CLEANED_BODY" > cleaned_body.txt + echo "Cleaned release notes:" + cat cleaned_body.txt + + - name: Update Draft Release with cleaned notes + run: | + CLEANED_BODY=$(jq -Rs '.' < cleaned_body.txt) + response=$(curl -s -X PATCH \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.create_release.outputs.release_id }} \ + -d "{\"tag_name\": \"${NEW_VERSION}\", \"target_commitish\": \"main\", \"body\": ${CLEANED_BODY}}") + + if echo "$response" | jq -e '.id' > /dev/null; then + echo "Release notes updated successfully" + echo "Draft release ready: ${{ steps.create_release.outputs.release_url }}" + else + echo "Warning: Failed to update release notes" + echo "$response" | jq '.' + fi From d46b5292502c672dbb0a84134775c40e0934b53b Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 12:40:01 -0500 Subject: [PATCH 14/32] update Node.js setup action to version 6 for improved performance and compatibility --- .github/workflows/agent-review-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/agent-review-pr.yml b/.github/workflows/agent-review-pr.yml index d346923..b5e98b5 100644 --- a/.github/workflows/agent-review-pr.yml +++ b/.github/workflows/agent-review-pr.yml @@ -46,7 +46,7 @@ jobs: GH_TOKEN: ${{ github.token }} - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: "npm" @@ -77,7 +77,7 @@ jobs: fetch-depth: 0 token: ${{ steps.review-bot-token.outputs.token }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 20 cache: npm From 387f09f0e7e699b385de0d7358dd005a06bea674 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 12:40:09 -0500 Subject: [PATCH 15/32] update Node.js setup action to version 6 for improved performance and compatibility --- .github/workflows/bot-respond.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bot-respond.yml b/.github/workflows/bot-respond.yml index 9cfbd1f..9afc2ee 100644 --- a/.github/workflows/bot-respond.yml +++ b/.github/workflows/bot-respond.yml @@ -41,7 +41,7 @@ jobs: fetch-depth: 0 token: ${{ steps.bot-token.outputs.token }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 20 cache: npm From 585cff26ffefcba3445c343b238fad0ff5321dab Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 12:40:16 -0500 Subject: [PATCH 16/32] update Node.js setup action to version 6 for improved performance and compatibility --- .github/workflows/publish-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 60226dc..393adfe 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -57,7 +57,7 @@ jobs: run: git checkout --detach "${{ steps.meta.outputs.tag }}" - name: Use Node.js 20.x - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 20.x cache: "npm" From 07e83d3b0ce06b8789c799ff4895fa5d67c4b2c9 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 12:43:22 -0500 Subject: [PATCH 17/32] update Node.js setup action to version 6 for improved performance and compatibility --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2dec65f..b2ca835 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: "npm" @@ -47,7 +47,7 @@ jobs: # bundle and fails if the committed output drifts from source, so a release can # never ship a stale bundle. - name: Use Node.js 20.x - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 20.x cache: "npm" From f0cd841df5d810b1bb8f1f46de819f56803a189d Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 13:02:47 -0500 Subject: [PATCH 18/32] update Node.js version in agent-review-pr workflow to 24.x for enhanced compatibility --- .github/workflows/agent-review-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/agent-review-pr.yml b/.github/workflows/agent-review-pr.yml index b5e98b5..5d2d1e8 100644 --- a/.github/workflows/agent-review-pr.yml +++ b/.github/workflows/agent-review-pr.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [20.x, 22.x] + node-version: [20.x, 22.x, 24.x] steps: - uses: actions/checkout@v4 @@ -79,7 +79,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 24 cache: npm - name: Install dependencies From f6c01c2c98b94bc276ee9ecca94b98b144b5a775 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 13:02:54 -0500 Subject: [PATCH 19/32] update Node.js version in bot-respond workflow to 24 for enhanced compatibility --- .github/workflows/bot-respond.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bot-respond.yml b/.github/workflows/bot-respond.yml index 9afc2ee..c96e40f 100644 --- a/.github/workflows/bot-respond.yml +++ b/.github/workflows/bot-respond.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 24 cache: npm - name: Install dependencies From a2c6fbd6f7819d39410c5d7c012586b0733ae9e6 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 13:02:59 -0500 Subject: [PATCH 20/32] update Node.js version in publish-release workflow to 24.x for enhanced compatibility --- .github/workflows/publish-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 393adfe..17157d6 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -56,10 +56,10 @@ jobs: - name: Checkout released tag run: git checkout --detach "${{ steps.meta.outputs.tag }}" - - name: Use Node.js 20.x + - name: Use Node.js 24.x uses: actions/setup-node@v6 with: - node-version: 20.x + node-version: 24.x cache: "npm" - name: Install dependencies From aec5b67ace4188c6ccf52bc8f82fff622621ce29 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Tue, 9 Jun 2026 13:03:08 -0500 Subject: [PATCH 21/32] update Node.js version in tests workflow to include 24.x for enhanced compatibility --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b2ca835..e52313f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [20.x, 22.x] + node-version: [20.x, 22.x, 24.x] steps: - uses: actions/checkout@v4 @@ -46,10 +46,10 @@ jobs: # The Action runs the committed dist/index.js, not src/. This job rebuilds the # bundle and fails if the committed output drifts from source, so a release can # never ship a stale bundle. - - name: Use Node.js 20.x + - name: Use Node.js 24.x uses: actions/setup-node@v6 with: - node-version: 20.x + node-version: 24.x cache: "npm" - name: Install dependencies From dfd14c8852c6ec66f40f11ae0b4254289bb0e812 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:30:14 -0500 Subject: [PATCH 22/32] update actions/checkout and actions/create-github-app-token versions for improved functionality --- .github/workflows/agent-review-pr.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/agent-review-pr.yml b/.github/workflows/agent-review-pr.yml index 5d2d1e8..f782bc5 100644 --- a/.github/workflows/agent-review-pr.yml +++ b/.github/workflows/agent-review-pr.yml @@ -37,7 +37,7 @@ jobs: node-version: [20.x, 22.x, 24.x] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Checkout PR (workflow_dispatch) if: github.event_name == 'workflow_dispatch' @@ -67,12 +67,12 @@ jobs: steps: - name: Generate review bot token id: review-bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.LLM_EXE_REVIEW_BOT_APP_ID }} + client-id: ${{ vars. LLM_EXE_REVIEW_BOT_CLIENT_ID }} private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 token: ${{ steps.review-bot-token.outputs.token }} @@ -146,16 +146,16 @@ jobs: steps: - name: Generate review bot token id: review-bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.LLM_EXE_REVIEW_BOT_APP_ID }} + client-id: ${{ vars. LLM_EXE_REVIEW_BOT_CLIENT_ID }} private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }} - name: Generate bot token id: bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.APP_ID }} + client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Approve or skip From 89e8a7e4debc16f1f77f32e0bec1e708128a60a8 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:30:31 -0500 Subject: [PATCH 23/32] update actions/checkout and actions/create-github-app-token versions for improved functionality --- .github/workflows/auto-merge-main-pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-merge-main-pr.yml b/.github/workflows/auto-merge-main-pr.yml index 9c5cd51..2bf3b18 100644 --- a/.github/workflows/auto-merge-main-pr.yml +++ b/.github/workflows/auto-merge-main-pr.yml @@ -30,13 +30,13 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'development' || github.event_name == 'pull_request' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Generate bot token id: bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.APP_ID }} + client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Get PR number for development to main From 66d205f7e9cd3edad6781465b4bc447b8b078ee5 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:30:58 -0500 Subject: [PATCH 24/32] update actions/create-github-app-token to v3 and actions/checkout to v6 for improved functionality --- .github/workflows/bot-respond.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bot-respond.yml b/.github/workflows/bot-respond.yml index c96e40f..17d083e 100644 --- a/.github/workflows/bot-respond.yml +++ b/.github/workflows/bot-respond.yml @@ -26,9 +26,9 @@ jobs: steps: - name: Generate bot token id: bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.APP_ID }} + client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Configure git @@ -36,7 +36,7 @@ jobs: git config --global user.name "llm-exe-bot[bot]" git config --global user.email "${{ secrets.APP_ID }}+llm-exe-bot[bot]@users.noreply.github.com" - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 token: ${{ steps.bot-token.outputs.token }} From 58908fea679e8ad08e24fba0ca0dad5c992ea472 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:31:07 -0500 Subject: [PATCH 25/32] update actions/checkout to v6 for improved functionality --- .github/workflows/check-semantic-versioning.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-semantic-versioning.yml b/.github/workflows/check-semantic-versioning.yml index 42e5649..6823b28 100644 --- a/.github/workflows/check-semantic-versioning.yml +++ b/.github/workflows/check-semantic-versioning.yml @@ -16,7 +16,7 @@ jobs: enforce-semantic-version: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Fetch all tags run: git fetch --tags From f93acb596443b70b0d60d27eb5d12c6c1c645645 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:31:20 -0500 Subject: [PATCH 26/32] update actions/checkout to v6 and refine release notes cleanup process --- .github/workflows/create-draft-release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml index 84d37a2..5ead5dd 100644 --- a/.github/workflows/create-draft-release.yml +++ b/.github/workflows/create-draft-release.yml @@ -21,7 +21,7 @@ jobs: if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Get all draft releases id: get_drafts @@ -97,8 +97,8 @@ jobs: - name: Clean up release notes id: clean_release_notes run: | - # Remove automation-related commits (chore: bump version, Draft PR for release, Bump Version on PR to Main, docs: sync) and remove "by @username in" from PR references - CLEANED_BODY=$(sed '/chore: bump version/Id; /Draft PR for release/Id; /Bump Version on PR to Main/Id; /docs: sync/Id' release_body.txt | sed -E 's/ by @[^ ]+ in/ /g') + # Remove automation-related commits and remove "by @username in" from PR references + CLEANED_BODY=$(sed '/chore: bump version/Id; /Draft PR for release/Id; /Bump Version on PR to Main/Id; /docs: sync/Id; /revert version bump after failed publish/Id' release_body.txt | sed -E 's/ by @[^ ]+ in/ /g') echo "$CLEANED_BODY" > cleaned_body.txt echo "Cleaned release notes:" cat cleaned_body.txt From 1045f402ce4ec8b5408d94346e890a5e218c6384 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:31:39 -0500 Subject: [PATCH 27/32] update actions/checkout to v6 and actions/create-github-app-token to v3 for improved functionality --- .github/workflows/draft-main-pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/draft-main-pr.yml b/.github/workflows/draft-main-pr.yml index 739c143..9aaf545 100644 --- a/.github/workflows/draft-main-pr.yml +++ b/.github/workflows/draft-main-pr.yml @@ -20,15 +20,15 @@ jobs: if: ${{ github.event_name == 'release' || !(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'development' && github.event.pull_request.head.ref == 'bump-version-branch') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Generate bot token id: bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.APP_ID }} + client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - name: Get the latest release version From 253322c8c2aac2aae6991a8efe0cb6fd2694e8f5 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:31:57 -0500 Subject: [PATCH 28/32] update actions/checkout to v6 and actions/create-github-app-token to v3 for improved functionality and enhanced release handling --- .github/workflows/publish-release.yml | 91 +++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 17157d6..73eb23b 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -8,6 +8,7 @@ on: permissions: contents: write + pull-requests: write jobs: check-release-branch: @@ -31,7 +32,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -120,9 +121,20 @@ jobs: needs: [publish-action] name: Revert Release to Draft runs-on: ubuntu-latest - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: + - name: Generate bot token + id: bot-token + uses: actions/create-github-app-token@v3 + with: + client-id: ${{ vars.APP_CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + token: ${{ steps.bot-token.outputs.token }} + - name: Revert release to draft run: | RELEASE_ID=$(jq -r .release.id "$GITHUB_EVENT_PATH") @@ -141,7 +153,7 @@ jobs: BODY_JSON=$(jq -Rs '.' < release_body.txt) response=$(curl -s -X PATCH \ -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${GH_TOKEN}" \ + -H "Authorization: Bearer ${{ steps.bot-token.outputs.token }}" \ https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID \ -d "{\"draft\": true, \"body\": ${BODY_JSON}}") if echo "$response" | jq -e '.id' > /dev/null; then @@ -151,3 +163,74 @@ jobs: echo "$response" | jq '.' exit 1 fi + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Delete release tag + run: | + TAG_NAME=$(jq -r .release.tag_name "$GITHUB_EVENT_PATH") + echo "Deleting git tag: $TAG_NAME" + if gh api -X DELETE "repos/${{ github.repository }}/git/refs/tags/$TAG_NAME"; then + echo "Tag $TAG_NAME deleted — version bump logic will no longer treat this version as released" + else + echo "Could not delete tag $TAG_NAME" + fi + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + + - name: Revert version bump in development and update draft PR title + run: | + TAG_NAME=$(jq -r .release.tag_name "$GITHUB_EVENT_PATH") + FAILED_VERSION="${TAG_NAME#v}" + + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git fetch origin development + + CURRENT_DEV_VERSION=$(git show origin/development:package.json | jq -r '.version') + echo "Failed version: $FAILED_VERSION | Current version on development: $CURRENT_DEV_VERSION" + + if [ "$CURRENT_DEV_VERSION" != "$FAILED_VERSION" ]; then + echo "development was bumped to v$CURRENT_DEV_VERSION after the failed release — reverting to v$FAILED_VERSION" + + git checkout -b revert-version-bump origin/development + jq --arg v "$FAILED_VERSION" '.version = $v' package.json > package.tmp && mv package.tmp package.json + git add package.json + git commit -m "chore: revert version bump after failed publish of v$FAILED_VERSION" + git push origin revert-version-bump --force + + EXISTING=$(gh pr list --state open --head revert-version-bump --base development --json number --jq '.[0].number') + if [ -z "$EXISTING" ]; then + gh pr create \ + --title "chore: revert version bump after failed publish of v$FAILED_VERSION" \ + --body "The v$FAILED_VERSION publish failed. Reverting package.json so the next release attempt re-uses v$FAILED_VERSION." \ + --base development \ + --head revert-version-bump + fi + + REVERT_PR=$(gh pr list --state open --head revert-version-bump --base development --json number --jq '.[0].number') + gh pr merge "$REVERT_PR" --admin --squash --delete-branch --repo ${{ github.repository }} + echo "development reverted to v$FAILED_VERSION" + else + echo "development already at v$FAILED_VERSION — no version revert needed" + fi + + # Update the draft dev→main PR title immediately (draft-main-pr.yml will also + # update it when the revert PR merges, but this ensures it happens right away) + EXPECTED_TITLE="Draft PR for release version v$FAILED_VERSION" + PR_INFO=$(gh pr list --base main --head development --state open --json number,title --jq '.[0]') + if [ -n "$PR_INFO" ] && [ "$PR_INFO" != "null" ]; then + PR_NUMBER=$(echo "$PR_INFO" | jq -r '.number') + PR_TITLE=$(echo "$PR_INFO" | jq -r '.title') + if [ "$PR_TITLE" != "$EXPECTED_TITLE" ]; then + gh pr edit "$PR_NUMBER" --title "$EXPECTED_TITLE" --repo ${{ github.repository }} + echo "Updated dev→main PR #$PR_NUMBER title to: $EXPECTED_TITLE" + else + echo "PR title already correct: $PR_TITLE" + fi + else + echo "No open dev→main PR found — title update skipped" + fi + env: + GH_TOKEN: ${{ steps.bot-token.outputs.token }} + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} From 97e0dc1254046135f1a8187d461eae35e84cdbb3 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:32:18 -0500 Subject: [PATCH 29/32] update actions/checkout to v6 for consistency across jobs --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e52313f..39992c3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: matrix: node-version: [20.x, 22.x, 24.x] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 @@ -41,7 +41,7 @@ jobs: name: Verify dist is in sync with source runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 # The Action runs the committed dist/index.js, not src/. This job rebuilds the # bundle and fails if the committed output drifts from source, so a release can From 4dbe9f7dc9b4b10914aa334e89337e91bfd96a60 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 10:34:01 -0500 Subject: [PATCH 30/32] update actions/create-github-app-token to v3 and actions/checkout to v6 for improved functionality --- .github/workflows/update-prs-with-development.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-prs-with-development.yml b/.github/workflows/update-prs-with-development.yml index 2e67cef..e7df36a 100644 --- a/.github/workflows/update-prs-with-development.yml +++ b/.github/workflows/update-prs-with-development.yml @@ -17,12 +17,12 @@ jobs: steps: - name: Generate bot token id: bot-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v3 with: - app-id: ${{ secrets.APP_ID }} + client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: token: ${{ steps.bot-token.outputs.token }} From 8efbd59caf5b3724d8dd0d52a6cd4e351c7dbf92 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 13:51:53 -0500 Subject: [PATCH 31/32] fix: remove extra space in client-id variable for create-github-app-token action --- .github/workflows/agent-review-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/agent-review-pr.yml b/.github/workflows/agent-review-pr.yml index f782bc5..9a17f7d 100644 --- a/.github/workflows/agent-review-pr.yml +++ b/.github/workflows/agent-review-pr.yml @@ -69,7 +69,7 @@ jobs: id: review-bot-token uses: actions/create-github-app-token@v3 with: - client-id: ${{ vars. LLM_EXE_REVIEW_BOT_CLIENT_ID }} + client-id: ${{ vars.LLM_EXE_REVIEW_BOT_CLIENT_ID }} private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }} - uses: actions/checkout@v6 @@ -148,7 +148,7 @@ jobs: id: review-bot-token uses: actions/create-github-app-token@v3 with: - client-id: ${{ vars. LLM_EXE_REVIEW_BOT_CLIENT_ID }} + client-id: ${{ vars.LLM_EXE_REVIEW_BOT_CLIENT_ID }} private-key: ${{ secrets.LLM_EXE_REVIEW_BOT_PRIVATE_KEY }} - name: Generate bot token From 124008f18814ce0a6d126369c548b9d2862d9da0 Mon Sep 17 00:00:00 2001 From: devfrankduah Date: Thu, 18 Jun 2026 13:52:13 -0500 Subject: [PATCH 32/32] fix: update git user email configuration to use APP_BOT_USER_ID variable --- .github/workflows/bot-respond.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bot-respond.yml b/.github/workflows/bot-respond.yml index 17d083e..754ecff 100644 --- a/.github/workflows/bot-respond.yml +++ b/.github/workflows/bot-respond.yml @@ -34,7 +34,7 @@ jobs: - name: Configure git run: | git config --global user.name "llm-exe-bot[bot]" - git config --global user.email "${{ secrets.APP_ID }}+llm-exe-bot[bot]@users.noreply.github.com" + git config --global user.email "${{ vars.APP_BOT_USER_ID }}+llm-exe-bot[bot]@users.noreply.github.com" - uses: actions/checkout@v6 with: