remyroy is building binaries and drafting a release #4
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 | |
mv -v dist/* "${BUILD_FILE_NAME}" | |
export ARCHIVE_FILE_NAME="${BUILD_FILE_NAME}".tar.gz | |
echo "ARCHIVE_FILE_NAME=${ARCHIVE_FILE_NAME}" >> "$GITHUB_ENV" | |
tar -zcvf "${ARCHIVE_FILE_NAME}" ./"${BUILD_FILE_NAME}" | |
mkdir -p output/artifacts | |
cp "${ARCHIVE_FILE_NAME}" output/artifacts | |
sha256sum "${ARCHIVE_FILE_NAME}" | head -c 64 > output/artifacts/"${ARCHIVE_FILE_NAME}".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 | |
Move-Item -Path dist\* -Destination $env:BUILD_FILE_NAME | |
$env:ZIP_FILE_NAME = ($env:BUILD_FILE_NAME + ".zip") | |
echo ("ZIP_FILE_NAME=" + $env:ZIP_FILE_NAME) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
Compress-Archive -Path $env:BUILD_FILE_NAME_PATH -DestinationPath $env:ZIP_FILE_NAME | |
mkdir output\artifacts | |
copy $env:ZIP_FILE_NAME output\artifacts | |
$env:CHECKSUM_FILE_NAME_PATH = ("output\artifacts\" + $env:ZIP_FILE_NAME + ".sha256") | |
certUtil -hashfile $env:ZIP_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 binaryPlatformRegex = /(\w+)-(\w+)(?=\.(?:zip|tar\.gz)(.sha256)?$)/; | |
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, | |
}); | |
const match = fileName.match(binaryPlatformRegex); | |
if (match) { | |
const platform = `${match[1]}-${match[2]}`; | |
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}`); |