Port AKV, MST, and AAS extension packs with zero-copy adaptation #1108
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
| #### Build, Test, and Publish #### | |
| # This is the main workflow for the CoseSignTool project. It handles the following events: | |
| # - Pull requests: When a user submits a pull request, or pushes a commit to an existing pull request, this workflow | |
| # - generates a changelog and commits it to the working branch, and then | |
| # - builds and tests the code. | |
| # - Pushes to the main branch: When a user pushes a commit to the main branch, this workflow | |
| # - creates a semantically versioned tag, | |
| # - creates a release with the new tag, and then | |
| # - triggers the release portion of the workflow. | |
| # - Releases: When a user creates a release, or a release is created in response to a push event, this workflow | |
| # - builds, publishes, and zips the outputs, and then | |
| # - uploads the zipped assets to the release. | |
| name: Build, Test, and Publish | |
| on: | |
| pull_request: | |
| branches: [ "**" ] # Trigger on all branches for pull requests. | |
| push: | |
| branches: [ "main" ] # Trigger on pushes to the main branch. | |
| release: | |
| types: [ created ] # Trigger on new releases. | |
| workflow_dispatch: # Allow manual triggering from the Actions tab. | |
| jobs: | |
| #### CHANGE DETECTION #### | |
| # Determine which paths changed so downstream jobs only run when relevant. | |
| detect-changes: | |
| name: detect-changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| native: ${{ steps.filter.outputs.native }} | |
| dotnet: ${{ steps.filter.outputs.dotnet }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check changed paths | |
| id: filter | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| native: | |
| - 'native/**' | |
| dotnet: | |
| - '**/*.cs' | |
| - '**/*.csproj' | |
| - '**/*.sln' | |
| - '*.props' | |
| - '*.targets' | |
| - 'Directory.Build.props' | |
| - 'Directory.Packages.props' | |
| - 'Nuget.config' | |
| - 'global.json' | |
| #### PULL REQUEST EVENTS #### | |
| # Build and test the code. | |
| build: | |
| name: dotnet-build | |
| needs: [ detect-changes ] | |
| if: ${{ needs.detect-changes.outputs.dotnet == 'true' }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| include: | |
| - os: windows-latest | |
| dir_command: gci -Recurse | |
| - os: ubuntu-latest | |
| dir_command: ls -a -R | |
| - os: macos-latest | |
| runtime_id: osx-x64 | |
| dir_command: ls -a -R | |
| - os: macos-latest | |
| runtime_id: osx-arm64 | |
| dir_command: ls -a -R | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v3 | |
| with: | |
| dotnet-version: 8.0.x | |
| # Show dotnet info for debugging architecture issues | |
| - name: Show .NET info | |
| run: dotnet --info | |
| # Use the Dotnet Test command to load dependencies, build, and test the code. | |
| # We have to run the test projects individually so CoseSignTool.Tests can run under arm64 on the Mac runner. | |
| - name: Build and Test debug | |
| id: build-test | |
| run: | | |
| dotnet build --configuration Debug CoseSignTool.sln | |
| dotnet test --no-restore CoseSign1.Tests/CoseSign1.Tests.csproj | |
| dotnet test --no-restore CoseSign1.Certificates.Tests/CoseSign1.Certificates.Tests.csproj | |
| dotnet test --no-restore CoseSign1.Certificates.AzureArtifactSigning.Tests/CoseSign1.Certificates.AzureArtifactSigning.Tests.csproj | |
| dotnet test --no-restore CoseSign1.Headers.Tests/CoseSign1.Headers.Tests.csproj | |
| dotnet test --no-restore CoseIndirectSignature.Tests/CoseIndirectSignature.Tests.csproj | |
| dotnet test --no-restore CoseSign1.Transparent.Tests/CoseSign1.Transparent.Tests.csproj | |
| dotnet test --no-restore CoseSign1.Transparent.MST.Tests/CoseSign1.Transparent.MST.Tests.csproj | |
| dotnet test --no-restore CoseHandler.Tests/CoseHandler.Tests.csproj | |
| dotnet test --no-restore CoseSignTool.Tests/CoseSignTool.Tests.csproj | |
| dotnet test --no-restore CoseSignTool.Abstractions.Tests/CoseSignTool.Abstractions.Tests.csproj | |
| dotnet test --no-restore CoseSignTool.MST.Plugin.Tests/CoseSignTool.MST.Plugin.Tests.csproj | |
| dotnet test --no-restore CoseSignTool.IndirectSignature.Plugin.Tests/CoseSignTool.IndirectSignature.Plugin.Tests.csproj | |
| # Build Release to catch issues the internal ADO pipeline would hit (e.g. netstandard2.0 API surface differences). | |
| - name: Build Release | |
| run: dotnet build --configuration Release CoseSignTool.sln | |
| # List the contents of the working directory to make sure all the artifacts are there. | |
| - name: List working directory | |
| run: ${{ matrix.dir_command }} | |
| # ── Native Rust: lint, build, test, coverage ───────────────────────── | |
| native-rust: | |
| name: native-rust | |
| needs: [ detect-changes ] | |
| if: ${{ needs.detect-changes.outputs.native == 'true' }} | |
| runs-on: windows-latest | |
| env: | |
| VCPKG_ROOT: C:\vcpkg | |
| OPENSSL_DIR: C:\vcpkg\installed\x64-windows | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Cache vcpkg packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: C:\vcpkg\installed | |
| key: vcpkg-openssl-x64-windows-v1 | |
| - name: Install OpenSSL via vcpkg | |
| shell: pwsh | |
| run: | | |
| if (Test-Path "$env:VCPKG_ROOT\installed\x64-windows\lib\libssl.lib") { | |
| Write-Host "OpenSSL already cached" -ForegroundColor Green | |
| } else { | |
| & "$env:VCPKG_ROOT\vcpkg" install openssl:x64-windows | |
| } | |
| - name: Setup Rust (stable) | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - name: Cache Rust build artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: native/rust -> target | |
| - name: Rust format check | |
| shell: pwsh | |
| working-directory: native/rust | |
| run: | | |
| # Per-package to avoid Windows OS error 206 (command line too long) | |
| $members = (cargo metadata --no-deps --format-version 1 | ConvertFrom-Json).packages.name | |
| foreach ($pkg in $members) { | |
| cargo fmt -p $pkg -- --check | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| } | |
| # FFI crates with test=false exclude test files from cargo fmt. | |
| # Check them directly with rustfmt. | |
| Get-ChildItem -Path . -Filter '*.rs' -Recurse | | |
| Where-Object { $_.FullName -match 'ffi[\\/]tests[\\/]' } | | |
| ForEach-Object { | |
| rustfmt --check $_.FullName | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| } | |
| - name: Rust clippy | |
| shell: pwsh | |
| working-directory: native/rust | |
| run: | | |
| $env:PATH = "$env:VCPKG_ROOT\installed\x64-windows\bin;$env:PATH" | |
| cargo clippy --workspace -- -D warnings | |
| - name: Setup Rust (nightly, for coverage) | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| components: llvm-tools-preview | |
| - name: Cache cargo-llvm-cov | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/bin/cargo-llvm-cov* | |
| key: cargo-llvm-cov-${{ runner.os }} | |
| - name: Install cargo-llvm-cov | |
| shell: pwsh | |
| run: | | |
| if (Get-Command cargo-llvm-cov -ErrorAction SilentlyContinue) { | |
| Write-Host "cargo-llvm-cov already cached" -ForegroundColor Green | |
| } else { | |
| cargo install cargo-llvm-cov --locked | |
| } | |
| - name: Build Rust workspace | |
| shell: pwsh | |
| working-directory: native/rust | |
| run: | | |
| $env:PATH = "$env:VCPKG_ROOT\installed\x64-windows\bin;$env:PATH" | |
| cargo build --workspace --exclude cose-openssl | |
| - name: Test Rust workspace | |
| shell: pwsh | |
| working-directory: native/rust | |
| run: | | |
| $env:PATH = "$env:VCPKG_ROOT\installed\x64-windows\bin;$env:PATH" | |
| cargo test --workspace --exclude cose-openssl | |
| - name: Rust coverage (90% line gate) | |
| shell: pwsh | |
| working-directory: native/rust | |
| run: | | |
| $env:PATH = "$env:VCPKG_ROOT\installed\x64-windows\bin;$env:PATH" | |
| pwsh -NoProfile -File collect-coverage.ps1 -NoHtml | |
| # ── Native C/C++: build, test, coverage (ASAN) ──────────────────── | |
| native-c-cpp: | |
| name: native-c-cpp | |
| needs: [ detect-changes ] | |
| if: ${{ needs.detect-changes.outputs.native == 'true' }} | |
| runs-on: windows-latest | |
| env: | |
| VCPKG_ROOT: C:\vcpkg | |
| OPENSSL_DIR: C:\vcpkg\installed\x64-windows | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Cache vcpkg packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: C:\vcpkg\installed | |
| key: vcpkg-openssl-x64-windows-v1 | |
| - name: Install OpenSSL via vcpkg | |
| shell: pwsh | |
| run: | | |
| if (Test-Path "$env:VCPKG_ROOT\installed\x64-windows\lib\libssl.lib") { | |
| Write-Host "OpenSSL already cached" -ForegroundColor Green | |
| } else { | |
| & "$env:VCPKG_ROOT\vcpkg" install openssl:x64-windows | |
| } | |
| - name: Setup Rust (stable) | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Setup Rust (nightly, for coverage) | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| components: llvm-tools-preview | |
| - name: Cache Rust build artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: native/rust -> target | |
| - name: Install OpenCppCoverage | |
| shell: pwsh | |
| run: | | |
| choco install opencppcoverage -y --no-progress | |
| - name: Native ASAN coverage (Rust + C + C++, 90% gate) | |
| shell: pwsh | |
| run: | | |
| $env:PATH = "$env:VCPKG_ROOT\installed\x64-windows\bin;$env:PATH" | |
| ./native/collect-coverage-asan.ps1 -Configuration Debug -MinimumLineCoveragePercent 90 | |
| # Generate and commit a changelog on every push to main. | |
| # On pull requests this job passes without committing because: | |
| # - Fork PRs cannot receive pushes via GITHUB_TOKEN (GitHub security boundary). | |
| # - The changelog is auto-generated from merged PRs, so it only needs to be | |
| # up-to-date on main, not in every PR branch. | |
| # Commits made with GITHUB_TOKEN do not trigger new workflow runs, so there is | |
| # no risk of an infinite loop. | |
| create_changelog: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| #### PUSH TO MAIN — generate, commit, and push the changelog #### | |
| - name: Checkout main | |
| if: ${{ github.event_name == 'push' }} | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Configure git | |
| if: ${{ github.event_name == 'push' }} | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| - name: Generate changelog | |
| if: ${{ github.event_name == 'push' }} | |
| uses: tj-actions/github-changelog-generator@v1.19 | |
| with: | |
| output: CHANGELOG.md | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Commit changelog | |
| if: ${{ github.event_name == 'push' }} | |
| run: | | |
| git add CHANGELOG.md | |
| if git diff-index --quiet HEAD; then | |
| echo "No changelog changes to commit." | |
| else | |
| git commit -m "Update changelog" | |
| git push | |
| fi | |
| #### PULL REQUEST — nothing to commit; just pass #### | |
| - name: Skip changelog commit for PRs | |
| if: ${{ github.event_name == 'pull_request' }} | |
| run: echo "Changelog will be updated automatically when this PR is merged to main." | |
| #### OTHER EVENTS — nothing to do #### | |
| - name: No-op for other events | |
| if: ${{ github.event_name != 'push' && github.event_name != 'pull_request' }} | |
| run: echo "Changelog is already up to date." | |
| #### PUSH EVENTS #### | |
| # Create a semantically versioned release. | |
| # A prerelease is created for every push to the main branch. | |
| # Official releases are created manually on GitHub. | |
| create_release: | |
| name: Create Release | |
| if: ${{ (github.event_name == 'push' && needs.detect-changes.outputs.dotnet == 'true') || github.event_name == 'release' }} | |
| needs: [ detect-changes, create_changelog ] # Ensure changelog is committed before tagging. | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: write | |
| contents: write | |
| deployments: write | |
| packages: write | |
| pull-requests: write | |
| security-events: write | |
| statuses: write | |
| outputs: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} | |
| tag_name: ${{ steps.output_tag_name.outputs.tag_name }} | |
| steps: | |
| # Checkout the main branch and fetch tags. | |
| - name: Checkout code | |
| if: ${{ github.event_name == 'push' }} | |
| uses: actions/checkout@v3 | |
| # Checkout the main branch so we can see the correct tag set. | |
| - name: Fetch and checkout main | |
| if: ${{ github.event_name == 'push' }} | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| git fetch | |
| git checkout main | |
| # Create a semantically versioned tag that increments the last release. | |
| # If the last release is a pre-release, increment the pre-release number, so v1.2.3-pre4 becomes v1.2.3-pre5. | |
| # If the last release is an official release, create a new pre-release, so v1.2.3 becomes v1.2.3-pre1. | |
| - name: Increment pre-release tag | |
| if: ${{ github.event_name == 'push' }} | |
| id: new-tag # Output: ${{ steps.new-tag.outputs.newtag }} | |
| run: | | |
| CURRENT_TAG=$(git tag | sort --version-sort | tail -n1) | |
| echo "Current tag is $CURRENT_TAG" | |
| if [[ $CURRENT_TAG =~ ^v([0-9]+\.[0-9]+\.[0-9]+)(-pre([0-9]+))?$ ]]; then | |
| BASE_VERSION=${BASH_REMATCH[1]} | |
| PRE_VERSION=${BASH_REMATCH[3]} | |
| if [ -z "$PRE_VERSION" ]; then | |
| NEW_TAG="v$BASE_VERSION-pre1" | |
| else | |
| NEW_TAG="v$BASE_VERSION-pre$((PRE_VERSION + 1))" | |
| fi | |
| echo "New tag is $NEW_TAG" | |
| echo "Let's make sure this tag doesn't already exist..." | |
| tries=0 | |
| maxTries=5 | |
| while true; do | |
| echo "This is try $tries" | |
| RESPONSE=$(curl -sl https://api.github.com/repos/microsoft/CoseSignTool/releases/tags/$NEW_TAG) | |
| if [ "$(echo "$RESPONSE" | jq -r '.message')" == "Not Found" ]; then | |
| echo "Tag not found. We're good to go." | |
| break | |
| else | |
| if [ $tries -ge $maxTries ]; then | |
| echo "Max tries reached. Exiting." | |
| exit 1 | |
| fi | |
| echo "Oops! That tag already exists!" | |
| NEW_TAG="${NEW_TAG%-*}-pre$(( ${NEW_TAG##*-pre} + 1 ))" # Increment the prerelease number. | |
| echo "Let's try $NEW_TAG" | |
| tries=$((tries+1)) | |
| fi | |
| done | |
| echo "::set-output name=newtag::$NEW_TAG" | |
| else | |
| echo "Invalid tag format" | |
| exit 1 | |
| fi | |
| # Create the release. This should generate a release event, which will trigger the release_assets job. | |
| - name: Create Release | |
| if: ${{ github.event_name == 'push' }} | |
| id: create_release | |
| uses: actions/create-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| # Get the tag name and release name from the previous step. | |
| tag_name: ${{ steps.new-tag.outputs.newtag }} | |
| release_name: Release ${{ steps.new-tag.outputs.newtag }} | |
| # Generate release text from changelog. | |
| body_path: ./CHANGELOG.md | |
| # Always use prerelease for automated releases. Official releases are created manually. | |
| prerelease: true | |
| # Output the semver tag if it's a push event, or the most recent tag if it's a release event. | |
| - name: Output tag name | |
| id: output_tag_name | |
| run: | | |
| if [ "${{ github.event_name }}" == "push" ]; then | |
| echo "::set-output name=tag_name::${{ steps.new-tag.outputs.newtag }}" | |
| echo "Generated semver tag is ${{ steps.new-tag.outputs.newtag }}." | |
| else | |
| echo "::set-output name=tag_name::${{ github.event.release.tag_name }}" | |
| echo "Current release tag is ${{ github.event.release.tag_name }}." | |
| fi | |
| #### RELEASE EVENTS #### | |
| # Build, publish, and zip the outputs, and then upload them to the release. | |
| # We include the push event and the dependency on create_release to support automatic releases, because | |
| # automatic release creation does not trigger the release event. | |
| release_assets: | |
| name: release-assets | |
| if: ${{ github.event_name == 'release' || (github.event_name == 'push' && needs.detect-changes.outputs.dotnet == 'true') }} | |
| needs: [ detect-changes, create_release ] | |
| runs-on: ${{ matrix.os }} | |
| permissions: | |
| actions: write | |
| contents: write | |
| deployments: write | |
| packages: write | |
| pull-requests: write | |
| security-events: write | |
| statuses: write | |
| strategy: | |
| matrix: | |
| include: | |
| - os: windows-latest | |
| runtime_id: win-x64 | |
| dir_command: gci -Recurse | |
| zip_command_debug: Compress-Archive -Path ./debug/ -DestinationPath CoseSignTool-Windows-debug.zip | |
| zip_command_release: Compress-Archive -Path ./release/ -DestinationPath CoseSignTool-Windows-release.zip | |
| - os: ubuntu-latest | |
| runtime_id: linux-x64 | |
| dir_command: ls -a -R | |
| zip_command_debug: zip -r CoseSignTool-Linux-debug.zip ./debug/ | |
| zip_command_release: zip -r CoseSignTool-Linux-release.zip ./release/ | |
| - os: macos-latest | |
| runtime_id: osx-x64 | |
| dir_command: ls -a -R | |
| zip_command_debug: zip -r CoseSignTool-MacOS-x64-debug.zip ./debug/ | |
| zip_command_release: zip -r CoseSignTool-MacOS-x64-release.zip ./release/ | |
| - os: macos-latest | |
| runtime_id: osx-arm64 | |
| dir_command: ls -a -R | |
| zip_command_debug: zip -r CoseSignTool-MacOS-arm64-debug.zip ./debug/ | |
| zip_command_release: zip -r CoseSignTool-MacOS-arm64-release.zip ./release/ | |
| steps: | |
| # Checkout the branch. | |
| - name: Checkout code again | |
| uses: actions/checkout@v3 | |
| # Restore all projects including plugins. | |
| # Plugin projects are not direct ProjectReferences of CoseSignTool — they are discovered | |
| # by glob in the BuildPlugins MSBuild target. Because of this, `dotnet publish` only | |
| # restores CoseSignTool and its transitive project references, leaving plugin projects | |
| # without a project.assets.json. Restoring the full solution up front ensures plugins | |
| # are restored before the BuildPlugins target tries to compile them. | |
| - name: Restore all projects | |
| run: dotnet restore CoseSignTool.sln --runtime ${{ matrix.runtime_id }} | |
| # Build and publish the binaries to ./published. | |
| # Plugins are automatically built and deployed via BuildPlugins and DeployAllPlugins targets (enabled by default) | |
| # PublishSingleFile=true creates a single self-contained executable with plugins bundled inside | |
| - name: Publish outputs | |
| run: | | |
| VERSION=${{ needs.create_release.outputs.tag_name }} | |
| # Remove the 'v' prefix from VERSION for VersionNgt property | |
| VERSION_WITHOUT_V=$(echo "$VERSION" | sed 's/^v//') | |
| RUNTIME_ID=${{ matrix.runtime_id }} | |
| echo "Publishing single-file self-contained executable for runtime: $RUNTIME_ID" | |
| echo "Plugins will be bundled inside the executable" | |
| dotnet publish --no-restore --configuration Debug --self-contained true --runtime $RUNTIME_ID --output published/debug --property:FileVersion=$VERSION --property:VersionNgt=$VERSION_WITHOUT_V --property:PublishSingleFile=true CoseSignTool/CoseSignTool.csproj | |
| dotnet publish --no-restore --configuration Release --self-contained true --runtime $RUNTIME_ID --output published/release --property:FileVersion=$VERSION --property:VersionNgt=$VERSION_WITHOUT_V --property:PublishSingleFile=true CoseSignTool/CoseSignTool.csproj | |
| # PublishSingleFile=true bundles everything into a single executable: | |
| # - The .NET runtime (self-contained) | |
| # - All plugins (bundled and extracted on first run) | |
| # - No separate plugins folder needed - everything is in the exe | |
| shell: bash | |
| # Verify the single-file executable was created correctly | |
| # With PublishSingleFile=true and IncludeAllContentForSelfExtract=true, plugins are bundled INSIDE the exe | |
| - name: Verify single-file executable | |
| run: | | |
| Write-Host "Verifying single-file self-contained executable..." | |
| Write-Host "" | |
| # Check debug output | |
| Write-Host "=== Debug build ===" | |
| $debugExe = Get-ChildItem "published/debug/CoseSignTool*" -File | Where-Object { $_.Extension -eq '.exe' -or $_.Extension -eq '' } | Select-Object -First 1 | |
| if ($debugExe) { | |
| $sizeMB = [math]::Round($debugExe.Length / 1MB, 2) | |
| Write-Host "✅ Found: $($debugExe.Name) ($sizeMB MB)" | |
| # Plugins are bundled inside, so exe should be > 40MB (contains runtime + plugins) | |
| if ($sizeMB -gt 40) { | |
| Write-Host "✅ Size indicates plugins are bundled (expected for single-file with plugins)" | |
| } else { | |
| Write-Host "⚠️ Size seems small - plugins may not be bundled correctly" | |
| } | |
| } else { | |
| Write-Host "❌ CoseSignTool executable not found in debug output!" | |
| exit 1 | |
| } | |
| # Check that plugins folder does NOT exist (should be bundled in exe) | |
| if (Test-Path "published/debug/plugins") { | |
| Write-Host "⚠️ Plugins folder exists - it should be cleaned up for single-file publish" | |
| } else { | |
| Write-Host "✅ No external plugins folder (correctly bundled in exe)" | |
| } | |
| Write-Host "" | |
| Write-Host "=== Release build ===" | |
| $releaseExe = Get-ChildItem "published/release/CoseSignTool*" -File | Where-Object { $_.Extension -eq '.exe' -or $_.Extension -eq '' } | Select-Object -First 1 | |
| if ($releaseExe) { | |
| $sizeMB = [math]::Round($releaseExe.Length / 1MB, 2) | |
| Write-Host "✅ Found: $($releaseExe.Name) ($sizeMB MB)" | |
| if ($sizeMB -gt 40) { | |
| Write-Host "✅ Size indicates plugins are bundled" | |
| } else { | |
| Write-Host "⚠️ Size seems small - plugins may not be bundled correctly" | |
| } | |
| } else { | |
| Write-Host "❌ CoseSignTool executable not found in release output!" | |
| exit 1 | |
| } | |
| if (Test-Path "published/release/plugins") { | |
| Write-Host "⚠️ Plugins folder exists - it should be cleaned up for single-file publish" | |
| } else { | |
| Write-Host "✅ No external plugins folder (correctly bundled in exe)" | |
| } | |
| Write-Host "" | |
| Write-Host "Single-file verification complete. Plugins are bundled inside the executable." | |
| Write-Host "On first run, the exe will extract to a temp directory including the plugins folder." | |
| shell: pwsh | |
| # List the contents of the published directory to make sure all the artifacts are there. | |
| - name: List published directory | |
| run: ${{ matrix.dir_command }} | |
| working-directory: ./published | |
| # Verify that the file versions on the exe match the release version | |
| - name: Check File Version | |
| run: | | |
| # With single-file publish, we have CoseSignTool.exe (Windows) or CoseSignTool (Unix) | |
| $file = Get-Item "CoseSignTool*" -ErrorAction SilentlyContinue | Where-Object { $_.Extension -eq '.exe' -or $_.Extension -eq '' } | |
| if ($file) { | |
| $version = $file.VersionInfo.FileVersion | |
| Write-Output "File Version is $version" | |
| } else { | |
| Write-Output "Warning: Could not find CoseSignTool executable" | |
| } | |
| shell: pwsh | |
| working-directory: ./published/debug | |
| # Create NuGet packages for library projects (commented out for now) | |
| - name: Create NuGet packages | |
| run: | | |
| echo "📦 Creating NuGet packages for library projects..." | |
| VERSION=${{ needs.create_release.outputs.tag_name }} | |
| # Remove the 'v' prefix from VERSION for VersionNgt property | |
| VERSION_WITHOUT_V=$(echo "$VERSION" | sed 's/^v//') | |
| # Define library projects that should be packaged (excluding plugins and test projects) | |
| LIBRARY_PROJECTS=( | |
| "CoseHandler/CoseHandler.csproj" | |
| "CoseIndirectSignature/CoseIndirectSignature.csproj" | |
| "CoseSign1/CoseSign1.csproj" | |
| "CoseSign1.Abstractions/CoseSign1.Abstractions.csproj" | |
| "CoseSign1.Certificates/CoseSign1.Certificates.csproj" | |
| "CoseSign1.Headers/CoseSign1.Headers.csproj" | |
| "CoseSign1.Transparent/CoseSign1.Transparent.csproj" | |
| "CoseSign1.Transparent.MST/CoseSign1.Transparent.MST.csproj" | |
| "CoseSignTool.Abstractions/CoseSignTool.Abstractions.csproj" | |
| ) | |
| # Create packages directory | |
| mkdir -p published/packages | |
| # Pack each library project | |
| for project in "${LIBRARY_PROJECTS[@]}"; do | |
| if [ -f "$project" ]; then | |
| project_name=$(basename "${project%.*}") | |
| echo "📦 Creating package for $project_name..." | |
| dotnet pack "$project" \ | |
| --configuration Release \ | |
| --property:FileVersion=$VERSION \ | |
| --property:PackageVersion=$VERSION_WITHOUT_V \ | |
| --property:VersionNgt=$VERSION_WITHOUT_V \ | |
| --output published/packages \ | |
| --verbosity minimal | |
| if [ $? -eq 0 ]; then | |
| echo "✅ Successfully created package for $project_name" | |
| else | |
| echo "❌ Failed to create package for $project_name" | |
| fi | |
| else | |
| echo "⚠️ Project file not found: $project" | |
| fi | |
| done | |
| # List created packages | |
| echo "" | |
| echo "📋 Created NuGet packages:" | |
| if [ -d "published/packages" ]; then | |
| ls -la published/packages/*.nupkg | while read -r line; do | |
| echo " 📦 $(echo "$line" | awk '{print $9}')" | |
| done | |
| else | |
| echo "❌ No packages directory found" | |
| fi | |
| echo "🎯 NuGet package creation completed." | |
| shell: bash | |
| # Copy the docs, license, and markdown files to the release folders. | |
| - name: Copy docs to release folders | |
| run: | | |
| mkdir -p published/debug/docs | |
| cp -r docs/* published/debug/docs/ | |
| mkdir -p published/release/docs | |
| cp -r docs/* published/release/docs/ | |
| cp -r LICENSE published/debug/ | |
| cp -r LICENSE published/release/ | |
| cp -r *.md published/debug/ | |
| cp -r *.md published/release/ | |
| # Create zip files for release. | |
| - name: Create zip files for the release | |
| run: | | |
| ${{ matrix.zip_command_debug }} | |
| ${{ matrix.zip_command_release }} | |
| working-directory: ./published | |
| # List the contents of the published directory to make sure all the artifacts are there. | |
| - name: List published directory | |
| run: ${{ matrix.dir_command }} | |
| working-directory: ./published | |
| # Upload the zipped assets to the release. | |
| - name: Upload binary archives | |
| uses: svenstaro/upload-release-action@v2 | |
| with: | |
| repo_token: ${{ secrets.GITHUB_TOKEN }} | |
| file: ./published/CoseSignTool-*.zip | |
| file_glob: true | |
| overwrite: true | |
| tag: ${{ needs.create_release.outputs.tag_name }} | |
| # Commented out until we decide to support publishing of nuget packages. | |
| # Upload the NuGet packages to the release (commented out for now) | |
| # - name: Upload NuGet packages | |
| # uses: svenstaro/upload-release-action@v2 | |
| # with: | |
| # repo_token: ${{ secrets.GITHUB_TOKEN }} | |
| # file: ./published/packages/*.nupkg | |
| # file_glob: true | |
| # overwrite: true | |
| # tag: ${{ needs.create_release.outputs.tag_name }} |