Release #8
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: 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@v3 | |
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@v3 | |
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" |