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: |
: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"