Skip to content

Release

Release #9

Workflow file for this run

name: Release
on:
workflow_run:
workflows: ["Autotag"]
types:
- completed
jobs:
build:
runs-on: windows-latest
permissions:
id-token: write
contents: write
attestations: write
env:
GO_VERSION: 1.23
QUIKGO_VERSION: 1.2.6
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AUTHOR_BRIDGE_TOKEN: ${{ secrets.AUTHOR_BRIDGE_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Extract Tag from Event
id: extract_tag
shell: pwsh
run: |
Write-Host "Event payload:"
Get-Content $env:GITHUB_EVENT_PATH
# Extract the commit SHA from the event payload
$COMMIT_SHA = (Get-Content -Raw $env:GITHUB_EVENT_PATH | ConvertFrom-Json).workflow_run.head_commit.id
if (-not $COMMIT_SHA) {
Write-Host "Error: No commit SHA found in the event payload."
exit 1
}
Write-Host "Commit SHA: $COMMIT_SHA"
# Fetch tags and find the tag associated with the commit
git fetch --tags
$TAG_REF = git tag --contains $COMMIT_SHA | ForEach-Object { $_.Trim() } | Select-Object -First 1
if (-not $TAG_REF) {
Write-Host "Error: No tag found for this commit."
exit 1
}
Write-Host "Extracted tag reference: $TAG_REF"
echo "TAG=$TAG_REF" | Out-File -Append -FilePath $env:GITHUB_ENV
- name: Display Tag
shell: pwsh
run: |
Write-Host "Identified Tag: $env:TAG"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '${{ env.GO_VERSION }}'
- name: Cache Go Modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache QuikGo
uses: actions/cache@v4
with:
path: |
~/go/bin
key: ${{ runner.os }}-qgo-v${{ env.QUIKGO_VERSION }}
restore-keys: |
${{ runner.os }}-qgo-
- name: Install QuikGo
run: |
if (-not (go list -m github.com/quikdev/go/cmd/qgo@v${{ env.QUIKGO_VERSION }})) {
if (-not (go list -m github.com/quikdev/go/cmd/qgo@v${{ env.QUIKGO_VERSION }})) {
go install github.com/quikdev/go/cmd/qgo@v${{ env.QUIKGO_VERSION }}
} else {
echo "QuikGo version ${{ env.QUIKGO_VERSION }} already installed."
}
} else {
echo "QuikGo version ${{ env.QUIKGO_VERSION }} already installed."
}
- name: Confirm QuikGo Installation
run: qgo --version
- name: Build NVM for Windows
run: |
cd src
qgo build --profile=release
echo ${{ github.workspace }}\bin
dir ${{ github.workspace }}\bin
cd ..\
# - name: Compress Executables
# uses: crazy-max/ghaction-upx@v3
# with:
# version: latest
# files: |
# ${{ github.workspace }}/bin/*.exe
# args: --brute
- name: Download Resource Hacker
run: |
Invoke-WebRequest -Uri "https://www.angusj.com/resourcehacker/resource_hacker.zip" -OutFile "resource_hacker.zip"
Expand-Archive -Path "resource_hacker.zip" -DestinationPath "$env:USERPROFILE\resource_hacker"
- name: Apply icon to executable
run: |
$resourceHackerPath = "$env:USERPROFILE\resource_hacker\ResourceHacker.exe"
$exe = "${{ github.workspace }}\bin\nvm.exe"
$iconFile = "${{ github.workspace }}\assets\nvm.ico"
& $resourceHackerPath -open $exe -save $exe -action add -res $iconFile -mask ICONGROUP,MAINICON
- name: Sign Executables
uses: azure/trusted-signing-action@v0.5.0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: ${{ secrets.AZURE_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }}
certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }}
files-folder: ${{ github.workspace }}\bin
files-folder-filter: exe,dll
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Identify & Download Latest Author-NVM Bridge App (Using GitHub API)
id: get_release
run: |
$repoOwner = "author"
$repoName = "author-nvm"
$token = $env:AUTHOR_BRIDGE_TOKEN # Ensure the token is provided as a secret
# Fetch the latest release information
Write-Host "GET https://api.github.com/repos/$repoOwner/$repoName/releases"
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/$repoOwner/$repoName/releases" `
-Headers @{ Authorization = "token $token"; Accept = "application/vnd.github.v3+json"; }
if ($response -and $response[0].tag_name) {
$releaseTag = $response[0].tag_name
Write-Host "Latest release tag identified: $releaseTag"
# Fetch the release details for the identified tag
Write-Host "GET https://api.github.com/repos/$repoOwner/$repoName/releases/tags/$releaseTag"
$releaseDetails = Invoke-RestMethod -Uri "https://api.github.com/repos/$repoOwner/$repoName/releases/tags/$releaseTag" `
-Headers @{ Authorization = "token $token"; Accept = "application/vnd.github.v3+json"; }
# Extract asset ID for author-nvm.exe
$asset = $releaseDetails.assets | Where-Object { $_.name -eq 'author-nvm.exe' } | Select-Object -First 1
if ($asset -and $asset.id) {
$assetId = $asset.id
Write-Host "Asset ID for author-nvm.exe: $assetId"
"ASSET_ID=$assetId" | Out-File -Append -FilePath $env:GITHUB_ENV
# Download asset
$assetDownloadUrl = "https://api.github.com/repos/$repoOwner/$repoName/releases/assets/$assetId"
Write-Host "GET $assetDownloadUrl"
Invoke-WebRequest -Uri $assetDownloadUrl `
-Headers @{ Authorization = "token $token"; Accept = "application/octet-stream"; } `
-OutFile "$env:GITHUB_WORKSPACE\bin\author-nvm.exe"
} else {
Write-Error "author-nvm.exe not found in the latest release."
exit 1
}
} else {
Write-Error "No release tags found in the repository."
exit 1
}
- name: Generate Core Assets
run: |
$bin = "${{ github.workspace }}\bin"
$license = "${{ github.workspace }}\LICENSE"
$source = "${{ github.workspace }}\assets"
$target = "${{ github.workspace }}\.tmp"
$distros = "${{ github.workspace }}\.dist"
if (!(Test-Path -Path $target)) {
New-Item -ItemType Directory -Path $target
}
# Copy binaries to distribution
Get-ChildItem -Path $bin -File | ForEach-Object {
Copy-Item -Path $_.FullName -Destination $target
}
# Copy assets to distribution
Get-ChildItem -Path $source -File | ForEach-Object {
Copy-Item -Path $_.FullName -Destination $target
}
# Copy license to distribution
Copy-Item -Path $license -Destination $target
shell: pwsh
- name: Generate Asset Distribution
run: |
$source = "${{ github.workspace }}\.tmp"
$manual = "${{ github.workspace }}\.dist\nvm-noinstall.zip"
$checksum = "${{ github.workspace }}\.dist\nvm-noinstall.zip.checksum.txt"
if (Test-Path -Path $manual) {
Remove-Item -Path $manual -Force
}
& "C:\Program Files\7-Zip\7z.exe" a -tzip $manual "$source\*" -mx=9
$hash = Get-FileHash -Path $manual -Algorithm MD5
$hash.Hash | Out-File -FilePath $checksum -Encoding utf8
shell: pwsh
# - name: Install Inno Setup
# run: |
# choco install innosetup -y
# # Verify installation
# if (!(Test-Path "C:\Program Files (x86)\Inno Setup 6\ISCC.exe")) {
# Write-Error "Inno Setup installation failed."
# exit 1
# }
- name: Generate Installer
run: |
$iss = "${{ github.workspace }}\nvm.iss"
$distros = "${{ github.workspace }}\.dist"
if (!(Test-Path -Path $iss)) {
Write-Error "Inno Setup Script file not found: $iss"
exit 1
}
# Replace version placeholder in the .iss file
$version = $env:TAG -replace "^v", ""
(Get-Content -Path $iss) -replace "{{VERSION}}", $version | Set-Content -Path $iss
# Output the file to assure replacements worked
Get-Content $iss
# Get the files and directories in the source directory
$items = Get-ChildItem -Path $source -Recurse
$source = "${{ github.workspace }}\.tmp"
$destination = "${{ github.workspace }}\bin"
foreach ($item in $items) {
# Construct the target path
$target = $item.FullName -replace [regex]::Escape($source), [regex]::Escape($destination)
if ($item.PSIsContainer) {
# Create directories if they don't exist
if (-not (Test-Path -Path $target)) {
New-Item -ItemType Directory -Path $target
}
} else {
# Copy files if they don't already exist
if (-not (Test-Path -Path $target)) {
Copy-Item -Path $item.FullName -Destination $target
}
}
}
Write-Host "Available assets in bin directory:"
dir ${{ github.workspace }}\bin
Write-Host "Available assets in .tmp directory:"
dir ${{ github.workspace }}\.tmp
Write-Host "Available build tools:"
dir ${{ github.workspace }}\assets\buildtools
${{ github.workspace }}\assets\buildtools\iscc.exe "$iss" "/o$distros"
# iscc "$iss" "/o$distros"
shell: pwsh
- name: Sign Installer
uses: azure/trusted-signing-action@v0.5.0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.INSTALLER_AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.INSTALLER_AZURE_CLIENT_SECRET }}
endpoint: ${{ secrets.AZURE_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }}
certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }}
files-folder: ${{ github.workspace }}\.dist
files-folder-filter: exe,dll
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
- name: Generate Official Distribution
run: |
$source = "${{ github.workspace }}\.dist\nvm-setup.exe"
$distro = "${{ github.workspace }}\.dist\nvm-setup.zip"
$checksum = "${{ github.workspace }}\.dist\nvm-setup.zip.checksum.txt"
if (!(Test-Path -Path $source)) {
Write-Error "Source file for ZIP not found: $source"
exit 1
}
& "C:\Program Files\7-Zip\7z.exe" a -tzip $distro "$source" -mx=9
if (!(Test-Path -Path $distro)) {
Write-Error "ZIP file generation failed."
exit 1
}
$hash = Get-FileHash -Path $distro -Algorithm MD5
$hash.Hash | Out-File -FilePath $checksum -Encoding utf8
shell: pwsh
- name: Attestation
uses: actions/attest-build-provenance@v2
id: attest
with:
subject-path: |
${{ github.workspace }}/bin/*.exe
${{ github.workspace }}/.dist/*.exe
${{ github.workspace }}/.dist/*.zip
${{ github.workspace }}/.dist/*.checksum.txt
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Release
uses: softprops/action-gh-release@v2
if: success()
with:
tag_name: ${{ env.TAG }}
files: |
${{ github.workspace }}/.dist/*
fail_on_unmatched_files: true
generate_release_notes: true
prerelease: false
draft: false
make_latest: true
- name: Add Attestation Report to Release Notes
if: success()
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.TAG }}
body: |
<br/>:office: **[Attestation Report](${{ steps.attest.outputs.attestation-url }})**
append_body: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
rollback:
runs-on: ubuntu-latest
needs: build
if: failure()
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Ensures that all references, including tags, are fetched
ref: ${{ github.event.workflow_run.head_sha }}
- name: Show current commit
run: |
git log -1 --decorate
git tag --contains HEAD
- name: Get the commit associated with the trigger
id: get_commit
run: |
if [ -f "$GITHUB_EVENT_PATH" ]; then
# Try to get the head SHA from workflow_run
COMMIT_SHA=$(jq -r '.head_commit.id' < "$GITHUB_EVENT_PATH")
if [ "$COMMIT_SHA" = "null" ]; then
COMMIT_SHA=$(jq -r '.workflow_run.head_commit.id' < "$GITHUB_EVENT_PATH")
fi
if [ -z "$COMMIT_SHA" ] || [ "$COMMIT_SHA" = "null" ]; then
echo "Error: Unable to retrieve commit SHA from GITHUB_EVENT_PATH"
cat "$GITHUB_EVENT_PATH"
echo "HEAD: $(git rev-parse HEAD)"
exit 1
fi
echo "Commit SHA: $COMMIT_SHA"
echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV
else
echo "Error: GITHUB_EVENT_PATH not found"
exit 1
fi
- name: Fetch the tag associated with the commit using git
id: fetch_tag
run: |
# Debugging: print all tags in the repository
git tag
# Get the tag associated with the commit
echo "Finding tag associated with the commit..."
TAG=$(git tag --contains "$COMMIT_SHA" | head -n 1)
echo "Tag associated with the commit is: $TAG"
# Debugging: Check if the tag is found
if [ -z "$TAG" ]; then
echo "No tag found for this commit."
else
echo "Found tag: $TAG"
fi
echo "TAG=$TAG" >> $GITHUB_ENV
- name: Delete release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Use the TAG from the environment variable
if [ -z "$TAG" ]; then
echo "No tag to delete."
exit 1
fi
RELEASE_ID=$(gh release view "$TAG" --json id --jq '.id' || echo "")
if [ -n "$RELEASE_ID" ]; then
gh release delete "$TAG" --yes
echo "Release $TAG deleted successfully."
else
echo "Release $TAG not found."
fi
- name: Delete tag associated with release
run: |
# Use the TAG from the environment variable
if [ -z "$TAG" ]; then
echo "No tag to delete."
exit 1
fi
git tag -d "$TAG"
git push --delete origin "$TAG"
- name: Notify Rollback
run: echo "Rollback completed for release $TAG"