diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index ad5dee64..6353d64e 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -42,7 +42,10 @@ jobs: env: deps: openssl,libcurl,libssh2,zlib,nghttp2,libpq tools_path: C:\tools\phpdev - dllname: "php_swow-php${{ matrix.php-version }}-${{ matrix.arch }}-${{ (matrix.ts == '0') && 'n' || '' }}ts-${{ (matrix.image == 'windows-2019') && 'VS16' || 'VS17' }}" + # old naming style php_swow-php8.1-x64-ts-VS16.dll + # dllname: "php_swow-php${{ matrix.php-version }}-${{ matrix.arch }}-${{ (matrix.ts == '0') && 'n' || '' }}ts-${{ (matrix.image == 'windows-2019') && 'VS16' || 'VS17' }}" + # PIE compatible naming style php_swow-v1.6.0-8.4-ts-vs17.dll + dllname: "php_swow-${{ github.ref_type == 'tag' && github.ref_name || github.sha }}-${{ matrix.php-version }}-${{ (matrix.ts == '0') && 'n' || '' }}ts-${{ (matrix.image == 'windows-2019') && 'vs16' || 'vs17' }}-${{ matrix.arch }}" steps: - name: Checkout uses: actions/checkout@v4 @@ -202,6 +205,8 @@ jobs: # see https://github.com/actions/upload-artifact/blob/main/docs/MIGRATION.md#multiple-uploads-to-the-same-named-artifact name: "${{ env.dllname }}-${{ steps.getref.outputs.ref }}" path: | + LICENSE + LICENSES.full ${{ env.dllname }}.dll ${{ env.dllname }}.pdb ${{ env.dllname }}.dll.json @@ -231,6 +236,10 @@ jobs: needs: - mergedlls runs-on: windows-2019 + permissions: + id-token: write + contents: write + attestations: write steps: - name: Checkout uses: actions/checkout@v4 @@ -247,6 +256,13 @@ jobs: with: name: all-dlls path: . + - name: Attestation + id: attest + uses: actions/attest-build-provenance@v1 + with: + subject-path: | + php_swow-*.dll + php_swow-*.pdb - name: Upload all dlls shell: powershell env: @@ -255,4 +271,5 @@ jobs: .\.github\workflows\winext\uploader.ps1 ` -RelId "${{ steps.create_release.outputs.id }}" ` -Repo "${{ github.repository }}" ` - -Token "${{ github.token }}" + -Token "${{ github.token }}" ` + -AttestationBundle "${{ steps.attest.outputs.bundle-path }}" diff --git a/.github/workflows/winext/uploader.ps1 b/.github/workflows/winext/uploader.ps1 index f89982d5..6968d8ef 100644 --- a/.github/workflows/winext/uploader.ps1 +++ b/.github/workflows/winext/uploader.ps1 @@ -1,131 +1,169 @@ -# dll release uploader (via artifact) - -param ( - [int]$MaxTry=3, - [string]$Repo, - [string]$RelID, - [string]$Token -) - -$scriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition -. "$scriptPath\utils.ps1" -ToolName "uploader" -MaxTry $MaxTry - -# gh api headers -$headers = @{ - "accept"="application/vnd.github.v3+json"; - "content-type"="application/json"; - "authorization"="Bearer ${Token}"; -} - -if(-Not $Repo -Or -Not $Token -Or -Not $RelID){ - err "Needs repo name and gh token to work." - #exit 1 -} - -$RelInfo = fetchjson ` - -Uri "https://api.github.com/repos/$Repo/releases/$RelID" ` - -Headers $headers -if(!$RelInfo){ - err "Failed fetch release information" - return -} - -$match = $RelInfo."upload_url" | Select-String -Pattern "(?(?:http|https)://.+)(?\{.+\})" -$uploadUrl = ($match.Matches[0].Groups["url"]).ToString() + "?name=" - -$RunID = $null -$jobdata = $null - -$note = "`n## Hashes and notes`n`n" + ` - "| File name | Size (in bytes) | SHA256 sum | Build log | Tests result |`n" + ` - "| - | - | - | - | - |`n" - -# read all jsons for all dlls -Get-ChildItem . | Sort-Object -Property Name | ForEach-Object -Process { - if($_.Name.EndsWith(".dll")){ - $fn = $_.Name - $jsonfn = "${fn}.json" - $pdbfn = $fn.replace('.dll', '.pdb') - if (Test-Path $jsonfn -Type Leaf){ - info "Read information from $jsonfn" - $data = Get-Content $jsonfn | ConvertFrom-Json - if($fn -Ne $data.name){ - warn "Not same filename, bad json, skip it" - continue - } - if(-Not $RunID){ - $RunID = $data.runid - $jobdata = (fetchjson ` - -Headers $headers ` - -Uri "https://api.github.com/repos/$Repo/actions/runs/$RunID/jobs")."jobs" - }else{ - if ($RunID -Ne $data.runid){ - warn "Not same runid, bad json, skip it" - continue - } - } - if((Get-FileHash -Algorithm SHA256 $fn).Hash -Ne $data.hash){ - warn "Bad dll hash, skip it" - continue - } - $link = $null - foreach($job in $jobdata) { - if($job.name.ToString().Contains($data.jobname)){ - $link = $job."html_url" - info "Workflow run link is $link" - } - } - $linkstr = "[link](${link})" - if(-Not $link){ - warn "Not found work run, strange" - $linkstr = "-" - } - info "Uploading file $fn" - $ret = Invoke-WebRequest ` - -Uri "$uploadUrl$fn" ` - -Method "POST" ` - -ContentType "application/zip" ` - -Headers $headers ` - -InFile $fn - if(-Not $ret){ - warn "Failed to upload $fn" - continue - } - info "Uploading file $pdbfn" - $ret = Invoke-WebRequest ` - -Uri "$uploadUrl$pdbfn" ` - -Method "POST" ` - -ContentType "application/zip" ` - -Headers $headers ` - -InFile $pdbfn - if(-Not $ret){ - warn "Failed to upload $pdbfn" - continue - } - $size = $data.size - $hash = $data.hash - $result = $data.result - $note += "| ${fn} | ${size} | ${hash} | ${linkstr} | ${result} |`n" - } - } -} - -info "Fetching original notes" -$note = $RelInfo.body.ToString() + $note -$patch = @{ - "body"="$note"; -} | ConvertTo-Json -Compress - -info "Repost note" -$ret = fetchjson ` - -Body $patch ` - -Method "PATCH" ` - -Uri "https://api.github.com/repos/$Repo/releases/$RelID" ` - -Headers $headers -if (-Not $ret){ - err "Failed patch notes" - exit 1 -} - -info Done - +# dll release uploader (via artifact) + +param ( + [int]$MaxTry=3, + [string]$Repo, + [string]$RelID, + [string]$Token, + [string]$AttestationBundle = "" +) + +$scriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition +. "$scriptPath\utils.ps1" -ToolName "uploader" -MaxTry $MaxTry + +# gh api headers +$headers = @{ + "accept"="application/vnd.github.v3+json"; + "content-type"="application/json"; + "authorization"="Bearer ${Token}"; +} + +if(-Not $Repo -Or -Not $Token -Or -Not $RelID){ + err "Needs repo name and gh token to work." + #exit 1 +} + +$RelInfo = fetchjson ` + -Uri "https://api.github.com/repos/$Repo/releases/$RelID" ` + -Headers $headers +if(!$RelInfo){ + err "Failed fetch release information" + return +} + +$match = $RelInfo."upload_url" | Select-String -Pattern "(?(?:http|https)://.+)(?\{.+\})" +$uploadUrl = ($match.Matches[0].Groups["url"]).ToString() + "?name=" + +$RunID = $null +$jobdata = $null + +$note = "`n## Hashes and notes`n`n" + ` + "| File name | Size (in bytes) | SHA256 sum | Build log | Tests result |`n" + ` + "| - | - | - | - | - |`n" + +# read all jsons for all dlls +Get-ChildItem . | Sort-Object -Property Name | ForEach-Object -Process { + if($_.Name.EndsWith(".dll")){ + $fn = $_.Name + $jsonfn = "${fn}.json" + $pdbfn = $fn.replace('.dll', '.pdb') + if (-Not (Test-Path $jsonfn -Type Leaf)){ + continue + } + info "Read information from $jsonfn" + $data = Get-Content $jsonfn | ConvertFrom-Json + if($fn -Ne $data.name){ + warn "Not same filename, bad json, skip it" + continue + } + if(-Not $RunID){ + $RunID = $data.runid + $jobdata = (fetchjson ` + -Headers $headers ` + -Uri "https://api.github.com/repos/$Repo/actions/runs/$RunID/jobs")."jobs" + }else{ + if ($RunID -Ne $data.runid){ + warn "Not same runid, bad json, skip it" + continue + } + } + if((Get-FileHash -Algorithm SHA256 $fn).Hash -Ne $data.hash){ + warn "Bad dll hash, skip it" + continue + } + $link = $null + foreach($job in $jobdata) { + if($job.name.ToString().Contains($data.jobname)){ + $link = $job."html_url" + info "Workflow run link is $link" + } + } + $linkstr = "[link](${link})" + if(-Not $link){ + warn "Not found work run, strange" + $linkstr = "-" + } + info "Uploading file $fn" + $ret = Invoke-WebRequest ` + -Uri "$uploadUrl$fn" ` + -Method "POST" ` + -ContentType "application/zip" ` + -Headers $headers ` + -InFile $fn + if(-Not $ret){ + warn "Failed to upload $fn" + continue + } + info "Uploading file $pdbfn" + $ret = Invoke-WebRequest ` + -Uri "$uploadUrl$pdbfn" ` + -Method "POST" ` + -ContentType "application/zip" ` + -Headers $headers ` + -InFile $pdbfn + if(-Not $ret){ + warn "Failed to upload $pdbfn" + continue + } + $size = $data.size + $hash = $data.hash + $result = $data.result + $note += "| ${fn} | ${size} | ${hash} | ${linkstr} | ${result} |`n" + + # build PIE zip + $zipfn = $fn.replace('.dll', '.zip') + info "Building PIE zip $zipfn" + $zip = [System.IO.Compression.ZipFile]::Open($zipfn, [System.IO.Compression.ZipArchiveMode]::Create) + $zip.CreateEntryFromFile($fn, $fn) + $zip.CreateEntryFromFile($pdbfn, $pdbfn) + $zip.CreateEntryFromFile("LICENSE", "LICENSE") + $zip.CreateEntryFromFile("LICENSES.full", "LICENSES.full") + $zip.Dispose() + + info "Uploading PIE zip $zipfn" + $ret = Invoke-WebRequest ` + -Uri "$uploadUrl$zipfn" ` + -Method "POST" ` + -ContentType "application/zip" ` + -Headers $headers ` + -InFile $zipfn + if(-Not $ret){ + warn "Failed to upload $zipfn" + continue + } + } +} + +if ($AttestationBundle) { + info "Uploading Attestation bundle $AttestationBundle" + $ret = Invoke-WebRequest ` + -Uri "${uploadUrl}attestation.jsonl" ` + -Method "POST" ` + -ContentType "application/json" ` + -Headers $headers ` + -InFile $AttestationBundle + if(-Not $ret){ + warn "Failed to upload $AttestationBundle" + continue + } +} + +info "Fetching original notes" +$note = $RelInfo.body.ToString() + $note +$patch = @{ + "body"="$note"; +} | ConvertTo-Json -Compress + +info "Repost note" +$ret = fetchjson ` + -Body $patch ` + -Method "PATCH" ` + -Uri "https://api.github.com/repos/$Repo/releases/$RelID" ` + -Headers $headers +if (-Not $ret){ + err "Failed patch notes" + exit 1 +} + +info Done +