Step 3 - Release VSIX packages #278
This file contains 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: Step 3 - Release VSIX packages | |
on: | |
workflow_run: | |
workflows: ["Step 2 - Build VSIX packages"] | |
types: | |
- completed | |
workflow_dispatch: | |
inputs: | |
force_release: | |
description: 'Override any failure conditions in uploading release artifacts, to release to Open VSX' | |
type: boolean | |
default: false | |
required: false | |
jobs: | |
release: | |
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout Repo | |
uses: actions/checkout@v4 | |
with: | |
ref: main | |
fetch-depth: 0 | |
- name: Find Latest Successful CI Run | |
id: find-ci-run | |
uses: actions/github-script@v6 | |
with: | |
script: | | |
const runs = await github.rest.actions.listWorkflowRuns({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
workflow_id: "ci-build.yml", | |
status: "success", | |
per_page: 1 | |
}); | |
if (runs.data.workflow_runs.length === 0) { | |
throw new Error("No successful workflow runs found"); | |
} | |
return runs.data.workflow_runs[0].id; | |
- name: Set Workflow Run ID | |
run: echo "WORKFLOW_RUN_ID=${{ steps.find-ci-run.outputs.result }}" >> $GITHUB_ENV | |
- name: Download all build artifacts | |
uses: actions/github-script@v6 | |
with: | |
script: | | |
const fs = require('fs'); | |
const path = require('path'); | |
const runId = process.env.WORKFLOW_RUN_ID; | |
const outputPath = './vsix-release'; | |
async function downloadArtifact(url, dest) { | |
const response = await fetch(url, { | |
//headers: { | |
// Authorization: `Bearer ${process.env.GH_TOKEN}`, | |
//} | |
}); | |
if (!response.ok) throw new Error(`Failed to fetch ${url} - ${response.statusText}`); | |
const fileStream = fs.createWriteStream(dest); | |
return new Promise((resolve, reject) => { | |
response.body.pipe(fileStream); | |
response.body.on('error', reject); | |
fileStream.on('finish', resolve); | |
}); | |
} | |
async function main() { | |
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: runId, | |
}); | |
if (!fs.existsSync(outputPath)) { | |
fs.mkdirSync(outputPath, { recursive: true }); | |
} | |
for (const artifact of artifacts.artifacts) { | |
core.notice(`Getting redirect URL for artifact ${artifact.id} ${artifact.name}`) | |
const result = await github.rest.actions.downloadArtifact({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
artifact_id: artifact.id, | |
archive_format: 'zip', | |
}); | |
const dest = path.join(outputPath, `${artifact.name}.zip`); | |
core.notice(`Downloading ${result.url} to ${dest}`) | |
await downloadArtifact(result.url, dest); | |
} | |
} | |
main().catch(error => { | |
core.setFailed(error.message); | |
}); | |
env: | |
WORKFLOW_RUN_ID: ${{ env.WORKFLOW_RUN_ID }} | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- name: Extract build artifacts | |
run: | | |
for zipfile in ./vsix-release/*.zip; do | |
base_name=$(basename "$zipfile" .zip) | |
extract_dir="vsix-release/${base_name}_tmp/" | |
7z x "$zipfile" -o"$extract_dir" > /dev/null | |
extracted_file=$(find "$extract_dir" -type f) | |
extension="${extracted_file##*.}" | |
echo "$extracted_file" | |
mv "$extracted_file" "vsix-release/${base_name}" | |
rm -rf "$extract_dir" | |
done | |
- name: Display structure of downloaded files | |
run: | | |
ls -R ./vsix-release | |
- name: Get package version | |
run: node -e "console.log('VERSION=' + require('./package.json').version)" >> $GITHUB_ENV | |
- name: Set Force Release Override from Manual Worfklow Dispatch | |
if: github.event_name == 'workflow_dispatch' | |
run: echo "FORCE_RELEASE=${{ github.event.inputs.force_release }}" >> $GITHUB_ENV | |
- name: Set Force Release Override from Commit Message | |
if: github.event_name == 'push' | |
run: | | |
if [[ "$(git log -1 --pretty=%B)" == *"[force-release]"* ]]; then | |
echo "FORCE_RELEASE=true" >> $GITHUB_ENV | |
else | |
echo "FORCE_RELEASE=false" >> $GITHUB_ENV | |
fi | |
- name: Create GitHub Release | |
id: create_release | |
uses: actions/github-script@v6 | |
continue-on-error: true | |
with: | |
script: | | |
try { | |
const response = await github.rest.repos.createRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
tag_name: `v${process.env.VERSION}`, | |
name: `Release ${process.env.VERSION}`, | |
draft: false, | |
prerelease: false, | |
}); | |
core.setOutput('upload_url', response.data.upload_url); | |
} catch (error) { | |
if (error.status === 422 && error.message.includes('already_exists')) { | |
console.log('Release already exists. Retrieving existing release upload URL.'); | |
const releases = await github.rest.repos.listReleases({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
}); | |
const existingRelease = releases.data.find(release => release.tag_name === `v${process.env.VERSION}`); | |
if (existingRelease) { | |
core.setOutput('upload_url', existingRelease.upload_url); | |
} else { | |
throw new Error('Release exists but could not find it.'); | |
} | |
} else { | |
throw error; | |
} | |
} | |
- name: Upload release builds | |
uses: actions/github-script@v6 | |
with: | |
script: | | |
const fs = require('fs'); | |
const path = require('path'); | |
const forceReleaseVar = process.env.FORCE_RELEASE | |
const forceRelease = forceReleaseVar === "true" || forceReleaseVar === true | |
if (forceRelease) { | |
console.log("FORCE_RELEASE is true - errors will be logged but ignored") | |
} | |
const releasesPath = path.join(process.env.GITHUB_WORKSPACE, 'vsix-release') | |
const releasePlatforms = [ | |
'darwin-x64', 'linux-x64', 'win32-x64', | |
'platform-neutral' | |
] | |
const artifacts = releasePlatforms.map( | |
platform => ({ | |
path: path.join(releasesPath, `csharp-${platform}.vsix`), | |
name: `csharp-${platform}-${process.env.VERSION}.vsix` | |
}) | |
) | |
const uploadedArtifacts = [] | |
for (const artifact of artifacts) { | |
if (fs.existsSync(artifact.path)) { | |
console.log(`Uploading ${artifact.name}...`); | |
await github.rest.repos.uploadReleaseAsset({ | |
url: '${{ steps.create_release.outputs.upload_url }}', | |
headers: { | |
'content-type': 'application/zip', | |
'content-length': fs.statSync(artifact.path).size | |
}, | |
name: artifact.name, | |
data: fs.readFileSync(artifact.path) | |
}).then(response => { | |
console.log(`Uploaded ${artifact.name}`); | |
uploadedArtifacts.push(artifact) | |
}).catch(error => { | |
const msg = `Failed to upload ${artifact.name}: ${error}` | |
if (forceRelease) { | |
console.log(msg) | |
} else { | |
console.error(msg); | |
} | |
}); | |
} else { | |
console.log(`File ${artifact.path} does not exist. Skipping upload.`); | |
} | |
} | |
if (!uploadedArtifacts.length) { | |
const msg = "Did not find any artifacts to upload" | |
if (forceRelease) { | |
console.log(msg) | |
} else { | |
throw new Error(msg) | |
} | |
} | |
- name: Install Node.js 18.x | |
uses: actions/setup-node@v4 | |
with: | |
node-version: '18.x' | |
- name: Publish to Open-VSX | |
run: | | |
npm ci | |
npm i -g ovsx | |
ovsx publish --packagePath $(find ./vsix-release -iname *.vsix) -p ${{ secrets.OPEN_VSX_TOKEN }} |