Fix atrocity arg parsing #61
This file contains hidden or 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 Build | |
| on: | |
| pull_request: | |
| types: [closed] | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release tag to build (e.g. v0.1.7)" | |
| required: false | |
| type: string | |
| version_number: | |
| description: "Version number for compilation (e.g. 0.1.7)" | |
| required: false | |
| type: string | |
| notes: | |
| description: "Release notes override" | |
| required: false | |
| type: string | |
| permissions: | |
| contents: write | |
| actions: write | |
| checks: write | |
| jobs: | |
| prepare: | |
| name: Prepare release (check files, version, notes) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_release: ${{ steps.override.outputs.should_release || steps.check_files.outputs.should_release }} | |
| version: ${{ steps.override.outputs.version || steps.next_version.outputs.version }} | |
| version_number: ${{ steps.override.outputs.version_number || steps.next_version.outputs.version_number }} | |
| notes: ${{ steps.override.outputs.notes || steps.parse_commit.outputs.notes }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Override outputs for manual runs | |
| if: github.event_name == 'workflow_dispatch' | |
| id: override | |
| shell: pwsh | |
| run: | | |
| Write-Output "should_release=true" >> $env:GITHUB_OUTPUT | |
| if (-not [string]::IsNullOrWhiteSpace("${{ inputs.version }}")) { | |
| Write-Output "version=${{ inputs.version }}" >> $env:GITHUB_OUTPUT | |
| } | |
| if (-not [string]::IsNullOrWhiteSpace("${{ inputs.version_number }}")) { | |
| Write-Output "version_number=${{ inputs.version_number }}" >> $env:GITHUB_OUTPUT | |
| } elseif (-not [string]::IsNullOrWhiteSpace("${{ inputs.version }}")) { | |
| $verNum = "${{ inputs.version }}" | |
| if ($verNum.StartsWith("v")) { $verNum = $verNum.Substring(1) } | |
| Write-Output "version_number=$verNum" >> $env:GITHUB_OUTPUT | |
| } | |
| if (-not [string]::IsNullOrWhiteSpace("${{ inputs.notes }}")) { | |
| Write-Output "notes=${{ inputs.notes }}" >> $env:GITHUB_OUTPUT | |
| } | |
| - name: Resolve last merged PR for manual runs | |
| if: github.event_name == 'workflow_dispatch' | |
| id: resolve_pr | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| $repo = "${{ github.repository }}" | |
| $headers = @{"Authorization" = "token $env:GITHUB_TOKEN"; "Accept" = "application/vnd.github.v3+json"} | |
| $prs = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/pulls?state=closed&base=main&sort=updated&direction=desc&per_page=50" -Headers $headers -UseBasicParsing | |
| $merged = $prs | Where-Object { $_.merged_at -ne $null } | Select-Object -First 1 | |
| if (-not $merged) { | |
| Write-Error "No merged PR found for base=main" | |
| exit 1 | |
| } | |
| Write-Output "base_sha=$($merged.base.sha)" >> $env:GITHUB_OUTPUT | |
| Write-Output "head_sha=$($merged.head.sha)" >> $env:GITHUB_OUTPUT | |
| # Get the merge commit SHA and fetch its message body | |
| # This allows personalized release notes to be written in the merge commit message | |
| $mergeCommitSha = $merged.merge_commit_sha | |
| Write-Output "merge_commit_sha=$mergeCommitSha" >> $env:GITHUB_OUTPUT | |
| # Fetch the merge commit details to get its message body | |
| if (-not [string]::IsNullOrWhiteSpace($mergeCommitSha)) { | |
| try { | |
| $commitData = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/commits/$mergeCommitSha" -Headers $headers -UseBasicParsing | |
| $commitMessageParts = $commitData.commit.message -split "`n", 2 | |
| if ($commitMessageParts.Count -gt 1) { | |
| $body = $commitMessageParts[1].Trim() | |
| } else { | |
| $body = "" | |
| } | |
| } catch { | |
| Write-Output "Warning: Could not fetch merge commit body, using empty string" | |
| $body = "" | |
| } | |
| } else { | |
| $body = "" | |
| } | |
| Write-Output "merge_body=$body" >> $env:GITHUB_OUTPUT | |
| - name: Check if source files changed | |
| if: github.event_name != 'workflow_dispatch' | |
| id: check_files | |
| shell: pwsh | |
| run: | | |
| Write-Output "DEBUG: Starting check_files" | |
| Write-Output "DEBUG: PR Event Data - Base: ${{ github.event.pull_request.base.sha }} - Head: ${{ github.event.pull_request.head.sha }}" | |
| $changedFiles = git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | |
| Write-Output "DEBUG: Changed files found:" | |
| Write-Output $changedFiles | |
| $sourceChanged = $false | |
| foreach ($file in $changedFiles) { | |
| if ($file -match '\.(cpp|h|hpp|c|cc|cxx)$') { | |
| $sourceChanged = $true | |
| Write-Output "DEBUG: Source file changed: $file" | |
| break | |
| } | |
| } | |
| Write-Output "DEBUG: sourceChanged is $sourceChanged" | |
| if ($sourceChanged) { | |
| Write-Output "should_release=true" >> $env:GITHUB_OUTPUT | |
| Write-Output "DEBUG: Setting should_release=true" | |
| } else { | |
| Write-Output "should_release=false" >> $env:GITHUB_OUTPUT | |
| Write-Output "DEBUG: Setting should_release=false" | |
| } | |
| - name: Get latest release tag | |
| if: github.event_name != 'workflow_dispatch' && steps.check_files.outputs.should_release == 'true' | |
| id: get_latest_tag | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| Write-Output "DEBUG: Starting get_latest_tag" | |
| $repo = "${{ github.repository }}" | |
| Write-Output "DEBUG: Repo is $repo" | |
| $headers = @{"Authorization" = "token $env:GITHUB_TOKEN"; "Accept" = "application/vnd.github.v3+json"} | |
| # Try GitHub Releases API first (includes prereleases) | |
| try { | |
| Write-Output "DEBUG: Fetching releases from API..." | |
| $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/releases?per_page=100" -Headers $headers -UseBasicParsing | |
| Write-Output "DEBUG: Fetched $($releases.Count) releases" | |
| } catch { | |
| Write-Output "DEBUG: Failed to fetch releases from API: $_" | |
| $releases = @() | |
| } | |
| $candidates = @() | |
| foreach ($r in $releases) { | |
| Write-Output "DEBUG: Checking release tag: $($r.tag_name)" | |
| if ($r.tag_name -match 'v?(\d+)\.(\d+)\.(\d+)') { | |
| $candidates += [PSCustomObject]@{ Tag = $r.tag_name; Major = [int]$Matches[1]; Minor = [int]$Matches[2]; Patch = [int]$Matches[3] } | |
| } | |
| } | |
| if ($candidates.Count -gt 0) { | |
| Write-Output "DEBUG: Found valid candidates in releases." | |
| $sorted = $candidates | Sort-Object -Property @{Expression={$_.Major};Descending=$true}, @{Expression={$_.Minor};Descending=$true}, @{Expression={$_.Patch};Descending=$true} | |
| $latestTag = $sorted[0].Tag | |
| Write-Output "DEBUG: Selected latest tag from API: $latestTag" | |
| Write-Output "latest_tag=$latestTag" >> $env:GITHUB_OUTPUT | |
| exit 0 | |
| } | |
| # Fallback: ensure tags are fetched and inspect local tags | |
| Write-Output "DEBUG: Fallback to local git tags" | |
| git fetch --tags --prune || true | |
| $local = git tag -l "v[0-9]*.[0-9]*.[0-9]*" | |
| Write-Output "DEBUG: Local tags found: $local" | |
| if ($local) { | |
| $sortedTags = $local | ForEach-Object { | |
| if ($_ -match 'v?(\d+)\.(\d+)\.(\d+)') { | |
| [PSCustomObject]@{ Tag = $_; Major = [int]$Matches[1]; Minor = [int]$Matches[2]; Patch = [int]$Matches[3] } | |
| } | |
| } | Sort-Object -Property @{Expression={$_.Major};Descending=$true}, @{Expression={$_.Minor};Descending=$true}, @{Expression={$_.Patch};Descending=$true} | |
| if ($sortedTags.Count -gt 0) { | |
| $latestTag = $sortedTags[0].Tag | |
| Write-Output "DEBUG: Selected latest tag from local tags: $latestTag" | |
| Write-Output "latest_tag=$latestTag" >> $env:GITHUB_OUTPUT | |
| } else { | |
| Write-Output "DEBUG: No valid semantic version tags found locally. Defaulting to v0.0.0" | |
| Write-Output "latest_tag=v0.0.0" >> $env:GITHUB_OUTPUT | |
| } | |
| } else { | |
| Write-Output "DEBUG: No local tags found. Defaulting to v0.0.0" | |
| Write-Output "latest_tag=v0.0.0" >> $env:GITHUB_OUTPUT | |
| } | |
| - name: Determine next version | |
| if: github.event_name != 'workflow_dispatch' && steps.check_files.outputs.should_release == 'true' | |
| id: next_version | |
| shell: pwsh | |
| run: | | |
| $latestTag = "${{ steps.get_latest_tag.outputs.latest_tag }}" | |
| Write-Output "DEBUG: Starting next_version. Input latestTag: $latestTag" | |
| if ($latestTag -match 'v?(\d+)\.(\d+)\.(\d+)') { | |
| $major = [int]$Matches[1] | |
| $minor = [int]$Matches[2] | |
| $patch = [int]$Matches[3] | |
| $patch = $patch + 1 | |
| $newVersion = "v$major.$minor.$patch" | |
| Write-Output "DEBUG: Calculated new version: $newVersion" | |
| Write-Output "version=$newVersion" >> $env:GITHUB_OUTPUT | |
| Write-Output "version_number=$major.$minor.$patch" >> $env:GITHUB_OUTPUT | |
| } else { | |
| Write-Error "Could not parse version from tag: $latestTag" | |
| exit 1 | |
| } | |
| - name: Parse commit message for release notes | |
| if: (github.event_name == 'workflow_dispatch' && steps.override.outputs.should_release == 'true') || (github.event_name != 'workflow_dispatch' && steps.check_files.outputs.should_release == 'true') | |
| id: parse_commit | |
| shell: pwsh | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| Write-Output "DEBUG: Starting parse_commit" | |
| # Get all commits from the PR (base to head) | |
| if ("${{ github.event_name }}" -eq "workflow_dispatch") { | |
| $baseSha = "${{ steps.resolve_pr.outputs.base_sha }}" | |
| $headSha = "${{ steps.resolve_pr.outputs.head_sha }}" | |
| } else { | |
| $baseSha = "${{ github.event.pull_request.base.sha }}" | |
| $headSha = "${{ github.event.pull_request.head.sha }}" | |
| } | |
| Write-Output "DEBUG: Base SHA: $baseSha" | |
| Write-Output "DEBUG: Head SHA: $headSha" | |
| # Get the merge commit message body for personalized release notes | |
| # This extracts the extended description from the merge commit (not the PR description) | |
| # The merge commit body allows writing custom release notes during PR merge | |
| if ("${{ github.event_name }}" -eq "workflow_dispatch") { | |
| # For manual runs, use the merge commit body from resolve_pr step | |
| $mergeCommitBody = "${{ steps.resolve_pr.outputs.merge_body }}" | |
| } else { | |
| # For PR events, get the merge commit SHA and fetch its body | |
| $mergeCommitSha = "${{ github.event.pull_request.merge_commit_sha }}" | |
| Write-Output "DEBUG: Merge commit SHA: $mergeCommitSha" | |
| if (-not [string]::IsNullOrWhiteSpace($mergeCommitSha)) { | |
| try { | |
| $repo = "${{ github.repository }}" | |
| $headers = @{"Authorization" = "token $env:GITHUB_TOKEN"; "Accept" = "application/vnd.github.v3+json"} | |
| $commitData = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/commits/$mergeCommitSha" -Headers $headers -UseBasicParsing | |
| $commitMessage = $commitData.commit.message | |
| # Split the message to get just the body (everything after the first line) | |
| $messageParts = $commitMessage -split "`n", 2 | |
| if ($messageParts.Count -gt 1) { | |
| $mergeCommitBody = $messageParts[1].Trim() | |
| } else { | |
| $mergeCommitBody = "" | |
| } | |
| } catch { | |
| Write-Output "DEBUG: Could not fetch merge commit body via API, falling back to git log" | |
| # Fallback to git log if API fails | |
| $mergeCommitBody = git log -1 --pretty=format:"%b" "$mergeCommitSha" 2>$null | |
| if ([string]::IsNullOrWhiteSpace($mergeCommitBody)) { | |
| Write-Output "DEBUG: Git log fallback returned empty result for merge commit" | |
| $mergeCommitBody = "" | |
| } | |
| } | |
| } else { | |
| Write-Output "DEBUG: No merge commit SHA available, using empty body" | |
| $mergeCommitBody = "" | |
| } | |
| } | |
| Write-Output "DEBUG: Merge commit body: $mergeCommitBody" | |
| # Get all commit messages in the PR | |
| $commits = @(git log --pretty=format:"%h|%s|%b" "$baseSha..$headSha") | |
| Write-Output "DEBUG: Found $($commits.Count) commits in PR" | |
| # Initialize categorized lists | |
| $features = @() | |
| $fixes = @() | |
| $performance = @() | |
| $refactors = @() | |
| $docs = @() | |
| $tests = @() | |
| $chores = @() | |
| $builds = @() | |
| $ci = @() | |
| $style = @() | |
| $reverts = @() | |
| $others = @() | |
| # Parse each commit | |
| foreach ($line in $commits) { | |
| if ([string]::IsNullOrWhiteSpace($line)) { continue } | |
| $parts = $line -split '\|', 3 | |
| if ($parts.Count -lt 2) { continue } | |
| $commitHash = $parts[0] | |
| $subject = $parts[1] | |
| $body = if ($parts.Count -eq 3) { $parts[2] } else { "" } | |
| Write-Output "DEBUG: Processing commit $commitHash : $subject" | |
| # Parse conventional commit format | |
| if ($subject -match '^(fix|feat|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?:\s*(.+)$') { | |
| $type = $Matches[1] | |
| $scope = if ($Matches[2]) { $Matches[2] } else { "" } | |
| $description = $Matches[3] | |
| $entry = "- $description ($commitHash)" | |
| switch ($type) { | |
| 'feat' { $features += $entry } | |
| 'fix' { $fixes += $entry } | |
| 'perf' { $performance += $entry } | |
| 'refactor' { $refactors += $entry } | |
| 'docs' { $docs += $entry } | |
| 'test' { $tests += $entry } | |
| 'chore' { $chores += $entry } | |
| 'build' { $builds += $entry } | |
| 'ci' { $ci += $entry } | |
| 'style' { $style += $entry } | |
| 'revert' { $reverts += $entry } | |
| default { $others += "- $subject ($commitHash)" } | |
| } | |
| } else { | |
| # Non-conventional commit | |
| $others += "- $subject ($commitHash)" | |
| } | |
| } | |
| # Build release notes with BOTH merge commit body AND PR commit history | |
| # The merge commit body appears first (for personalized notes), followed by | |
| # categorized commit history from the PR | |
| $releaseNotes = "" | |
| # Add merge commit body at the top if it exists (personalized release notes) | |
| if (-not [string]::IsNullOrWhiteSpace($mergeCommitBody)) { | |
| $releaseNotes += "$mergeCommitBody`n`n---`n`n" | |
| } | |
| $releaseNotes += "## Changes`n`n" | |
| if ($features.Count -gt 0) { | |
| $releaseNotes += "### ✨ Features`n" | |
| $releaseNotes += ($features -join "`n") + "`n`n" | |
| } | |
| if ($fixes.Count -gt 0) { | |
| $releaseNotes += "### 🐛 Fixes`n" | |
| $releaseNotes += ($fixes -join "`n") + "`n`n" | |
| } | |
| if ($performance.Count -gt 0) { | |
| $releaseNotes += "### ⚡ Performance`n" | |
| $releaseNotes += ($performance -join "`n") + "`n`n" | |
| } | |
| if ($refactors.Count -gt 0) { | |
| $releaseNotes += "### ♻️ Refactoring`n" | |
| $releaseNotes += ($refactors -join "`n") + "`n`n" | |
| } | |
| if ($docs.Count -gt 0) { | |
| $releaseNotes += "### 📝 Documentation`n" | |
| $releaseNotes += ($docs -join "`n") + "`n`n" | |
| } | |
| if ($tests.Count -gt 0) { | |
| $releaseNotes += "### ✅ Tests`n" | |
| $releaseNotes += ($tests -join "`n") + "`n`n" | |
| } | |
| if ($builds.Count -gt 0) { | |
| $releaseNotes += "### 📦 Build`n" | |
| $releaseNotes += ($builds -join "`n") + "`n`n" | |
| } | |
| if ($chores.Count -gt 0) { | |
| $releaseNotes += "### 🔧 Chores`n" | |
| $releaseNotes += ($chores -join "`n") + "`n`n" | |
| } | |
| if ($ci.Count -gt 0) { | |
| $releaseNotes += "### 🔄 CI/CD`n" | |
| $releaseNotes += ($ci -join "`n") + "`n`n" | |
| } | |
| if ($style.Count -gt 0) { | |
| $releaseNotes += "### 💄 Style`n" | |
| $releaseNotes += ($style -join "`n") + "`n`n" | |
| } | |
| if ($reverts.Count -gt 0) { | |
| $releaseNotes += "### ⏪ Reverts`n" | |
| $releaseNotes += ($reverts -join "`n") + "`n`n" | |
| } | |
| if ($others.Count -gt 0) { | |
| $releaseNotes += "### 📋 Other Changes`n" | |
| $releaseNotes += ($others -join "`n") + "`n`n" | |
| } | |
| Write-Output "DEBUG: releaseNotes: $releaseNotes" | |
| $releaseNotes += "---`n*Based on commits from $baseSha to $headSha*" | |
| $delimiter = "EOF_$(Get-Random)" | |
| Write-Output "DEBUG: Writing notes to output using delimiter $delimiter" | |
| Write-Output "notes<<$delimiter" >> $env:GITHUB_OUTPUT | |
| Write-Output $releaseNotes >> $env:GITHUB_OUTPUT | |
| Write-Output $delimiter >> $env:GITHUB_OUTPUT | |
| build: | |
| name: Build matrix for multiple Windows architectures | |
| needs: prepare | |
| if: needs.prepare.outputs.should_release == 'true' | |
| runs-on: windows-latest | |
| strategy: | |
| matrix: | |
| arch: [x64, x86, arm64] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Compile for ${{ matrix.arch }} | |
| shell: pwsh | |
| run: | | |
| # Set version as environment variable | |
| $env:VERSION_NUMBER = "${{ needs.prepare.outputs.version }}" | |
| # Find and initialize MSVC | |
| $vsPath = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" ` | |
| -latest -products * ` | |
| -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ` | |
| -property installationPath | |
| $vcvarsPath = Join-Path $vsPath "VC\Auxiliary\Build\vcvarsall.bat" | |
| $targetArch = "${{ matrix.arch }}" | |
| $vcvarsArch = $targetArch | |
| if ($targetArch -eq "x86") { $vcvarsArch = "x64_x86" } | |
| if ($targetArch -eq "arm64") { $vcvarsArch = "x64_arm64" } | |
| # Use cmd to call vcvarsall and then cl | |
| $outName = "win-witr-${{ matrix.arch }}.exe" | |
| $verTag = "${{ needs.prepare.outputs.version }}" | |
| cmd /c "`"$vcvarsPath`" $vcvarsArch && cl /O2 /Ot /GL /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /DVERSION_NUMBER=$verTag /Fe:$outName" | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Host "Build failed with exit code $LASTEXITCODE" | |
| Exit $LASTEXITCODE | |
| } | |
| - name: Upload build artifact for ${{ matrix.arch }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: win-witr-${{ matrix.arch }} | |
| path: win-witr-${{ matrix.arch }}.exe | |
| create-release: | |
| name: Create GitHub Release with all artifacts | |
| needs: [prepare, build] | |
| if: needs.prepare.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository for tagging | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Download all build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: List artifacts | |
| run: ls -R artifacts | |
| - name: Flatten artifact directory structure | |
| run: | | |
| set -e | |
| echo "Flattening artifact directory structure..." | |
| find artifacts -name "*.exe" -type f -exec mv -n {} artifacts/ \; | |
| echo "Removing empty subdirectories..." | |
| find artifacts -mindepth 1 -type d -delete || echo "Warning: Failed to delete some subdirectories in artifacts/ - this may indicate remaining files or permission issues" | |
| echo "Flattened artifacts directory:" | |
| ls -la artifacts/ | |
| - name: Ensure release tag exists and push | |
| if: needs.prepare.outputs.version != '' | |
| shell: bash | |
| run: | | |
| set -e | |
| echo "DEBUG: Ensure Release Tag Exists" | |
| echo "DEBUG: received version: '${{ needs.prepare.outputs.version }}'" | |
| echo "DEBUG: received sha: $(git rev-parse HEAD)" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| TAG="${{ needs.prepare.outputs.version }}" | |
| echo "Preparing tag: $TAG" | |
| if [ -z "$TAG" ]; then | |
| echo "No tag provided; cannot create release" | |
| exit 1 | |
| fi | |
| if git rev-parse "$TAG" >/dev/null 2>&1; then | |
| echo "DEBUG: Tag $TAG already exists locally" | |
| else | |
| # Explicitly tag the HEAD sha to match the checked out state | |
| HEAD_SHA=$(git rev-parse HEAD) | |
| echo "DEBUG: Creating tag $TAG on $HEAD_SHA" | |
| git tag -a "$TAG" -m "Release $TAG" "$HEAD_SHA" | |
| echo "DEBUG: Created local tag $TAG" | |
| fi | |
| # Push tag (skip errors if it already exists on remote) | |
| echo "DEBUG: Pushing tag $TAG" | |
| git push origin "$TAG" || echo "Tag push failed or already exists on remote" | |
| - name: Create Release using GH CLI | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG: ${{ needs.prepare.outputs.version }} | |
| NOTES: ${{ needs.prepare.outputs.notes }} | |
| run: | | |
| TITLE="win-witr $TAG" | |
| echo "Checking if release '$TAG' exists..." | |
| if gh release view "$TAG" > /dev/null 2>&1; then | |
| echo "Release '$TAG' found. Deleting to recreate..." | |
| gh release delete "$TAG" -y | |
| else | |
| echo "Release '$TAG' does not exist." | |
| fi | |
| echo "Locating artifacts..." | |
| # Find the files anywhere in the current directory, robust to structure changes | |
| FILE_X64=$(find . -type f -name "win-witr-x64.exe" | head -n 1) | |
| FILE_X86=$(find . -type f -name "win-witr-x86.exe" | head -n 1) | |
| FILE_ARM64=$(find . -type f -name "win-witr-arm64.exe" | head -n 1) | |
| echo "Found x64 artifact: $FILE_X64" | |
| echo "Found x86 artifact: $FILE_X86" | |
| echo "Found arm64 artifact: $FILE_ARM64" | |
| if [[ -z "$FILE_X64" || -z "$FILE_X86" || -z "$FILE_ARM64" ]]; then | |
| echo "Error: Could not locate all expected artifacts." | |
| echo "Searching for any .exe files:" | |
| find . -name "*.exe" | |
| exit 1 | |
| fi | |
| echo "Creating release '$TAG'..." | |
| gh release create "$TAG" \ | |
| "$FILE_X64" \ | |
| "$FILE_X86" \ | |
| "$FILE_ARM64" \ | |
| --title "$TITLE" \ | |
| --notes "$NOTES" \ | |
| --prerelease |