remyroy is building binaries and drafting a release #2
Workflow file for this run
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: ci-build | |
run-name: ${{ github.actor }} is building binaries and drafting a release | |
on: | |
workflow_dispatch: | |
push: | |
tags: | |
- v* | |
jobs: | |
build-binaries: | |
runs-on: ${{ matrix.os }} | |
permissions: | |
id-token: write | |
contents: read | |
attestations: write | |
strategy: | |
fail-fast: false | |
matrix: | |
os: [ubuntu-20.04, macos-13, macos-latest, windows-latest] | |
python-version: ["3.12"] | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ matrix.python-version }} | |
cache: 'pip' | |
- name: Setup variables (Linux & macOS) | |
if: ${{ startsWith(matrix.os, 'ubuntu-') || startsWith(matrix.os, 'macos-') }} | |
env: | |
MATRIX_OS: '${{ matrix.os }}' | |
run: | | |
echo "PYTHONHASHSEED=42" >> "$GITHUB_ENV" | |
SHORT_SHA=$(echo ${{ github.sha }} | cut -c -7) | |
echo "SHORT_SHA=${SHORT_SHA}" >> "$GITHUB_ENV" | |
if [[ $MATRIX_OS == ubuntu-* ]] ; | |
then | |
BUILD_SYSTEM=linux | |
fi | |
if [[ $MATRIX_OS == macos-* ]] ; | |
then | |
BUILD_SYSTEM=darwin | |
brew install coreutils | |
fi | |
BUILD_ARCHITECTURE=amd64 | |
if [[ $MATRIX_OS == *arm* ]] || [[ $MATRIX_OS == macos-latest ]] ; | |
then | |
BUILD_ARCHITECTURE=arm64 | |
fi | |
BUILD_FILE_NAME=wagyu-key-gen-${SHORT_SHA}-${BUILD_SYSTEM}-${BUILD_ARCHITECTURE} | |
mkdir "${BUILD_FILE_NAME}" | |
echo "BUILD_FILE_NAME=${BUILD_FILE_NAME}" >> "$GITHUB_ENV" | |
echo "BUILD_CONFIGS_PATH=${BUILD_CONFIGS_PATH}" >> "$GITHUB_ENV" | |
- name: Setup variables (Windows) | |
if: ${{ startsWith(matrix.os, 'windows-') }} | |
env: | |
MATRIX_OS: '${{ matrix.os }}' | |
run: | | |
echo "PYTHONHASHSEED=42" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
$env:SHORT_SHA = "${{ github.sha }}".Substring(0, 7) | |
echo ("SHORT_SHA=" + $env:SHORT_SHA) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
if ($env:MATRIX_OS.Contains("arm")) { | |
$env:BUILD_ARCHITECTURE = "arm64" | |
} | |
else { | |
$env:BUILD_ARCHITECTURE = "amd64" | |
} | |
$env:BUILD_FILE_NAME = ("wagyu-key-gen-" + $env:SHORT_SHA + "-windows-" + $env:BUILD_ARCHITECTURE) | |
echo ("BUILD_FILE_NAME=" + $env:BUILD_FILE_NAME) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
mkdir $env:BUILD_FILE_NAME | |
$env:BUILD_FILE_NAME_PATH = (".\" + $env:BUILD_FILE_NAME) | |
echo ("BUILD_FILE_NAME_PATH=" + $env:BUILD_FILE_NAME_PATH) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
- name: Build on Linux & macOS | |
if: ${{ startsWith(matrix.os, 'ubuntu-') || startsWith(matrix.os, 'macos-') }} | |
run: | | |
python -m pip install --upgrade pip | |
pip install pyinstaller | |
yarn install | |
yarn run build | |
yarn run buildcli | |
yarn run dist | |
mkdir -p output/artifacts | |
distfile=$(find dist/ -maxdepth 1 -type f \( -iname \*.AppImage -o -iname \*.dmg \)) | |
distfilename=$(basename "${distfile}") | |
distfilename="${distfilename// /.}" | |
distfilename=$(echo "$distfilename" | sed -r "s/([0-9]+.[0-9]+.[0-9])/\1-$SHORT_SHA/") | |
mv "${distfile}" "output/artifacts/${distfilename}" | |
sha256sum "output/artifacts/${distfilename}" | head -c 64 > "output/artifacts/${distfilename}.sha256" | |
- name: Build on Windows | |
if: ${{ startsWith(matrix.os, 'windows-') }} | |
run: | | |
python -m pip install --upgrade pip | |
pip install pyinstaller | |
yarn install | |
yarn run build | |
yarn run buildcliwin | |
yarn run dist | |
mkdir output\artifacts | |
$env:BINANY_FILE_NAME = (Get-ChildItem -Path "dist\" -Name -Filter *.exe) | |
Move-Item -Path ("dist\" + $env:BINANY_FILE_NAME) -Destination output\artifacts | |
$env:BINANY_NEW_FILE_NAME = $env:BINANY_FILE_NAME.Replace(' ', '.') | |
$env:BINANY_NEW_FILE_NAME = ($env:BINANY_NEW_FILE_NAME -replace '(\d+\.\d+\.\d+)',('$1-' + $env:SHORT_SHA)) | |
Rename-Item -Path ("output\artifacts\" + $env:BINANY_FILE_NAME) -NewName $env:BINANY_NEW_FILE_NAME | |
$env:CHECKSUM_FILE_NAME_PATH = ("output\artifacts\" + $env:BINANY_NEW_FILE_NAME + ".sha256") | |
certUtil -hashfile ("output\artifacts\" + $env:BINANY_NEW_FILE_NAME) SHA256 | findstr /i /v "SHA256" | findstr /i /v "CertUtil" > $env:CHECKSUM_FILE_NAME_PATH | |
- name: Generate artifacts attestation | |
uses: actions/attest-build-provenance@v1 | |
with: | |
subject-path: output/artifacts/* | |
- name: Archive production artifacts | |
uses: actions/upload-artifact@v4 | |
with: | |
name: binary-${{ matrix.os }}-${{ github.sha }}-${{ github.run_id }} | |
path: output/artifacts | |
create-release: | |
needs: [build-binaries] | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Download build binaries | |
uses: actions/download-artifact@v4 | |
with: | |
path: assets/ | |
pattern: binary-* | |
- name: Create draft release | |
uses: actions/github-script@v7 | |
env: | |
DOCKER_IMAGE_METADATA: '${{ needs.build-and-push-docker.outputs.metadata }}' | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
var path = require('path'); | |
var fs = require('fs'); | |
var tagName = ''; | |
if (context.eventName == 'push') { | |
const tagRegex = /(?:refs\/tags\/)?(v\d+\.\d+\.\d+)$/; | |
const match = context.ref.match(tagRegex); | |
if (match) { | |
tagName = match[1]; | |
} else { | |
core.setFailed(`Cannot extract the tag version from ref value '${context.ref}'.`); | |
} | |
} else if (context.eventName == 'workflow_dispatch') { | |
tagName = `dev-${context.actor}-${context.sha.substring(0, 7)}-${context.runId}`; | |
} else { | |
core.setFailed(`Unhandled triggering event.`); | |
} | |
console.log(`Creating draft release for tag ${tagName}...`) | |
console.log(`tagName: ${tagName}`); | |
console.log(`context.sha: ${context.sha}`); | |
const { data: release } = await github.rest.repos.createRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
tag_name: tagName, | |
target_commitish: context.sha, | |
draft: true, | |
generate_release_notes: true, | |
}); | |
console.log(`Release ${release.id} created.`); | |
let binariesMap = new Map(); | |
const emptyBinaryObject = { | |
system: null, | |
architecture: null, | |
binary_archive: null, | |
binary_archive_download_url: null, | |
binary_checksum: null, | |
binary_checksum_download_url: null, | |
attestation: null, | |
}; | |
const windowsSystem = 'Windows'; | |
const macOSSystem = 'macOS'; | |
const linuxSystem = 'Linux'; | |
const amd64Architecture = 'x86_64'; | |
const arm64Architecture = 'aarch64'; | |
binariesMap.set('windows-amd64', Object.assign({}, emptyBinaryObject, { | |
system: windowsSystem, | |
architecture: amd64Architecture, | |
})); | |
binariesMap.set('windows-arm64', Object.assign({}, emptyBinaryObject, { | |
system: windowsSystem, | |
architecture: arm64Architecture, | |
})); | |
binariesMap.set('darwin-amd64', Object.assign({}, emptyBinaryObject, { | |
system: macOSSystem, | |
architecture: amd64Architecture, | |
})); | |
binariesMap.set('darwin-arm64', Object.assign({}, emptyBinaryObject, { | |
system: macOSSystem, | |
architecture: arm64Architecture, | |
})); | |
binariesMap.set('linux-amd64', Object.assign({}, emptyBinaryObject, { | |
system: linuxSystem, | |
architecture: amd64Architecture, | |
})); | |
binariesMap.set('linux-arm64', Object.assign({}, emptyBinaryObject, { | |
system: linuxSystem, | |
architecture: arm64Architecture, | |
})); | |
console.log('Uploading release assets...'); | |
const archivesGlobber = await glob.create('assets/*/*') | |
for await (const file of archivesGlobber.globGenerator()) { | |
console.log(`Uploading ${path.basename(file)} to the release ${release.id}`); | |
const fileName = path.basename(file); | |
const fileContent = fs.readFileSync(file); | |
const { data: asset } = await github.rest.repos.uploadReleaseAsset({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
release_id: release.id, | |
name: fileName, | |
data: fileContent, | |
}); | |
let platform = ''; | |
if (fileName.endsWith('.exe') || fileName.endsWith('.exe.sha256')) { | |
platform = 'windows-amd64'; | |
} else if (fileName.endsWith('.AppImage') || fileName.endsWith('.AppImage.sha256')) { | |
platform = 'linux-amd64'; | |
} else if (fileName.endsWith('-arm64.dmg') || fileName.endsWith('-arm64.dmg.sha256')) { | |
platform = 'darwin-arm64'; | |
} else if (fileName.endsWith('.dmg') || fileName.endsWith('.dmg.sha256')) { | |
platform = 'darwin-amd64'; | |
} | |
const binaryDetails = binariesMap.get(platform); | |
if (fileName.endsWith('.sha256')) { | |
binariesMap.set(platform, Object.assign({}, binaryDetails, { | |
binary_checksum: fileName, | |
binary_checksum_download_url: asset.browser_download_url, | |
})); | |
} else { | |
binariesMap.set(platform, Object.assign({}, binaryDetails, { | |
binary_archive: fileName, | |
binary_archive_download_url: asset.browser_download_url, | |
})); | |
} | |
} | |
const binariesTable = [ | |
'| System | Architecture | Binary | Checksum |', | |
'|---------|--------------|--------------------|------------------------|' | |
]; | |
binariesMap.forEach((details, platform) => { | |
if ( | |
details.binary_archive !== null && | |
details.binary_archive_download_url !== null && | |
details.binary_checksum !== null && | |
details.binary_checksum_download_url !== null | |
) { | |
const system = details.system; | |
const architecture = details.architecture; | |
const binaryName = details.binary_archive; | |
const binaryUrl = details.binary_archive_download_url; | |
const checksumName = details.binary_checksum; | |
const checksumUrl = details.binary_checksum_download_url; | |
const binaryAssetUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/releases/download/${tagName}/${binaryName}`; | |
const checksumAssetUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/releases/download/${tagName}/${checksumName}`; | |
binariesTable.push(`| ${system} | ${architecture} | [${binaryName}](${binaryAssetUrl}) | [sha256](${checksumAssetUrl}) |`); | |
} | |
}); | |
const binariesTableContent = binariesTable.join('\n'); | |
const { data: workflowRun } = await github.rest.actions.getWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: context.runId, | |
}); | |
let releaseBodyTemplate = fs.readFileSync('.github/release_template.md', { encoding: 'utf8'}); | |
console.log('Removing comments in release template...'); | |
releaseBodyTemplate = releaseBodyTemplate.replaceAll(/^\[comment\]:\s*<>\s*\((.*?)\)\s*$/gm, ''); | |
releaseBodyTemplate = releaseBodyTemplate.trim(); | |
let releaseBody = releaseBodyTemplate.replaceAll('`[GENERATED-RELEASE-NOTES]`', release.body); | |
releaseBody = releaseBody.replaceAll('`[BINARIES-TABLE]`', binariesTableContent); | |
releaseBody = releaseBody.replaceAll('`[WORKFLOW-RUN-URL]`', workflowRun.html_url); | |
console.log('Updating release body with generated content and template...'); | |
const { data: updatedRelease } = await github.rest.repos.updateRelease({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
release_id: release.id, | |
tag_name: tagName, | |
target_commitish: context.sha, | |
body: releaseBody, | |
}); | |
console.log(`Release ${updatedRelease.id} updated. Explore it on ${updatedRelease.html_url}`); |