chore(release): update version and changelog for v1.8.1 #43
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Existing tag to release (e.g. v1.2.3). Tag must already exist — use only for emergency re-runs.' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| actions: read | |
| concurrency: | |
| group: release-${{ github.ref_name }} | |
| cancel-in-progress: true | |
| jobs: | |
| detect-tag: | |
| name: Detect release tag and readiness | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: write | |
| actions: read | |
| outputs: | |
| release_tag: ${{ steps.tag.outputs.release_tag }} | |
| ready: ${{ steps.tag.outputs.ready }} | |
| is_prerelease: ${{ steps.tag.outputs.is_prerelease }} | |
| steps: | |
| - name: Resolve tag from successful workflow runs | |
| id: tag | |
| uses: actions/github-script@v8 | |
| env: | |
| RELEASE_TAG_INPUT: ${{ inputs.tag }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| let tagName; | |
| if (context.eventName === 'workflow_dispatch') { | |
| const tagInput = process.env.RELEASE_TAG_INPUT?.trim() ?? ''; | |
| if (!tagInput) { | |
| core.setFailed('tag input is required for workflow_dispatch.'); | |
| return; | |
| } | |
| tagName = tagInput.startsWith('v') ? tagInput : `v${tagInput}`; | |
| } else { | |
| tagName = context.ref.replace('refs/tags/', ''); | |
| } | |
| if (!tagName || !tagName.startsWith('v')) { | |
| core.info(`Ref '${context.ref}' is not a release tag; skipping.`); | |
| core.setOutput('release_tag', ''); | |
| core.setOutput('ready', 'false'); | |
| return; | |
| } | |
| const refData = await github.rest.git.getRef({ | |
| owner, | |
| repo, | |
| ref: `tags/${tagName}`, | |
| }); | |
| const refObj = refData.data.object; | |
| const taggedSha = | |
| refObj.type === 'tag' | |
| ? (await github.rest.git.getTag({ owner, repo, tag_sha: refObj.sha })).data.object.sha | |
| : refObj.sha; | |
| const main = await github.rest.repos.getBranch({ owner, repo, branch: 'main' }); | |
| const mainSha = main.data.commit.sha; | |
| if (context.eventName !== 'workflow_dispatch' && taggedSha !== mainSha) { | |
| core.setFailed( | |
| `Release blocked: tag ${tagName} points to ${taggedSha}, but latest main is ${mainSha}.` | |
| ); | |
| return; | |
| } | |
| // Guard: fail if a GitHub Release already exists for this tag. | |
| try { | |
| await github.rest.repos.getReleaseByTag({ owner, repo, tag: tagName }); | |
| core.setFailed(`Release ${tagName} already exists on GitHub. Delete it first if you want to re-release.`); | |
| return; | |
| } catch (e) { | |
| if (e.status !== 404) throw e; | |
| // 404 = no release yet, proceed | |
| } | |
| // Verify that the CI workflow passed for this commit. | |
| const ciRuns = await github.rest.actions.listWorkflowRuns({ | |
| owner, | |
| repo, | |
| workflow_id: 'ci.yml', | |
| head_sha: taggedSha, | |
| status: 'completed', | |
| per_page: 5, | |
| }); | |
| const successfulRun = ciRuns.data.workflow_runs.find(r => r.conclusion === 'success'); | |
| if (!successfulRun) { | |
| core.setFailed( | |
| `Release blocked: no successful CI run found for commit ${taggedSha}. Push to main/tag and wait for CI to pass before releasing.` | |
| ); | |
| return; | |
| } | |
| core.info(`CI run ${successfulRun.id} passed for commit ${taggedSha}.`); | |
| core.info(`Release tag detected: ${tagName} on commit ${taggedSha}; tag verified.`); | |
| const isPrerelease = tagName.includes('-'); | |
| core.setOutput('release_tag', tagName); | |
| core.setOutput('ready', 'true'); | |
| core.setOutput('is_prerelease', String(isPrerelease)); | |
| release: | |
| name: Build and Release | |
| needs: detect-tag | |
| if: needs.detect-tag.outputs.release_tag != '' && needs.detect-tag.outputs.ready == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| deployments: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.detect-tag.outputs.release_tag }} | |
| fetch-depth: 0 | |
| - name: Generate release notes for changelog | |
| id: changelog_notes | |
| uses: actions/github-script@v8 | |
| env: | |
| RELEASE_TAG: ${{ needs.detect-tag.outputs.release_tag }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const currentTag = process.env.RELEASE_TAG?.trim() ?? ''; | |
| const tagsResp = await github.rest.repos.listTags({ | |
| owner, | |
| repo, | |
| per_page: 100, | |
| }); | |
| const tags = tagsResp.data; | |
| const index = tags.findIndex(tag => tag.name === currentTag); | |
| const previousTag = | |
| index >= 0 | |
| ? (tags.slice(index + 1).find(tag => tag.name.startsWith('v'))?.name ?? '') | |
| : ''; | |
| const notesResponse = await github.rest.repos.generateReleaseNotes({ | |
| owner, | |
| repo, | |
| tag_name: currentTag, | |
| ...(previousTag ? { previous_tag_name: previousTag } : {}), | |
| target_commitish: 'main', | |
| }); | |
| core.info( | |
| previousTag | |
| ? `Generated changelog notes from ${previousTag} to ${currentTag}.` | |
| : `Generated changelog notes for ${currentTag} (no previous tag found).` | |
| ); | |
| core.setOutput('previous_tag', previousTag); | |
| core.setOutput('notes', notesResponse.data.body ?? ''); | |
| - name: Write release notes to file | |
| run: printf '%s' "$RELEASE_NOTES" > release-notes.md | |
| env: | |
| RELEASE_NOTES: ${{ steps.changelog_notes.outputs.notes }} | |
| # NOTE: Version/changelog mutation and commit/tag operations were moved to | |
| # local automation (`scripts/deploy.sh`) to comply with branch rules that | |
| # require PR-based changes on main. | |
| - name: Skip repo mutation in release workflow | |
| run: echo "Version/changelog updates are performed locally before tagging." | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| cache: 'pnpm' | |
| - name: Setup task runner | |
| uses: go-task/setup-task@01a4adf9db2d14c1de7a560f09170b6e0df736aa # v2.1.0 | |
| with: | |
| version: "3.x" | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build extension package | |
| run: xvfb-run -a task compile | |
| - name: Package extension | |
| run: | | |
| if [ "${{ needs.detect-tag.outputs.is_prerelease }}" = "true" ]; then | |
| pnpm dlx @vscode/vsce package --pre-release --no-dependencies --out opilot.vsix | |
| else | |
| pnpm dlx @vscode/vsce package --no-dependencies --out opilot.vsix | |
| fi | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.detect-tag.outputs.release_tag }} | |
| body_path: release-notes.md | |
| files: 'opilot.vsix' | |
| draft: false | |
| prerelease: ${{ needs.detect-tag.outputs.is_prerelease == 'true' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create VS Code Marketplace deployment (pre-release) | |
| id: deploy_vsce_pre | |
| if: needs.detect-tag.outputs.is_prerelease == 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const deployment = await github.rest.repos.createDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: '${{ needs.detect-tag.outputs.release_tag }}', | |
| environment: 'vscode-marketplace', | |
| description: 'Publish pre-release to VS Code Marketplace', | |
| auto_merge: false, | |
| required_contexts: [], | |
| production_environment: false, | |
| }); | |
| core.setOutput('deployment_id', deployment.data.id); | |
| - name: Publish pre-release to VS Code Marketplace | |
| id: publish_vsce_pre | |
| if: needs.detect-tag.outputs.is_prerelease == 'true' | |
| run: pnpm dlx @vscode/vsce publish --pre-release --allow-all-proposed-apis --packagePath opilot.vsix -p ${{ secrets.VSCE_PAT }} | |
| env: | |
| VSCE_PAT: ${{ secrets.VSCE_PAT }} | |
| - name: Update VS Code Marketplace deployment status (pre-release) | |
| if: always() && needs.detect-tag.outputs.is_prerelease == 'true' && steps.deploy_vsce_pre.outputs.deployment_id != '' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const outcome = '${{ steps.publish_vsce_pre.outcome }}'; | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: ${{ steps.deploy_vsce_pre.outputs.deployment_id }}, | |
| state: outcome === 'success' ? 'success' : 'failure', | |
| description: `VS Code Marketplace pre-release publish ${outcome}`, | |
| environment_url: 'https://marketplace.visualstudio.com/items?itemName=selfagency.opilot', | |
| auto_inactive: false, | |
| }); | |
| - name: Create Open VSX deployment (pre-release) | |
| id: deploy_ovsx_pre | |
| if: needs.detect-tag.outputs.is_prerelease == 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const deployment = await github.rest.repos.createDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: '${{ needs.detect-tag.outputs.release_tag }}', | |
| environment: 'open-vsx', | |
| description: 'Publish pre-release to Open VSX Registry', | |
| auto_merge: false, | |
| required_contexts: [], | |
| production_environment: false, | |
| }); | |
| core.setOutput('deployment_id', deployment.data.id); | |
| - name: Publish pre-release to Open VSX | |
| id: publish_ovsx_pre | |
| if: needs.detect-tag.outputs.is_prerelease == 'true' | |
| run: pnpm dlx ovsx publish opilot.vsix --pre-release -p ${{ secrets.OVSX_PAT }} | |
| env: | |
| OVSX_PAT: ${{ secrets.OVSX_PAT }} | |
| - name: Update Open VSX deployment status (pre-release) | |
| if: always() && needs.detect-tag.outputs.is_prerelease == 'true' && steps.deploy_ovsx_pre.outputs.deployment_id != '' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const outcome = '${{ steps.publish_ovsx_pre.outcome }}'; | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: ${{ steps.deploy_ovsx_pre.outputs.deployment_id }}, | |
| state: outcome === 'success' ? 'success' : 'failure', | |
| description: `Open VSX pre-release publish ${outcome}`, | |
| environment_url: 'https://open-vsx.org/extension/selfagency/opilot', | |
| auto_inactive: false, | |
| }); | |
| - name: Create VS Code Marketplace deployment | |
| id: deploy_vsce | |
| if: needs.detect-tag.outputs.is_prerelease != 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const deployment = await github.rest.repos.createDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: '${{ needs.detect-tag.outputs.release_tag }}', | |
| environment: 'vscode-marketplace', | |
| description: 'Publish to VS Code Marketplace', | |
| auto_merge: false, | |
| required_contexts: [], | |
| production_environment: true, | |
| }); | |
| core.setOutput('deployment_id', deployment.data.id); | |
| - name: Publish to VS Code Marketplace | |
| id: publish_vsce | |
| if: needs.detect-tag.outputs.is_prerelease != 'true' | |
| run: pnpm dlx @vscode/vsce publish --allow-all-proposed-apis --packagePath opilot.vsix -p ${{ secrets.VSCE_PAT }} | |
| env: | |
| VSCE_PAT: ${{ secrets.VSCE_PAT }} | |
| - name: Update VS Code Marketplace deployment status | |
| if: always() && needs.detect-tag.outputs.is_prerelease != 'true' && steps.deploy_vsce.outputs.deployment_id != '' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const outcome = '${{ steps.publish_vsce.outcome }}'; | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: ${{ steps.deploy_vsce.outputs.deployment_id }}, | |
| state: outcome === 'success' ? 'success' : 'failure', | |
| description: `VS Code Marketplace publish ${outcome}`, | |
| environment_url: 'https://marketplace.visualstudio.com/items?itemName=selfagency.opilot', | |
| auto_inactive: false, | |
| }); | |
| - name: Create Open VSX deployment | |
| id: deploy_ovsx | |
| if: needs.detect-tag.outputs.is_prerelease != 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const deployment = await github.rest.repos.createDeployment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: '${{ needs.detect-tag.outputs.release_tag }}', | |
| environment: 'open-vsx', | |
| description: 'Publish to Open VSX Registry', | |
| auto_merge: false, | |
| required_contexts: [], | |
| production_environment: true, | |
| }); | |
| core.setOutput('deployment_id', deployment.data.id); | |
| - name: Publish to Open VSX | |
| id: publish_ovsx | |
| if: needs.detect-tag.outputs.is_prerelease != 'true' | |
| run: pnpm dlx ovsx publish opilot.vsix -p ${{ secrets.OVSX_PAT }} | |
| env: | |
| OVSX_PAT: ${{ secrets.OVSX_PAT }} | |
| - name: Update Open VSX deployment status | |
| if: always() && needs.detect-tag.outputs.is_prerelease != 'true' && steps.deploy_ovsx.outputs.deployment_id != '' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const outcome = '${{ steps.publish_ovsx.outcome }}'; | |
| await github.rest.repos.createDeploymentStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| deployment_id: ${{ steps.deploy_ovsx.outputs.deployment_id }}, | |
| state: outcome === 'success' ? 'success' : 'failure', | |
| description: `Open VSX publish ${outcome}`, | |
| environment_url: 'https://open-vsx.org/extension/selfagency/opilot', | |
| auto_inactive: false, | |
| }); |