From 1e9a865a40fa86fb3de5b37b86d80ddd85907669 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 21 Nov 2025 15:14:11 -0800 Subject: [PATCH 01/16] It should upload the vsix file too. --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b59818..70d516d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -126,6 +126,12 @@ jobs: pattern: vscode-extension-* merge-multiple: true + - name: Get package filename + id: package + run: | + PACKAGE_FILE=$(ls *.vsix | head -1) + echo "filename=$PACKAGE_FILE" >> $GITHUB_OUTPUT + - name: Create release id: release uses: secondlife-3p/action-gh-release@v1 @@ -136,3 +142,4 @@ jobs: generate_release_notes: true target_commitish: ${{ github.sha }} append_body: true + files: ${{ steps.package.outputs.filename }} From d31f5d91418b9dc4c8e339e6d250a6c77061fa8f Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 09:01:49 -0700 Subject: [PATCH 02/16] Updating build. --- .github/workflows/ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6f3ff1..e80e762 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,25 +66,23 @@ jobs: - name: Compile extension run: npm run vscode:prepublish - - name: Determine version suffix + - name: Determine build metadata id: version run: | if [ "${{ github.ref }}" = "refs/heads/main" ]; then - echo "suffix=" >> $GITHUB_OUTPUT echo "release_type=release" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" = "refs/heads/debug" ]; then - echo "suffix=-debug" >> $GITHUB_OUTPUT - echo "release_type=debug" >> $GITHUB_OUTPUT + echo "release_type=beta" >> $GITHUB_OUTPUT fi echo "sha_short=$(echo ${{ github.sha }} | cut -c1-8)" >> $GITHUB_OUTPUT - name: Update package.json version for non-main builds - if: github.ref != 'refs/heads/main' + if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/v') run: | - # Get current version and append build info - CURRENT_VERSION=$(node -p "require('./package.json').version") - NEW_VERSION="${CURRENT_VERSION}${{ steps.version.outputs.suffix }}-${GITHUB_RUN_NUMBER}+${{ steps.version.outputs.sha_short }}" - npm version "$NEW_VERSION" --no-git-tag-version + # Build strict numeric version: major.minor.patch.run + BASE_VERSION=$(node -p "(() => { const m = (require('./package.json').version.match(/\\d+/g) || []); return [m[0] || '0', m[1] || '0', m[2] || '0'].join('.'); })()") + NEW_VERSION="${BASE_VERSION}.${GITHUB_RUN_NUMBER}" + node -e "const fs=require('fs');const p=require('./package.json');p.version=process.env.NEW_VERSION;fs.writeFileSync('package.json', JSON.stringify(p, null, 2) + '\\n');" echo "Updated version to: $NEW_VERSION" - name: Package extension From 88e7917c548e74686b99077d6dd8dfd4d09f9588 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 12:08:08 -0700 Subject: [PATCH 03/16] Build changes --- .github/workflows/ci.yml | 23 ++- .github/workflows/generate-release-notes.yml | 45 +++++ .github/workflows/release.yml | 197 +++++++++++++++++++ 3 files changed, 261 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/generate-release-notes.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e80e762..168b04c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,6 @@ name: CI on: push: branches: [ "*" ] - tags: [ "v*" ] pull_request: branches: [ main, develop ] workflow_dispatch: @@ -102,9 +101,20 @@ jobs: path: ${{ steps.package.outputs.filename }} retention-days: 30 + release_notes: + name: Generate release notes + needs: build + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/workflows/generate-release-notes.yml + permissions: + contents: read + with: + tag_name: ${{ github.ref_name }} + target_commitish: ${{ github.sha }} + release: name: Update release info - needs: build + needs: [build, release_notes] runs-on: ubuntu-latest timeout-minutes: 10 if: startsWith(github.ref, 'refs/tags/v') @@ -131,6 +141,12 @@ jobs: PACKAGE_FILE=$(ls *.vsix | head -1) echo "filename=$PACKAGE_FILE" >> $GITHUB_OUTPUT + - name: Write release notes file + env: + RELEASE_NOTES: ${{ needs.release_notes.outputs.release_notes }} + run: | + printf '%s\n' "$RELEASE_NOTES" > release_notes.md + - name: Create release id: release uses: secondlife-3p/action-gh-release@v1 @@ -138,7 +154,6 @@ jobs: # name the release page for the branch name: "SL-VScode Plugin ${{ steps.version.outputs.version }} Release" prerelease: true - generate_release_notes: true + body_path: release_notes.md target_commitish: ${{ github.sha }} - append_body: true files: ${{ steps.package.outputs.filename }} diff --git a/.github/workflows/generate-release-notes.yml b/.github/workflows/generate-release-notes.yml new file mode 100644 index 0000000..f52da99 --- /dev/null +++ b/.github/workflows/generate-release-notes.yml @@ -0,0 +1,45 @@ +name: Generate Release Notes + +on: + workflow_call: + inputs: + tag_name: + description: Tag name to generate release notes for + required: true + type: string + target_commitish: + description: Branch or commit SHA used as target for note generation + required: true + type: string + outputs: + release_notes: + description: Generated release notes body + value: ${{ jobs.generate.outputs.release_notes }} + +permissions: + contents: read + +jobs: + generate: + runs-on: ubuntu-latest + outputs: + release_notes: ${{ steps.generate.outputs.release_notes }} + + steps: + - name: Generate release notes body + id: generate + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const tag_name = core.getInput('tag_name'); + const target_commitish = core.getInput('target_commitish'); + + const response = await github.rest.repos.generateReleaseNotes({ + owner, + repo, + tag_name, + target_commitish, + }); + + core.setOutput('release_notes', response.data.body || ''); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3f0d06e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,197 @@ +name: Release & Publish + +on: + workflow_dispatch: + inputs: + tag_name: + description: Release tag to create, for example v1.0.3 + required: true + type: string + target_commitish: + description: Branch or commit SHA the tag should point to + required: true + default: rider-test # main + type: string + prerelease: + description: Mark the GitHub release as a prerelease + required: true + default: true + type: boolean + draft: + description: Create the GitHub release as a draft + required: true + default: false + type: boolean + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + + outputs: + tag_name: ${{ steps.release_meta.outputs.tag_name }} + release_name: ${{ steps.release_meta.outputs.release_name }} + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ inputs.target_commitish }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: '24.x' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: Validate tag matches package version + id: release_meta + shell: bash + run: | + PACKAGE_VERSION=$(node -p "require('./package.json').version") + EXPECTED_TAG="v${PACKAGE_VERSION}" + if [ "${{ inputs.tag_name }}" != "$EXPECTED_TAG" ]; then + echo "Expected release tag $EXPECTED_TAG for package version $PACKAGE_VERSION, got ${{ inputs.tag_name }}" >&2 + exit 1 + fi + + echo "tag_name=${{ inputs.tag_name }}" >> "$GITHUB_OUTPUT" + echo "release_name=SL-VScode Plugin ${PACKAGE_VERSION} Release" >> "$GITHUB_OUTPUT" + + - name: Compile extension + run: npm run vscode:prepublish + + - name: Install vsce + run: npm install -g @vscode/vsce + + - name: Package extension + run: vsce package + + - name: Get package filename + id: package + shell: bash + run: | + PACKAGE_FILE=$(ls *.vsix | head -1) + echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" + + - name: Upload VSIX artifact + uses: actions/upload-artifact@v4 + with: + name: vscode-extension-release-${{ github.run_id }} + path: ${{ steps.package.outputs.filename }} + retention-days: 30 + + release_notes: + name: Generate release notes + needs: build + uses: ./.github/workflows/generate-release-notes.yml + permissions: + contents: read + with: + tag_name: ${{ needs.build.outputs.tag_name }} + target_commitish: ${{ inputs.target_commitish }} + + release: + name: Publish release + needs: [build, release_notes] + runs-on: ubuntu-latest + + steps: + - name: Checkout target branch + uses: actions/checkout@v5 + with: + ref: ${{ inputs.target_commitish }} + + - name: Write release notes file + shell: bash + env: + RELEASE_NOTES: ${{ needs.release_notes.outputs.release_notes }} + run: | + printf '%s\n' "$RELEASE_NOTES" > release_notes.md + + - name: Update CHANGELOG.md + shell: bash + run: | + VERSION="${{ needs.build.outputs.tag_name }}" + VERSION="${VERSION#v}" + RELEASE_DATE=$(date -u +%Y-%m-%d) + + { + echo "## [${VERSION}] - ${RELEASE_DATE}" + echo + cat release_notes.md + echo + } > changelog_entry.md + + awk ' + BEGIN { inserted = 0 } + /^## \[/ && inserted == 0 { + while ((getline line < "changelog_entry.md") > 0) print line + close("changelog_entry.md") + print "" + inserted = 1 + } + { print } + END { + if (inserted == 0) { + print "" + while ((getline line < "changelog_entry.md") > 0) print line + close("changelog_entry.md") + } + } + ' CHANGELOG.md > CHANGELOG.new.md + + mv CHANGELOG.new.md CHANGELOG.md + + - name: Commit and push changelog update + shell: bash + run: | + TARGET_BRANCH="${{ inputs.target_commitish }}" + TARGET_BRANCH="${TARGET_BRANCH#refs/heads/}" + + if ! git ls-remote --exit-code --heads origin "$TARGET_BRANCH" > /dev/null; then + echo "target_commitish must be a branch name (or refs/heads/) to update CHANGELOG.md" >&2 + echo "Received: ${{ inputs.target_commitish }}" >&2 + exit 1 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git add CHANGELOG.md + + if git diff --cached --quiet; then + echo "No changelog changes to commit" + exit 0 + fi + + git commit -m "docs: update changelog for ${{ needs.build.outputs.tag_name }}" + git push origin HEAD:$TARGET_BRANCH + + - name: Download VSIX artifact + uses: actions/download-artifact@v4 + with: + name: vscode-extension-release-${{ github.run_id }} + + - name: Get package filename + id: package + shell: bash + run: | + PACKAGE_FILE=$(ls *.vsix | head -1) + echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" + + # - name: Create GitHub release + # uses: secondlife-3p/action-gh-release@v1 + # with: + # tag_name: ${{ needs.build.outputs.tag_name }} + # target_commitish: ${{ inputs.target_commitish }} + # name: ${{ needs.build.outputs.release_name }} + # prerelease: ${{ inputs.prerelease }} + # draft: ${{ inputs.draft }} + # body_path: release_notes.md + # files: ${{ steps.package.outputs.filename }} From 2a9bbf426993c304cbc0c58583ba31b1ee918873 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 12:21:27 -0700 Subject: [PATCH 04/16] reorder jobs --- .github/workflows/release.yml | 46 +++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f0d06e..3da1317 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Release & Publish +name: Cut Release on: workflow_dispatch: @@ -27,7 +27,17 @@ permissions: contents: write jobs: + release_notes: + name: Generate release notes + uses: ./.github/workflows/generate-release-notes.yml + permissions: + contents: read + with: + tag_name: ${{ inputs.tag_name }} + target_commitish: ${{ inputs.target_commitish }} + build: + needs: release_notes runs-on: ubuntu-latest outputs: @@ -49,19 +59,19 @@ jobs: - name: Install dependencies run: npm install - - name: Validate tag matches package version - id: release_meta - shell: bash - run: | - PACKAGE_VERSION=$(node -p "require('./package.json').version") - EXPECTED_TAG="v${PACKAGE_VERSION}" - if [ "${{ inputs.tag_name }}" != "$EXPECTED_TAG" ]; then - echo "Expected release tag $EXPECTED_TAG for package version $PACKAGE_VERSION, got ${{ inputs.tag_name }}" >&2 - exit 1 - fi + # - name: Validate tag matches package version + # id: release_meta + # shell: bash + # run: | + # PACKAGE_VERSION=$(node -p "require('./package.json').version") + # EXPECTED_TAG="v${PACKAGE_VERSION}" + # if [ "${{ inputs.tag_name }}" != "$EXPECTED_TAG" ]; then + # echo "Expected release tag $EXPECTED_TAG for package version $PACKAGE_VERSION, got ${{ inputs.tag_name }}" >&2 + # exit 1 + # fi - echo "tag_name=${{ inputs.tag_name }}" >> "$GITHUB_OUTPUT" - echo "release_name=SL-VScode Plugin ${PACKAGE_VERSION} Release" >> "$GITHUB_OUTPUT" + # echo "tag_name=${{ inputs.tag_name }}" >> "$GITHUB_OUTPUT" + # echo "release_name=SL-VScode Plugin ${PACKAGE_VERSION} Release" >> "$GITHUB_OUTPUT" - name: Compile extension run: npm run vscode:prepublish @@ -86,16 +96,6 @@ jobs: path: ${{ steps.package.outputs.filename }} retention-days: 30 - release_notes: - name: Generate release notes - needs: build - uses: ./.github/workflows/generate-release-notes.yml - permissions: - contents: read - with: - tag_name: ${{ needs.build.outputs.tag_name }} - target_commitish: ${{ inputs.target_commitish }} - release: name: Publish release needs: [build, release_notes] From c32d90dd0ff2778e754cf2a73d1f08e6274c5c30 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 12:28:37 -0700 Subject: [PATCH 05/16] update permissions --- .github/workflows/generate-release-notes.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-release-notes.yml b/.github/workflows/generate-release-notes.yml index f52da99..9ca4829 100644 --- a/.github/workflows/generate-release-notes.yml +++ b/.github/workflows/generate-release-notes.yml @@ -17,7 +17,7 @@ on: value: ${{ jobs.generate.outputs.release_notes }} permissions: - contents: read + contents: write jobs: generate: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3da1317..a7459f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: name: Generate release notes uses: ./.github/workflows/generate-release-notes.yml permissions: - contents: read + contents: write with: tag_name: ${{ inputs.tag_name }} target_commitish: ${{ inputs.target_commitish }} From 75f1463f81f514cd4c394b009b5f94ef10e34166 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 12:37:18 -0700 Subject: [PATCH 06/16] Validation --- .github/workflows/release.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a7459f1..2c415b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,25 @@ permissions: contents: write jobs: + validate_inputs: + name: Validate release inputs + runs-on: ubuntu-latest + steps: + - name: Validate tag_name + shell: bash + run: | + if [ -z "${{ inputs.tag_name }}" ]; then + echo "tag_name is required and cannot be empty" >&2 + exit 1 + fi + + if [[ ! "${{ inputs.tag_name }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$ ]]; then + echo "tag_name must look like v1.0.3" >&2 + exit 1 + fi + release_notes: + needs: validate_inputs name: Generate release notes uses: ./.github/workflows/generate-release-notes.yml permissions: From 93e355f59c8722d2d8d758994d7d253953139838 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 12:49:02 -0700 Subject: [PATCH 07/16] echo params --- .github/workflows/release.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c415b4..e8da1ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,12 @@ jobs: exit 1 fi + echo "tag_name=${{ inputs.tag_name }}" >&2 + echo "target_commitish=${{ inputs.target_commitish }}" >&2 + echo "prerelease=${{ inputs.prerelease }}" >&2 + echo "draft=${{ inputs.draft }}" >&2 + + release_notes: needs: validate_inputs name: Generate release notes From 743f1f279da53c26db2ece5178e7107713f14ed8 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 13:00:31 -0700 Subject: [PATCH 08/16] Fix the release notes gen. --- .github/workflows/generate-release-notes.yml | 7 +++- .github/workflows/release.yml | 39 ++++++++++---------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/.github/workflows/generate-release-notes.yml b/.github/workflows/generate-release-notes.yml index 9ca4829..b66a97a 100644 --- a/.github/workflows/generate-release-notes.yml +++ b/.github/workflows/generate-release-notes.yml @@ -29,11 +29,14 @@ jobs: - name: Generate release notes body id: generate uses: actions/github-script@v7 + env: + TAG_NAME: ${{ inputs.tag_name }} + TARGET_COMMITISH: ${{ inputs.target_commitish }} with: script: | const { owner, repo } = context.repo; - const tag_name = core.getInput('tag_name'); - const target_commitish = core.getInput('target_commitish'); + const tag_name = process.env.TAG_NAME; + const target_commitish = process.env.TARGET_COMMITISH; const response = await github.rest.repos.generateReleaseNotes({ owner, diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8da1ef..b8031f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -171,31 +171,32 @@ jobs: ' CHANGELOG.md > CHANGELOG.new.md mv CHANGELOG.new.md CHANGELOG.md + cat CHANGELOG.md >&2 - - name: Commit and push changelog update - shell: bash - run: | - TARGET_BRANCH="${{ inputs.target_commitish }}" - TARGET_BRANCH="${TARGET_BRANCH#refs/heads/}" + # - name: Commit and push changelog update + # shell: bash + # run: | + # TARGET_BRANCH="${{ inputs.target_commitish }}" + # TARGET_BRANCH="${TARGET_BRANCH#refs/heads/}" - if ! git ls-remote --exit-code --heads origin "$TARGET_BRANCH" > /dev/null; then - echo "target_commitish must be a branch name (or refs/heads/) to update CHANGELOG.md" >&2 - echo "Received: ${{ inputs.target_commitish }}" >&2 - exit 1 - fi + # if ! git ls-remote --exit-code --heads origin "$TARGET_BRANCH" > /dev/null; then + # echo "target_commitish must be a branch name (or refs/heads/) to update CHANGELOG.md" >&2 + # echo "Received: ${{ inputs.target_commitish }}" >&2 + # exit 1 + # fi - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + # git config user.name "github-actions[bot]" + # git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add CHANGELOG.md + # git add CHANGELOG.md - if git diff --cached --quiet; then - echo "No changelog changes to commit" - exit 0 - fi + # if git diff --cached --quiet; then + # echo "No changelog changes to commit" + # exit 0 + # fi - git commit -m "docs: update changelog for ${{ needs.build.outputs.tag_name }}" - git push origin HEAD:$TARGET_BRANCH + # git commit -m "docs: update changelog for ${{ needs.build.outputs.tag_name }}" + # git push origin HEAD:$TARGET_BRANCH - name: Download VSIX artifact uses: actions/download-artifact@v4 From 4fe0fc63da247f376b91d766cba2b91db533db97 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 16:15:47 -0700 Subject: [PATCH 09/16] Update versions in docs and release page. --- .github/workflows/release.yml | 262 +++++++++++++++++++++++++--------- 1 file changed, 193 insertions(+), 69 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b8031f9..b564c99 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,11 +27,42 @@ permissions: contents: write jobs: - validate_inputs: + authorize_actor: + name: Authorize triggering user + runs-on: ubuntu-latest + steps: + - name: Ensure actor has access + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const username = context.actor; + + const response = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username, + }); + + const allowed = ['admin', 'maintain']; + + if (!allowed.includes(response.data.permission)) { + core.setFailed( + `${username} must have maintain or admin access to run this workflow. Current permission: ${response.data.permission}` + ); + return; + } + + core.info(`${username} is authorized with permission: ${response.data.permission}`); + + validate: name: Validate release inputs + needs: authorize_actor runs-on: ubuntu-latest + outputs: + version: ${{ steps.set_variables.outputs.version }} steps: - - name: Validate tag_name + - name: Validate shell: bash run: | if [ -z "${{ inputs.tag_name }}" ]; then @@ -39,8 +70,9 @@ jobs: exit 1 fi - if [[ ! "${{ inputs.tag_name }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-].*)?$ ]]; then - echo "tag_name must look like v1.0.3" >&2 + # Enforce SemVer 2.0.0 with a required leading "v" (e.g. v1.2.3, v1.2.3-rc.1, v1.2.3+build.7) + if [[ ! "${{ inputs.tag_name }}" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$ ]]; then + echo "tag_name must be valid SemVer with a leading v (for example v1.0.3 or v1.0.3-rc.1+build.7)" >&2 exit 1 fi @@ -49,27 +81,47 @@ jobs: echo "prerelease=${{ inputs.prerelease }}" >&2 echo "draft=${{ inputs.draft }}" >&2 + - name: Set Variables + id: set_variables + shell: bash + run: | + TAG_NAME="${{ inputs.tag_name }}" + SEMANTIC_VERSION="${TAG_NAME#v}" + echo "version=$SEMANTIC_VERSION" >> "$GITHUB_OUTPUT" + echo "version=$SEMANTIC_VERSION" >&2 release_notes: - needs: validate_inputs + needs: validate name: Generate release notes - uses: ./.github/workflows/generate-release-notes.yml + runs-on: ubuntu-latest + outputs: + release_notes: ${{ steps.generate.outputs.release_notes }} permissions: contents: write - with: - tag_name: ${{ inputs.tag_name }} - target_commitish: ${{ inputs.target_commitish }} - build: - needs: release_notes - runs-on: ubuntu-latest + steps: + - name: Generate release notes body + id: generate + uses: actions/github-script@v7 + env: + TAG_NAME: ${{ inputs.tag_name }} + TARGET_COMMITISH: ${{ inputs.target_commitish }} + with: + script: | + const { owner, repo } = context.repo; + const tag_name = process.env.TAG_NAME; + const target_commitish = process.env.TARGET_COMMITISH; - outputs: - tag_name: ${{ steps.release_meta.outputs.tag_name }} - release_name: ${{ steps.release_meta.outputs.release_name }} + const response = await github.rest.repos.generateReleaseNotes({ + owner, + repo, + tag_name, + target_commitish, + }); - steps: - - name: Checkout code + core.setOutput('release_notes', response.data.body || ''); + + - name: Checkout target branch uses: actions/checkout@v5 with: ref: ${{ inputs.target_commitish }} @@ -80,69 +132,23 @@ jobs: node-version: '24.x' cache: 'npm' - - name: Install dependencies - run: npm install - - # - name: Validate tag matches package version - # id: release_meta - # shell: bash - # run: | - # PACKAGE_VERSION=$(node -p "require('./package.json').version") - # EXPECTED_TAG="v${PACKAGE_VERSION}" - # if [ "${{ inputs.tag_name }}" != "$EXPECTED_TAG" ]; then - # echo "Expected release tag $EXPECTED_TAG for package version $PACKAGE_VERSION, got ${{ inputs.tag_name }}" >&2 - # exit 1 - # fi - - # echo "tag_name=${{ inputs.tag_name }}" >> "$GITHUB_OUTPUT" - # echo "release_name=SL-VScode Plugin ${PACKAGE_VERSION} Release" >> "$GITHUB_OUTPUT" - - - name: Compile extension - run: npm run vscode:prepublish - - - name: Install vsce - run: npm install -g @vscode/vsce - - - name: Package extension - run: vsce package - - - name: Get package filename - id: package + - name: Update version shell: bash run: | - PACKAGE_FILE=$(ls *.vsix | head -1) - echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" - - - name: Upload VSIX artifact - uses: actions/upload-artifact@v4 - with: - name: vscode-extension-release-${{ github.run_id }} - path: ${{ steps.package.outputs.filename }} - retention-days: 30 - - release: - name: Publish release - needs: [build, release_notes] - runs-on: ubuntu-latest - - steps: - - name: Checkout target branch - uses: actions/checkout@v5 - with: - ref: ${{ inputs.target_commitish }} + VERSION="${{ needs.validate.outputs.version }}" + npm version "$VERSION" --no-git-tag-version --allow-same-version - name: Write release notes file shell: bash env: - RELEASE_NOTES: ${{ needs.release_notes.outputs.release_notes }} + RELEASE_NOTES: ${{ steps.generate.outputs.release_notes }} run: | printf '%s\n' "$RELEASE_NOTES" > release_notes.md - name: Update CHANGELOG.md shell: bash run: | - VERSION="${{ needs.build.outputs.tag_name }}" - VERSION="${VERSION#v}" + VERSION="${{ needs.validate.outputs.version }}" RELEASE_DATE=$(date -u +%Y-%m-%d) { @@ -173,6 +179,54 @@ jobs: mv CHANGELOG.new.md CHANGELOG.md cat CHANGELOG.md >&2 + - name: Update README badges from package.json + shell: bash + run: | + node - <<'NODE' + const fs = require('fs'); + + const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); + let vscodeVersion = pkg.engines && pkg.engines.vscode ? String(pkg.engines.vscode) : ''; + + if (vscodeVersion.startsWith('^')) { + vscodeVersion = `${vscodeVersion.slice(1)}+`; + } + + const versionBadge = `[![Version](https://img.shields.io/badge/version-${pkg.version}-blue.svg)](https://github.com/secondlife/sl-vscode-plugin)`; + const licenseBadge = `[![License](https://img.shields.io/badge/license-${pkg.license}-green.svg)](LICENSE)`; + const vscodeBadge = `[![VS Code](https://img.shields.io/badge/VS%20Code-${encodeURIComponent(vscodeVersion)}-red.svg)](https://code.visualstudio.com/)`; + + let readme = fs.readFileSync('README.md', 'utf8'); + + readme = readme.replace(/^\[!\[Version\]\(https:\/\/img\.shields\.io\/badge\/version-[^)]+\)\]\(https:\/\/github\.com\/secondlife\/sl-vscode-plugin\)$/m, versionBadge); + readme = readme.replace(/^\[!\[License\]\(https:\/\/img\.shields\.io\/badge\/license-[^)]+\)\]\(LICENSE\)$/m, licenseBadge); + readme = readme.replace(/^\[!\[VS Code\]\(https:\/\/img\.shields\.io\/badge\/VS%20Code-[^)]+\)\]\(https:\/\/code\.visualstudio\.com\/\)$/m, vscodeBadge); + + fs.writeFileSync('README.md', readme); + NODE + + - name: Package modified files for downstream jobs + shell: bash + run: | + git ls-files -m > modified-files.txt + git ls-files --others --exclude-standard >> modified-files.txt + + if [ ! -s modified-files.txt ]; then + echo "No modified files detected" > no-modified-files.txt + tar -czf release-notes-files.tgz no-modified-files.txt + else + tar -czf release-notes-files.tgz -T modified-files.txt + fi + + - name: Upload modified files artifact + uses: actions/upload-artifact@v4 + with: + name: release-notes-modified-files-${{ github.run_id }} + path: | + release-notes-files.tgz + modified-files.txt + retention-days: 1 + # - name: Commit and push changelog update # shell: bash # run: | @@ -195,9 +249,68 @@ jobs: # exit 0 # fi - # git commit -m "docs: update changelog for ${{ needs.build.outputs.tag_name }}" + # git commit -m "docs: update changelog for ${{ inputs.tag_name }}" # git push origin HEAD:$TARGET_BRANCH + build: + needs: release_notes + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ inputs.target_commitish }} + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: '24.x' + cache: 'npm' + + - name: Download modified files artifact + uses: actions/download-artifact@v4 + with: + name: release-notes-modified-files-${{ github.run_id }} + path: . + + - name: Apply modified files from release_notes job + shell: bash + run: | + tar -xzf release-notes-files.tgz + + - name: Install dependencies + run: npm install + + - name: Compile extension + run: npm run vscode:prepublish + + - name: Install vsce + run: npm install -g @vscode/vsce + + - name: Package extension + run: vsce package + + - name: Get package filename + id: package + shell: bash + run: | + PACKAGE_FILE=$(ls *.vsix | head -1) + echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" + + - name: Upload VSIX artifact + uses: actions/upload-artifact@v4 + with: + name: vscode-extension-release-${{ github.run_id }} + path: ${{ steps.package.outputs.filename }} + retention-days: 30 + + release: + name: Publish release + needs: [build, release_notes] + runs-on: ubuntu-latest + + steps: - name: Download VSIX artifact uses: actions/download-artifact@v4 with: @@ -210,6 +323,17 @@ jobs: PACKAGE_FILE=$(ls *.vsix | head -1) echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" + - name: Create release page + id: release + uses: secondlife-3p/action-gh-release@v1 + with: + # name the release page for the branch + name: "SL-VScode Plugin ${{ steps.version.outputs.version }} Release" + prerelease: true + body_path: release_notes.md + target_commitish: ${{ github.sha }} + files: ${{ steps.package.outputs.filename }} + # - name: Create GitHub release # uses: secondlife-3p/action-gh-release@v1 # with: From d32f69e87d78eedb3a3126e76e0e88b3f905f2ff Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Fri, 22 May 2026 17:01:57 -0700 Subject: [PATCH 10/16] Update tags and release page. File commit and push is disable. --- .github/workflows/release.yml | 82 +++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b564c99..e399031 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -208,14 +208,21 @@ jobs: - name: Package modified files for downstream jobs shell: bash run: | + rm -rf release-notes-files + mkdir -p release-notes-files + git ls-files -m > modified-files.txt git ls-files --others --exclude-standard >> modified-files.txt if [ ! -s modified-files.txt ]; then - echo "No modified files detected" > no-modified-files.txt - tar -czf release-notes-files.tgz no-modified-files.txt + echo "No modified files detected" > release-notes-files/no-modified-files.txt else - tar -czf release-notes-files.tgz -T modified-files.txt + while IFS= read -r file; do + if [ -n "$file" ] && [ -e "$file" ]; then + mkdir -p "release-notes-files/$(dirname "$file")" + cp "$file" "release-notes-files/$file" + fi + done < modified-files.txt fi - name: Upload modified files artifact @@ -223,7 +230,7 @@ jobs: with: name: release-notes-modified-files-${{ github.run_id }} path: | - release-notes-files.tgz + release-notes-files modified-files.txt retention-days: 1 @@ -277,7 +284,36 @@ jobs: - name: Apply modified files from release_notes job shell: bash run: | - tar -xzf release-notes-files.tgz + if [ -d release-notes-files ]; then + cp -R release-notes-files/. . + fi + + - name: Commit modified files and push tag + shell: bash + run: | + TARGET_BRANCH="${{ inputs.target_commitish }}" + TARGET_BRANCH="${TARGET_BRANCH#refs/heads/}" + + if ! git ls-remote --exit-code --heads origin "$TARGET_BRANCH" > /dev/null; then + echo "target_commitish must be a branch name (or refs/heads/) to commit and tag changes" >&2 + echo "Received: ${{ inputs.target_commitish }}" >&2 + exit 1 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git add -u + + if git diff --cached --quiet; then + echo "No repository changes to commit" + # else + # git commit -m "docs: prepare release ${{ inputs.tag_name }}" + # git push origin HEAD:$TARGET_BRANCH + fi + + git tag -fa "${{ inputs.tag_name }}" -m "Release ${{ inputs.tag_name }}" + git push --force origin "refs/tags/${{ inputs.tag_name }}" - name: Install dependencies run: npm install @@ -307,14 +343,25 @@ jobs: release: name: Publish release - needs: [build, release_notes] + needs: [build, release_notes, validate] runs-on: ubuntu-latest steps: - - name: Download VSIX artifact + - name: Download release artifacts uses: actions/download-artifact@v4 with: - name: vscode-extension-release-${{ github.run_id }} + pattern: | + vscode-extension-release-${{ github.run_id }} + release-notes-modified-files-${{ github.run_id }} + merge-multiple: true + path: . + + - name: Apply modified files from release_notes job + shell: bash + run: | + if [ -d release-notes-files ]; then + cp -R release-notes-files/. . + fi - name: Get package filename id: package @@ -328,19 +375,10 @@ jobs: uses: secondlife-3p/action-gh-release@v1 with: # name the release page for the branch - name: "SL-VScode Plugin ${{ steps.version.outputs.version }} Release" - prerelease: true + tag_name: ${{ inputs.tag_name }} + target_commitish: ${{ inputs.target_commitish }} + name: "SL-VScode Plugin ${{ needs.validate.outputs.version }} Release" + prerelease: ${{ inputs.prerelease }} + draft: ${{ inputs.draft }} body_path: release_notes.md - target_commitish: ${{ github.sha }} files: ${{ steps.package.outputs.filename }} - - # - name: Create GitHub release - # uses: secondlife-3p/action-gh-release@v1 - # with: - # tag_name: ${{ needs.build.outputs.tag_name }} - # target_commitish: ${{ inputs.target_commitish }} - # name: ${{ needs.build.outputs.release_name }} - # prerelease: ${{ inputs.prerelease }} - # draft: ${{ inputs.draft }} - # body_path: release_notes.md - # files: ${{ steps.package.outputs.filename }} From 4a9d17e6eb748d6c0a67552232247afeca9aea57 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 26 May 2026 09:55:10 -0700 Subject: [PATCH 11/16] Force build. --- .github/workflows/generate-release-notes.yml | 2 +- .github/workflows/release.yml | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/generate-release-notes.yml b/.github/workflows/generate-release-notes.yml index b66a97a..ed5a7c4 100644 --- a/.github/workflows/generate-release-notes.yml +++ b/.github/workflows/generate-release-notes.yml @@ -17,7 +17,7 @@ on: value: ${{ jobs.generate.outputs.release_notes }} permissions: - contents: write + contents: read jobs: generate: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e399031..1246c1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -347,13 +347,15 @@ jobs: runs-on: ubuntu-latest steps: - - name: Download release artifacts + - name: Download VSIX artifact uses: actions/download-artifact@v4 with: - pattern: | - vscode-extension-release-${{ github.run_id }} - release-notes-modified-files-${{ github.run_id }} - merge-multiple: true + name: vscode-extension-release-${{ github.run_id }} + + - name: Download modified files artifact + uses: actions/download-artifact@v4 + with: + name: release-notes-modified-files-${{ github.run_id }} path: . - name: Apply modified files from release_notes job From d696fa200e9a93e72066c1ff0898ffcef9a3ec46 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Tue, 26 May 2026 09:55:10 -0700 Subject: [PATCH 12/16] Force build. --- .github/workflows/generate-release-notes.yml | 2 +- .github/workflows/release.yml | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/generate-release-notes.yml b/.github/workflows/generate-release-notes.yml index b66a97a..ed5a7c4 100644 --- a/.github/workflows/generate-release-notes.yml +++ b/.github/workflows/generate-release-notes.yml @@ -17,7 +17,7 @@ on: value: ${{ jobs.generate.outputs.release_notes }} permissions: - contents: write + contents: read jobs: generate: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e399031..1246c1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -347,13 +347,15 @@ jobs: runs-on: ubuntu-latest steps: - - name: Download release artifacts + - name: Download VSIX artifact uses: actions/download-artifact@v4 with: - pattern: | - vscode-extension-release-${{ github.run_id }} - release-notes-modified-files-${{ github.run_id }} - merge-multiple: true + name: vscode-extension-release-${{ github.run_id }} + + - name: Download modified files artifact + uses: actions/download-artifact@v4 + with: + name: release-notes-modified-files-${{ github.run_id }} path: . - name: Apply modified files from release_notes job From fe4d6444c7171f45ca1d46916cce17e3bc696a8c Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 27 May 2026 09:22:28 -0700 Subject: [PATCH 13/16] PLace documentation changes on a branch and create PRs into dev and main. --- .github/workflows/ci.yml | 7 +++--- .github/workflows/release.yml | 43 ++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 168b04c..1e0ffe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: push: branches: [ "*" ] + tags-ignore: [ "**" ] pull_request: branches: [ main, develop ] workflow_dispatch: @@ -78,10 +79,10 @@ jobs: - name: Update package.json version for non-main builds if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/v') run: | - # Build strict numeric version: major.minor.patch.run + # Build semver prerelease version: major.minor.patch-build. BASE_VERSION=$(node -p "(() => { const m = (require('./package.json').version.match(/\\d+/g) || []); return [m[0] || '0', m[1] || '0', m[2] || '0'].join('.'); })()") - NEW_VERSION="${BASE_VERSION}.${GITHUB_RUN_NUMBER}" - node -e "const fs=require('fs');const p=require('./package.json');p.version=process.env.NEW_VERSION;fs.writeFileSync('package.json', JSON.stringify(p, null, 2) + '\\n');" + NEW_VERSION="${BASE_VERSION}-build.${GITHUB_RUN_NUMBER}" + npm version "$NEW_VERSION" --no-git-tag-version --allow-same-version echo "Updated version to: $NEW_VERSION" - name: Package extension diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1246c1a..9a51371 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,7 @@ on: permissions: contents: write + pull-requests: write jobs: authorize_actor: @@ -288,14 +289,15 @@ jobs: cp -R release-notes-files/. . fi - - name: Commit modified files and push tag + - name: Create release branch, commit, and push tag shell: bash run: | TARGET_BRANCH="${{ inputs.target_commitish }}" TARGET_BRANCH="${TARGET_BRANCH#refs/heads/}" + RELEASE_BRANCH="release/${{ inputs.tag_name }}" if ! git ls-remote --exit-code --heads origin "$TARGET_BRANCH" > /dev/null; then - echo "target_commitish must be a branch name (or refs/heads/) to commit and tag changes" >&2 + echo "target_commitish must be a branch name (or refs/heads/) to create a release branch" >&2 echo "Received: ${{ inputs.target_commitish }}" >&2 exit 1 fi @@ -303,18 +305,46 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$RELEASE_BRANCH" git add -u if git diff --cached --quiet; then echo "No repository changes to commit" - # else - # git commit -m "docs: prepare release ${{ inputs.tag_name }}" - # git push origin HEAD:$TARGET_BRANCH + else + git commit -m "chore: prepare release ${{ inputs.tag_name }}" fi + git push origin "$RELEASE_BRANCH" + git tag -fa "${{ inputs.tag_name }}" -m "Release ${{ inputs.tag_name }}" git push --force origin "refs/tags/${{ inputs.tag_name }}" + - name: Create release PRs + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const releaseBranch = `release/${{ inputs.tag_name }}`; + const tagName = '${{ inputs.tag_name }}'; + const prTitle = `chore: prepare release ${tagName}`; + const prBody = `Automated release PR for ${tagName}.\n\nUpdates CHANGELOG.md, package.json version, and README badges.`; + + for (const base of ['develop', 'main']) { + try { + const pr = await github.rest.pulls.create({ + owner, + repo, + title: prTitle, + head: releaseBranch, + base, + body: prBody, + }); + core.info(`Created PR into ${base}: ${pr.data.html_url}`); + } catch (err) { + core.warning(`Failed to create PR into ${base}: ${err.message}`); + } + } + - name: Install dependencies run: npm install @@ -351,6 +381,7 @@ jobs: uses: actions/download-artifact@v4 with: name: vscode-extension-release-${{ github.run_id }} + path: vsix/ - name: Download modified files artifact uses: actions/download-artifact@v4 @@ -369,7 +400,7 @@ jobs: id: package shell: bash run: | - PACKAGE_FILE=$(ls *.vsix | head -1) + PACKAGE_FILE=$(ls vsix/*.vsix | head -1) echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" - name: Create release page From c5fabb81fb70bfcccc4d0f9231192c18e8592f68 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 27 May 2026 10:44:54 -0700 Subject: [PATCH 14/16] Finalize and publish workflow changes. --- .github/workflows/ci.yml | 88 +----------------- .github/workflows/pre-commit.yaml | 10 +- .github/workflows/release.yml | 73 ++++++++++++++- package-lock.json | 146 +----------------------------- package.json | 1 - 5 files changed, 79 insertions(+), 239 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e0ffe4..f284e80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,23 +6,19 @@ on: tags-ignore: [ "**" ] pull_request: branches: [ main, develop ] - workflow_dispatch: jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [24.x] steps: - name: Checkout code uses: actions/checkout@v5 - - name: Setup Node.js ${{ matrix.node-version }} + - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: ${{ matrix.node-version }} + node-version: '24.x' cache: 'npm' - name: Install dependencies @@ -41,7 +37,6 @@ jobs: - name: Run full test suite run: xvfb-run -a npm test - continue-on-error: false build: needs: test @@ -66,24 +61,9 @@ jobs: - name: Compile extension run: npm run vscode:prepublish - - name: Determine build metadata + - name: Get build SHA id: version - run: | - if [ "${{ github.ref }}" = "refs/heads/main" ]; then - echo "release_type=release" >> $GITHUB_OUTPUT - elif [ "${{ github.ref }}" = "refs/heads/debug" ]; then - echo "release_type=beta" >> $GITHUB_OUTPUT - fi - echo "sha_short=$(echo ${{ github.sha }} | cut -c1-8)" >> $GITHUB_OUTPUT - - - name: Update package.json version for non-main builds - if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/v') - run: | - # Build semver prerelease version: major.minor.patch-build. - BASE_VERSION=$(node -p "(() => { const m = (require('./package.json').version.match(/\\d+/g) || []); return [m[0] || '0', m[1] || '0', m[2] || '0'].join('.'); })()") - NEW_VERSION="${BASE_VERSION}-build.${GITHUB_RUN_NUMBER}" - npm version "$NEW_VERSION" --no-git-tag-version --allow-same-version - echo "Updated version to: $NEW_VERSION" + run: echo "sha_short=$(echo ${{ github.sha }} | cut -c1-8)" >> $GITHUB_OUTPUT - name: Package extension run: vsce package @@ -93,68 +73,10 @@ jobs: run: | PACKAGE_FILE=$(ls *.vsix | head -1) echo "filename=$PACKAGE_FILE" >> $GITHUB_OUTPUT - echo "name=$(basename "$PACKAGE_FILE" .vsix)" >> $GITHUB_OUTPUT - name: Upload VSIX artifact uses: actions/upload-artifact@v4 with: - name: vscode-extension-${{ steps.version.outputs.release_type }}-${{ steps.version.outputs.sha_short }} + name: vscode-extension-${{ steps.version.outputs.sha_short }} path: ${{ steps.package.outputs.filename }} retention-days: 30 - - release_notes: - name: Generate release notes - needs: build - if: startsWith(github.ref, 'refs/tags/v') - uses: ./.github/workflows/generate-release-notes.yml - permissions: - contents: read - with: - tag_name: ${{ github.ref_name }} - target_commitish: ${{ github.sha }} - - release: - name: Update release info - needs: [build, release_notes] - runs-on: ubuntu-latest - timeout-minutes: 10 - if: startsWith(github.ref, 'refs/tags/v') - permissions: - contents: write - - steps: - - name: Get version from tag - id: version - run: | - VERSION=${GITHUB_REF#refs/tags/v} - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - - - name: Download VSIX artifact - uses: actions/download-artifact@v4 - with: - pattern: vscode-extension-* - merge-multiple: true - - - name: Get package filename - id: package - run: | - PACKAGE_FILE=$(ls *.vsix | head -1) - echo "filename=$PACKAGE_FILE" >> $GITHUB_OUTPUT - - - name: Write release notes file - env: - RELEASE_NOTES: ${{ needs.release_notes.outputs.release_notes }} - run: | - printf '%s\n' "$RELEASE_NOTES" > release_notes.md - - - name: Create release - id: release - uses: secondlife-3p/action-gh-release@v1 - with: - # name the release page for the branch - name: "SL-VScode Plugin ${{ steps.version.outputs.version }} Release" - prerelease: true - body_path: release_notes.md - target_commitish: ${{ github.sha }} - files: ${{ steps.package.outputs.filename }} diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 18a488f..debd5c3 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -7,7 +7,6 @@ on: branches: [main, develop] permissions: - id-token: write contents: read jobs: @@ -20,18 +19,15 @@ jobs: - name: Setup Node uses: actions/setup-node@v6 with: - node-version: 'latest' + node-version: '24.x' cache: 'npm' - name: Install dependencies - run: npm install + run: npm ci - name: Setup Python uses: actions/setup-python@v6 with: python-version: '3.x' - - name: Install pre-commit - run: pip install pre-commit - - name: Run pre-commit checks - run: pre-commit run --all-files + uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9a51371..ee28306 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,11 @@ on: required: true default: false type: boolean + publish_marketplace: + description: Publish the extension to the VS Code Marketplace + required: true + default: false + type: boolean permissions: contents: write @@ -71,9 +76,30 @@ jobs: exit 1 fi - # Enforce SemVer 2.0.0 with a required leading "v" (e.g. v1.2.3, v1.2.3-rc.1, v1.2.3+build.7) - if [[ ! "${{ inputs.tag_name }}" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$ ]]; then - echo "tag_name must be valid SemVer with a leading v (for example v1.0.3 or v1.0.3-rc.1+build.7)" >&2 + # Enforce plain vX.Y.Z — no pre-release suffixes or build metadata. + # The VS Code Marketplace does not support SemVer pre-release identifiers in + # extension versions. Use an odd minor number (e.g. v1.3.0) with the + # prerelease input flag instead of a suffix like v1.2.3-rc.1. + if [[ ! "${{ inputs.tag_name }}" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then + echo "tag_name must be plain vX.Y.Z (for example v1.0.3)." >&2 + echo "The VS Code Marketplace does not support pre-release suffixes in version numbers." >&2 + echo "Use an odd minor version (e.g. v1.3.0) with the prerelease flag for pre-release builds." >&2 + exit 1 + fi + + # Enforce odd/even minor version convention: + # even minor (0, 2, 4, ...) → stable release (prerelease must be false) + # odd minor (1, 3, 5, ...) → pre-release (prerelease must be true) + MINOR=$(echo "${{ inputs.tag_name }}" | cut -d. -f2) + IS_PRERELEASE="${{ inputs.prerelease }}" + if (( MINOR % 2 == 0 )) && [[ "$IS_PRERELEASE" == "true" ]]; then + echo "Even minor version (${{ inputs.tag_name }}) must not be marked as prerelease." >&2 + echo "Even minor = stable release. Use an odd minor version for pre-release builds." >&2 + exit 1 + fi + if (( MINOR % 2 == 1 )) && [[ "$IS_PRERELEASE" == "false" ]]; then + echo "Odd minor version (${{ inputs.tag_name }}) must be marked as prerelease." >&2 + echo "Odd minor = pre-release. Use an even minor version for stable releases." >&2 exit 1 fi @@ -321,13 +347,16 @@ jobs: - name: Create release PRs uses: actions/github-script@v7 + env: + RELEASE_NOTES: ${{ needs.release_notes.outputs.release_notes }} with: script: | const { owner, repo } = context.repo; const releaseBranch = `release/${{ inputs.tag_name }}`; const tagName = '${{ inputs.tag_name }}'; const prTitle = `chore: prepare release ${tagName}`; - const prBody = `Automated release PR for ${tagName}.\n\nUpdates CHANGELOG.md, package.json version, and README badges.`; + const releaseNotes = process.env.RELEASE_NOTES || ''; + const prBody = `Automated release PR for ${tagName}.\n\nUpdates CHANGELOG.md, package.json version, and README badges.\n\n## Release Notes\n\n${releaseNotes}`; for (const base of ['develop', 'main']) { try { @@ -415,3 +444,39 @@ jobs: draft: ${{ inputs.draft }} body_path: release_notes.md files: ${{ steps.package.outputs.filename }} + + publish: + name: Publish to VS Code Marketplace + needs: [release, build, validate] + runs-on: ubuntu-latest + # Skip if publish not requested or if this is a draft release + if: ${{ inputs.publish_marketplace && !inputs.draft }} + + steps: + - name: Download VSIX artifact + uses: actions/download-artifact@v4 + with: + name: vscode-extension-release-${{ github.run_id }} + path: vsix/ + + - name: Get package filename + id: package + shell: bash + run: | + PACKAGE_FILE=$(ls vsix/*.vsix | head -1) + echo "filename=$PACKAGE_FILE" >> "$GITHUB_OUTPUT" + + - name: Publish to Marketplace + shell: bash + env: + VSCE_PAT: ${{ secrets.VSCE_PAT }} + run: | + npm install -g @vscode/vsce + + if [[ "${{ inputs.prerelease }}" == "true" ]]; then + echo "Publishing pre-release to Marketplace..." >&2 + vsce publish --pre-release --packagePath "${{ steps.package.outputs.filename }}" --pat "$VSCE_PAT" + else + echo "Publishing stable release to Marketplace..." >&2 + vsce publish --packagePath "${{ steps.package.outputs.filename }}" --pat "$VSCE_PAT" + fi diff --git a/package-lock.json b/package-lock.json index befb184..a4cc9ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sl-vscode-plugin", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sl-vscode-plugin", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", @@ -29,7 +29,6 @@ "eslint": "^9.34.0", "glob": "^11.1.0", "mocha": "^10.0.0", - "pre-commit": "^1.2.2", "typescript": "^5.9.2" }, "engines": { @@ -525,7 +524,6 @@ "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.42.0", "@typescript-eslint/types": "8.42.0", @@ -932,7 +930,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1084,13 +1081,6 @@ "dev": true, "license": "ISC" }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, "node_modules/c8": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", @@ -1329,22 +1319,6 @@ "dev": true, "license": "MIT" }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1479,7 +1453,6 @@ "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -2849,15 +2822,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-shim": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", - "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2984,78 +2948,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pre-commit": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/pre-commit/-/pre-commit-1.2.2.tgz", - "integrity": "sha512-qokTiqxD6GjODy5ETAIgzsRgnBWWQHQH2ghy86PU7mIn/wuWeTwF3otyNQZxWBwVn8XNr8Tdzj/QfUXpH+gRZA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^5.0.1", - "spawn-sync": "^1.0.15", - "which": "1.2.x" - } - }, - "node_modules/pre-commit/node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/pre-commit/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "license": "ISC", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/pre-commit/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pre-commit/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pre-commit/node_modules/which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha512-16uPglFkRPzgiUXYMi1Jf8Z5EzN1iB4V0ZtMXcHZnwsBtQhhHeCqoWw7tsUY42hJGNDWtUsVLTjakIa5BgAxCw==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3073,13 +2965,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true, - "license": "ISC" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3294,18 +3179,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/spawn-sync": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", - "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "concat-stream": "^1.4.7", - "os-shim": "^0.1.2" - } - }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -3569,20 +3442,12 @@ "node": ">= 0.8.0" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true, - "license": "MIT" - }, "node_modules/typescript": { "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3846,13 +3711,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true, - "license": "ISC" - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 88a3cb1..39e17a1 100644 --- a/package.json +++ b/package.json @@ -204,7 +204,6 @@ "eslint": "^9.34.0", "glob": "^11.1.0", "mocha": "^10.0.0", - "pre-commit": "^1.2.2", "typescript": "^5.9.2" }, "dependencies": { From 9e9d3a757531d1a2c720df8463e7b808383f71c9 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 27 May 2026 11:34:41 -0700 Subject: [PATCH 15/16] Change default branch for release from 'rider-test' to 'develop' --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee28306..dff2095 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ on: target_commitish: description: Branch or commit SHA the tag should point to required: true - default: rider-test # main + default: develop type: string prerelease: description: Mark the GitHub release as a prerelease From 889ebb3d18c648689be6d9f230918a1b969caced Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 27 May 2026 11:37:38 -0700 Subject: [PATCH 16/16] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dff2095..055859b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -242,7 +242,7 @@ jobs: git ls-files --others --exclude-standard >> modified-files.txt if [ ! -s modified-files.txt ]; then - echo "No modified files detected" > release-notes-files/no-modified-files.txt + echo "No modified files detected" >&2 else while IFS= read -r file; do if [ -n "$file" ] && [ -e "$file" ]; then @@ -342,8 +342,8 @@ jobs: git push origin "$RELEASE_BRANCH" - git tag -fa "${{ inputs.tag_name }}" -m "Release ${{ inputs.tag_name }}" - git push --force origin "refs/tags/${{ inputs.tag_name }}" + git tag -a "${{ inputs.tag_name }}" -m "Release ${{ inputs.tag_name }}" + git push origin "refs/tags/${{ inputs.tag_name }}" - name: Create release PRs uses: actions/github-script@v7