|
| 1 | +name: Build Upstream OptiScaler |
| 2 | + |
| 3 | +on: |
| 4 | + workflow_call: |
| 5 | + inputs: |
| 6 | + mode: |
| 7 | + description: Build mode (master or latest-any) |
| 8 | + required: true |
| 9 | + type: string |
| 10 | + publish_release: |
| 11 | + description: Publish a GitHub prerelease after a successful build |
| 12 | + required: false |
| 13 | + default: false |
| 14 | + type: boolean |
| 15 | + |
| 16 | +permissions: |
| 17 | + contents: write |
| 18 | + |
| 19 | +jobs: |
| 20 | + build: |
| 21 | + runs-on: windows-latest |
| 22 | + |
| 23 | + steps: |
| 24 | + - name: Resolve upstream target |
| 25 | + id: resolve |
| 26 | + shell: pwsh |
| 27 | + env: |
| 28 | + GH_TOKEN: ${{ github.token }} |
| 29 | + MODE: ${{ inputs.mode }} |
| 30 | + PUBLISH_RELEASE: ${{ inputs.publish_release }} |
| 31 | + run: | |
| 32 | + $repo = 'optiscaler/OptiScaler' |
| 33 | + $headers = @{ |
| 34 | + 'Accept' = 'application/vnd.github+json' |
| 35 | + 'X-GitHub-Api-Version' = '2022-11-28' |
| 36 | + } |
| 37 | +
|
| 38 | + if ($env:GH_TOKEN) { |
| 39 | + $headers['Authorization'] = "Bearer $env:GH_TOKEN" |
| 40 | + } |
| 41 | +
|
| 42 | + function Get-Json([string]$url) { |
| 43 | + Invoke-RestMethod -Headers $headers -Uri $url |
| 44 | + } |
| 45 | +
|
| 46 | + function Get-BranchHead([string]$branchName) { |
| 47 | + $encodedBranch = [System.Uri]::EscapeDataString($branchName) |
| 48 | + Get-Json "https://api.github.com/repos/$repo/branches/$encodedBranch" |
| 49 | + } |
| 50 | +
|
| 51 | + function Get-Commit([string]$sha) { |
| 52 | + Get-Json "https://api.github.com/repos/$repo/commits/$sha" |
| 53 | + } |
| 54 | +
|
| 55 | + function Slugify([string]$value) { |
| 56 | + $slug = $value.ToLowerInvariant() -replace '[^a-z0-9._-]+', '-' |
| 57 | + $slug = $slug.Trim('-') |
| 58 | + if ([string]::IsNullOrWhiteSpace($slug)) { |
| 59 | + throw "Could not slugify value: $value" |
| 60 | + } |
| 61 | + return $slug |
| 62 | + } |
| 63 | +
|
| 64 | + $mode = $env:MODE |
| 65 | + $publishRelease = $env:PUBLISH_RELEASE -eq 'true' |
| 66 | +
|
| 67 | + if ($mode -eq 'master') { |
| 68 | + $branchName = 'master' |
| 69 | + $branch = Get-BranchHead $branchName |
| 70 | + $sha = $branch.commit.sha |
| 71 | + $commit = Get-Commit $sha |
| 72 | + $commitDate = $commit.commit.committer.date |
| 73 | + } |
| 74 | + elseif ($mode -eq 'latest-any') { |
| 75 | + $branches = Get-Json "https://api.github.com/repos/$repo/branches?per_page=100" |
| 76 | + if (-not $branches) { |
| 77 | + throw 'No upstream branches found.' |
| 78 | + } |
| 79 | +
|
| 80 | + $candidates = foreach ($branch in $branches) { |
| 81 | + $sha = $branch.commit.sha |
| 82 | + $commit = Get-Commit $sha |
| 83 | + [PSCustomObject]@{ |
| 84 | + BranchName = $branch.name |
| 85 | + Sha = $sha |
| 86 | + CommitDate = [DateTimeOffset]::Parse($commit.commit.committer.date) |
| 87 | + } |
| 88 | + } |
| 89 | +
|
| 90 | + $latest = $candidates | Sort-Object CommitDate -Descending | Select-Object -First 1 |
| 91 | + $branchName = $latest.BranchName |
| 92 | + $sha = $latest.Sha |
| 93 | + $commitDate = $latest.CommitDate.ToString('o') |
| 94 | + } |
| 95 | + else { |
| 96 | + throw "Unsupported mode: $mode" |
| 97 | + } |
| 98 | +
|
| 99 | + $branchSlug = Slugify $branchName |
| 100 | + $shortSha = $sha.Substring(0, 8) |
| 101 | + $commitUrl = "https://github.com/$repo/commit/$sha" |
| 102 | +
|
| 103 | + switch ($mode) { |
| 104 | + 'master' { |
| 105 | + $tagName = "master-$shortSha" |
| 106 | + $releaseName = "master @ $shortSha" |
| 107 | + $assetName = "OptiScaler_master_${shortSha}.7z" |
| 108 | + } |
| 109 | + 'latest-any' { |
| 110 | + $tagName = "any-$branchSlug-$shortSha" |
| 111 | + $releaseName = "latest-any: $branchName @ $shortSha" |
| 112 | + $assetName = "OptiScaler_any_${branchSlug}_${shortSha}.7z" |
| 113 | + } |
| 114 | + } |
| 115 | +
|
| 116 | + $releaseBody = $commitUrl |
| 117 | + $metadataName = "$tagName.json" |
| 118 | +
|
| 119 | + "upstream_repo=$repo" >> $env:GITHUB_OUTPUT |
| 120 | + "mode=$mode" >> $env:GITHUB_OUTPUT |
| 121 | + "branch_name=$branchName" >> $env:GITHUB_OUTPUT |
| 122 | + "branch_slug=$branchSlug" >> $env:GITHUB_OUTPUT |
| 123 | + "sha=$sha" >> $env:GITHUB_OUTPUT |
| 124 | + "short_sha=$shortSha" >> $env:GITHUB_OUTPUT |
| 125 | + "commit_date=$commitDate" >> $env:GITHUB_OUTPUT |
| 126 | + "commit_url=$commitUrl" >> $env:GITHUB_OUTPUT |
| 127 | + "tag_name=$tagName" >> $env:GITHUB_OUTPUT |
| 128 | + "release_name=$releaseName" >> $env:GITHUB_OUTPUT |
| 129 | + "asset_name=$assetName" >> $env:GITHUB_OUTPUT |
| 130 | + "metadata_name=$metadataName" >> $env:GITHUB_OUTPUT |
| 131 | + "release_body=$releaseBody" >> $env:GITHUB_OUTPUT |
| 132 | + "publish_release=$($publishRelease.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT |
| 133 | +
|
| 134 | + - name: Check for existing release |
| 135 | + id: release_check |
| 136 | + if: steps.resolve.outputs.publish_release == 'true' |
| 137 | + shell: pwsh |
| 138 | + env: |
| 139 | + GH_TOKEN: ${{ github.token }} |
| 140 | + TAG_NAME: ${{ steps.resolve.outputs.tag_name }} |
| 141 | + run: | |
| 142 | + $headers = @{ |
| 143 | + 'Accept' = 'application/vnd.github+json' |
| 144 | + 'X-GitHub-Api-Version' = '2022-11-28' |
| 145 | + } |
| 146 | +
|
| 147 | + if ($env:GH_TOKEN) { |
| 148 | + $headers['Authorization'] = "Bearer $env:GH_TOKEN" |
| 149 | + } |
| 150 | +
|
| 151 | + $url = "https://api.github.com/repos/${{ github.repository }}/releases/tags/$env:TAG_NAME" |
| 152 | + $exists = $false |
| 153 | +
|
| 154 | + try { |
| 155 | + Invoke-WebRequest -Headers $headers -Uri $url | Out-Null |
| 156 | + $exists = $true |
| 157 | + } |
| 158 | + catch { |
| 159 | + $statusCode = $_.Exception.Response.StatusCode.value__ |
| 160 | + if ($statusCode -ne 404) { |
| 161 | + throw |
| 162 | + } |
| 163 | + } |
| 164 | +
|
| 165 | + $existsString = $exists.ToString().ToLowerInvariant() |
| 166 | + "release_exists=$existsString" >> $env:GITHUB_OUTPUT |
| 167 | +
|
| 168 | + - name: Skip because release already exists |
| 169 | + if: steps.resolve.outputs.publish_release == 'true' && steps.release_check.outputs.release_exists == 'true' |
| 170 | + shell: pwsh |
| 171 | + run: | |
| 172 | + Write-Host "Release tag ${{ steps.resolve.outputs.tag_name }} already exists. Skipping build." |
| 173 | +
|
| 174 | + - name: Checkout upstream source |
| 175 | + if: steps.resolve.outputs.publish_release != 'true' || steps.release_check.outputs.release_exists != 'true' |
| 176 | + uses: actions/checkout@v4 |
| 177 | + with: |
| 178 | + repository: optiscaler/OptiScaler |
| 179 | + ref: ${{ steps.resolve.outputs.sha }} |
| 180 | + submodules: recursive |
| 181 | + fetch-depth: 1 |
| 182 | + path: upstream |
| 183 | + |
| 184 | + - name: Add MSBuild to PATH |
| 185 | + if: steps.resolve.outputs.publish_release != 'true' || steps.release_check.outputs.release_exists != 'true' |
| 186 | + uses: microsoft/setup-msbuild@v2 |
| 187 | + |
| 188 | + - name: Build upstream |
| 189 | + if: steps.resolve.outputs.publish_release != 'true' || steps.release_check.outputs.release_exists != 'true' |
| 190 | + shell: pwsh |
| 191 | + working-directory: ${{ github.workspace }}\upstream |
| 192 | + run: | |
| 193 | + msbuild /m /p:Configuration=Release . /verbosity:minimal |
| 194 | +
|
| 195 | + - name: Package build output |
| 196 | + id: package |
| 197 | + if: steps.resolve.outputs.publish_release != 'true' || steps.release_check.outputs.release_exists != 'true' |
| 198 | + shell: pwsh |
| 199 | + env: |
| 200 | + ASSET_NAME: ${{ steps.resolve.outputs.asset_name }} |
| 201 | + run: | |
| 202 | + $artifactDir = "${{ github.workspace }}\upstream\x64\Release\a" |
| 203 | + if (-not (Test-Path $artifactDir)) { |
| 204 | + throw "Expected build output directory not found: $artifactDir" |
| 205 | + } |
| 206 | +
|
| 207 | + $packagePath = "${{ runner.temp }}\$env:ASSET_NAME" |
| 208 | + if (Test-Path $packagePath) { |
| 209 | + Remove-Item -Force $packagePath |
| 210 | + } |
| 211 | +
|
| 212 | + & 7z a -r $packagePath "$artifactDir\*.*" |
| 213 | + if ($LASTEXITCODE -ne 0) { |
| 214 | + throw "7z packaging failed with exit code $LASTEXITCODE" |
| 215 | + } |
| 216 | +
|
| 217 | + "package_path=$packagePath" >> $env:GITHUB_OUTPUT |
| 218 | +
|
| 219 | + - name: Write metadata |
| 220 | + id: metadata |
| 221 | + if: steps.resolve.outputs.publish_release != 'true' || steps.release_check.outputs.release_exists != 'true' |
| 222 | + shell: pwsh |
| 223 | + env: |
| 224 | + METADATA_NAME: ${{ steps.resolve.outputs.metadata_name }} |
| 225 | + UPSTREAM_REPO: ${{ steps.resolve.outputs.upstream_repo }} |
| 226 | + MODE: ${{ steps.resolve.outputs.mode }} |
| 227 | + BRANCH_NAME: ${{ steps.resolve.outputs.branch_name }} |
| 228 | + BRANCH_SLUG: ${{ steps.resolve.outputs.branch_slug }} |
| 229 | + SHA: ${{ steps.resolve.outputs.sha }} |
| 230 | + SHORT_SHA: ${{ steps.resolve.outputs.short_sha }} |
| 231 | + COMMIT_DATE: ${{ steps.resolve.outputs.commit_date }} |
| 232 | + COMMIT_URL: ${{ steps.resolve.outputs.commit_url }} |
| 233 | + TAG_NAME: ${{ steps.resolve.outputs.tag_name }} |
| 234 | + RELEASE_NAME: ${{ steps.resolve.outputs.release_name }} |
| 235 | + ASSET_NAME: ${{ steps.resolve.outputs.asset_name }} |
| 236 | + PUBLISH_RELEASE: ${{ steps.resolve.outputs.publish_release }} |
| 237 | + run: | |
| 238 | + $metadata = [ordered]@{ |
| 239 | + upstream_repo = $env:UPSTREAM_REPO |
| 240 | + mode = $env:MODE |
| 241 | + branch = $env:BRANCH_NAME |
| 242 | + branch_slug = $env:BRANCH_SLUG |
| 243 | + sha = $env:SHA |
| 244 | + short_sha = $env:SHORT_SHA |
| 245 | + commit_date = $env:COMMIT_DATE |
| 246 | + commit_url = $env:COMMIT_URL |
| 247 | + tag_name = $env:TAG_NAME |
| 248 | + release_name = $env:RELEASE_NAME |
| 249 | + asset_name = $env:ASSET_NAME |
| 250 | + publish_release = ($env:PUBLISH_RELEASE -eq 'true') |
| 251 | + built_at = (Get-Date).ToUniversalTime().ToString('o') |
| 252 | + } |
| 253 | +
|
| 254 | + $metadataPath = "${{ runner.temp }}\$env:METADATA_NAME" |
| 255 | + $metadata | ConvertTo-Json -Depth 4 | Out-File -FilePath $metadataPath -Encoding utf8 |
| 256 | +
|
| 257 | + "metadata_path=$metadataPath" >> $env:GITHUB_OUTPUT |
| 258 | +
|
| 259 | + - name: Upload workflow artifacts |
| 260 | + if: steps.resolve.outputs.publish_release != 'true' || steps.release_check.outputs.release_exists != 'true' |
| 261 | + uses: actions/upload-artifact@v4 |
| 262 | + with: |
| 263 | + name: ${{ steps.resolve.outputs.tag_name }} |
| 264 | + retention-days: 14 |
| 265 | + path: | |
| 266 | + ${{ steps.package.outputs.package_path }} |
| 267 | + ${{ steps.metadata.outputs.metadata_path }} |
| 268 | +
|
| 269 | + - name: Create prerelease |
| 270 | + if: steps.resolve.outputs.publish_release == 'true' && steps.release_check.outputs.release_exists != 'true' |
| 271 | + uses: softprops/action-gh-release@v2 |
| 272 | + with: |
| 273 | + tag_name: ${{ steps.resolve.outputs.tag_name }} |
| 274 | + name: ${{ steps.resolve.outputs.release_name }} |
| 275 | + body: ${{ steps.resolve.outputs.release_body }} |
| 276 | + prerelease: true |
| 277 | + files: | |
| 278 | + ${{ steps.package.outputs.package_path }} |
| 279 | + ${{ steps.metadata.outputs.metadata_path }} |
0 commit comments