Skip to content

chore: gitignore AI config paths #41

chore: gitignore AI config paths

chore: gitignore AI config paths #41

Workflow file for this run

name: Build and Publish
on:
workflow_call:
inputs:
version:
description: 'Version tag (e.g. v1.0.0)'
required: true
type: string
permissions:
contents: write
id-token: write
attestations: write
jobs:
build:
runs-on: windows-latest
env:
VT_KEY: ${{ secrets.VIRUSTOTAL_API_KEY }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Set SOURCE_DATE_EPOCH from git commit
shell: pwsh
run: |
$epoch = git log -1 --format=%ct
echo "SOURCE_DATE_EPOCH=$epoch" >> $env:GITHUB_ENV
- name: Build and run tests
run: dotnet test "RED.Tests/RED.Tests.csproj" -c Release --logger "console;verbosity=normal"
- name: Publish self-contained single-file (x64)
run: dotnet publish "RED/RED+.csproj" -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o publish-x64
- name: Publish self-contained single-file (arm64)
run: dotnet publish "RED/RED+.csproj" -c Release -r win-arm64 --self-contained true -p:PublishSingleFile=true -o publish-arm64
- name: Safety smoke on published artifact (x64)
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$exe = Join-Path $PWD 'publish-x64\RED+.exe'
if (!(Test-Path $exe)) { throw "Missing published RED+.exe at $exe" }
$root = Join-Path $env:RUNNER_TEMP ("redpp-rel-" + [guid]::NewGuid().ToString("N"))
$junctionTarget = $root + '-junction-target'
New-Item -ItemType Directory -Path $root | Out-Null
try {
$emptyDir = Join-Path $root 'empty-child'
$nonEmpty = Join-Path $root 'non-empty'
$junction = Join-Path $root 'junction'
$deny = Join-Path $root 'deny-access'
New-Item -ItemType Directory -Path $emptyDir, $nonEmpty, $junctionTarget, $deny | Out-Null
Set-Content -Path (Join-Path $nonEmpty 'keep.txt') -Value 'content' -NoNewline
New-Item -ItemType File -Path (Join-Path $root 'empty-file.txt') | Out-Null
New-Item -ItemType Junction -Path $junction -Target $junctionTarget | Out-Null
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
icacls "$deny" /deny ($currentUser + ':(OI)(CI)(R)') | Out-Null
try {
& $exe -silent -path $root -emptyfiles -mode direct | Tee-Object -Variable output
if ($LASTEXITCODE -ne 0) { throw "RED++ exited with $LASTEXITCODE`n$output" }
} finally {
icacls "$deny" /remove:d "$currentUser" | Out-Null
}
if (($output -join "`n") -notmatch 'Found 1 empty directories') { throw "Unexpected found-directory count" }
if (!(Test-Path $root)) { throw 'Root deleted despite AutoProtectRoot' }
if (Test-Path $emptyDir) { throw 'Empty child not deleted' }
if (!(Test-Path (Join-Path $nonEmpty 'keep.txt'))) { throw 'Non-empty payload deleted' }
if (!(Test-Path $junction)) { throw 'Junction deleted' }
if (!(Test-Path $junctionTarget)) { throw 'Junction target deleted' }
if (!(Test-Path $deny)) { throw 'Deny-ACL dir deleted' }
if (Test-Path (Join-Path $root 'empty-file.txt')) { throw 'Empty file not deleted' }
# Undo: restore the deleted empty directory and empty file
& $exe -undo | Tee-Object -Variable undoOutput
if ($LASTEXITCODE -ne 0) { throw "RED++ -undo exited with $LASTEXITCODE`n$undoOutput" }
if (!(Test-Path $emptyDir)) { throw 'Undo did not recreate deleted empty directory' }
if (!(Test-Path (Join-Path $root 'empty-file.txt'))) { throw 'Undo did not recreate deleted empty file' }
& $exe -version | Tee-Object -Variable ver
if (($ver -join "`n") -notmatch 'RED\+\+\s+\d+\.\d+\.\d+') { throw "Unexpected -version output: $ver" }
}
finally {
Remove-Item -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $junctionTarget -Recurse -Force -ErrorAction SilentlyContinue
}
- name: Create release archives
shell: pwsh
run: |
# x64 archive
New-Item -ItemType Directory -Path release-x64 | Out-Null
Copy-Item publish-x64\RED+.exe release-x64\
Copy-Item LICENSE release-x64\
xcopy help release-x64\help\ /E /I /Q
xcopy language release-x64\language\ /E /I /Q
Compress-Archive -Path release-x64\* -DestinationPath "RED++_${{ inputs.version }}.zip"
# arm64 archive
New-Item -ItemType Directory -Path release-arm64 | Out-Null
Copy-Item publish-arm64\RED+.exe release-arm64\
Copy-Item LICENSE release-arm64\
xcopy help release-arm64\help\ /E /I /Q
xcopy language release-arm64\language\ /E /I /Q
Compress-Archive -Path release-arm64\* -DestinationPath "RED++_${{ inputs.version }}_arm64.zip"
- name: Generate SBOM (CycloneDX)
shell: pwsh
run: |
dotnet tool install --global CycloneDX
$env:PATH += ";$env:USERPROFILE\.dotnet\tools"
dotnet-CycloneDX "RED/RED+.csproj" -o sbom -F Json -fn bom.json
Copy-Item sbom\bom.json bom.json -Force
- name: Generate checksums
shell: pwsh
run: |
$zipX64 = "RED++_${{ inputs.version }}.zip"
$zipArm = "RED++_${{ inputs.version }}_arm64.zip"
$lines = @(
"{0} {1}" -f (Get-FileHash $zipX64 -Algorithm SHA256).Hash.ToLower(), $zipX64
"{0} {1}" -f (Get-FileHash "release-x64\RED+.exe" -Algorithm SHA256).Hash.ToLower(), "RED+.exe (x64)"
"{0} {1}" -f (Get-FileHash $zipArm -Algorithm SHA256).Hash.ToLower(), $zipArm
"{0} {1}" -f (Get-FileHash "release-arm64\RED+.exe" -Algorithm SHA256).Hash.ToLower(), "RED+.exe (arm64)"
"{0} {1}" -f (Get-FileHash "bom.json" -Algorithm SHA256).Hash.ToLower(), "bom.json"
)
Set-Content -Path SHA256SUMS -Value $lines -Encoding ascii
- name: Generate winget manifest
shell: pwsh
run: |
$version = "${{ inputs.version }}".TrimStart("v")
$tag = "${{ inputs.version }}"
$zipX64 = "RED++_${{ inputs.version }}.zip"
$zipArm = "RED++_${{ inputs.version }}_arm64.zip"
$hashX64 = (Get-FileHash $zipX64 -Algorithm SHA256).Hash.ToUpperInvariant()
$hashArm = (Get-FileHash $zipArm -Algorithm SHA256).Hash.ToUpperInvariant()
$manifest = "packaging\winget\SysAdminDoc.REDplusplus.yaml"
$content = Get-Content -Path $manifest -Raw
$content = $content -replace '(?m)^PackageVersion:.*$', "PackageVersion: $version"
$content = $content -replace '(?m)^( - Architecture: x64\s*\n InstallerUrl:).*$', "`$1 https://github.com/SysAdminDoc/REDplusplus/releases/download/$tag/$zipX64"
$content = $content -replace '(?m)^( InstallerSha256:).*$(?=[\s\S]*?Architecture: x64)', "`$1 $hashX64"
$content = $content -replace '(?m)^( - Architecture: arm64\s*\n InstallerUrl:).*$', "`$1 https://github.com/SysAdminDoc/REDplusplus/releases/download/$tag/$zipArm"
# Simpler approach: rewrite the entire Installers block
$newInstallers = @"
Installers:
- Architecture: x64
InstallerUrl: https://github.com/SysAdminDoc/REDplusplus/releases/download/$tag/$zipX64
InstallerSha256: $hashX64
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: RED+.exe
PortableCommandAlias: red++
- Architecture: arm64
InstallerUrl: https://github.com/SysAdminDoc/REDplusplus/releases/download/$tag/$zipArm
InstallerSha256: $hashArm
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: RED+.exe
PortableCommandAlias: red++
"@

Check failure on line 171 in .github/workflows/build-publish.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/build-publish.yml

Invalid workflow file

You have an error in your yaml syntax on line 171
$content = $content -replace '(?ms)^Installers:.*?(?=^ManifestType:)', $newInstallers
$content = $content -replace '(?m)^PackageVersion:.*$', "PackageVersion: $version"
Set-Content -Path $manifest -Value $content -Encoding utf8
if (Get-Command winget -ErrorAction SilentlyContinue) {
winget validate $manifest
}
- name: Attest build provenance
uses: actions/attest@v4
with:
subject-path: |
RED++_${{ inputs.version }}.zip
RED++_${{ inputs.version }}_arm64.zip
release-x64/RED+.exe
release-arm64/RED+.exe
bom.json
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ inputs.version }}" "RED++_${{ inputs.version }}.zip" "RED++_${{ inputs.version }}_arm64.zip" "release-x64\RED+.exe" SHA256SUMS bom.json "packaging\winget\SysAdminDoc.REDplusplus.yaml" --title "RED++ ${{ inputs.version }}" --generate-notes
- name: VirusTotal scan
id: virustotal
if: env.VT_KEY != ''
uses: crazy-max/ghaction-virustotal@v5
with:
vt_api_key: ${{ secrets.VIRUSTOTAL_API_KEY }}
files: |
RED++_${{ inputs.version }}.zip
- name: Append VirusTotal link to release notes
if: env.VT_KEY != '' && steps.virustotal.outputs.analysis != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANALYSIS: ${{ steps.virustotal.outputs.analysis }}
shell: pwsh
run: |
$url = ($env:ANALYSIS -split ',' | Select-Object -First 1) -replace '^[^=]+=', ''
gh release edit "${{ inputs.version }}" --notes-append "`n**VirusTotal:** $url"