Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 136 additions & 41 deletions .github/workflows/nuget_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,77 +9,172 @@ concurrency:
group: release
cancel-in-progress: false

# Default to no permissions; each job widens only what it needs.
permissions: {}

env:
DOTNET_VERSION: '10.0.x'
NODE_VERSION: '20'

jobs:
release:
# ---------------------------------------------------------------------------
# Untrusted-code zone. Restore/build/test (the bulk of third-party code
# execution) run here with NO release or publish credentials.
# ---------------------------------------------------------------------------
build-test:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
packages: write
issues: write
pull-requests: write
timeout-minutes: 30
contents: read
steps:
- name: Generate token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Checkout code
uses: actions/checkout@v4
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}

- name: Setup Node.js
uses: actions/setup-node@v4
# Audit first. After reviewing the audit run, switch to 'block' with a
# tuned allowed-endpoints list (see GITHUB_ACTIONS_HARDENING_ROLLOUT.md).
egress-policy: audit
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
node-version: ${{ env.NODE_VERSION }}

persist-credentials: false
- name: Guard against local-dev lockfile versions
run: |
if grep -rln '"resolved": "1.99.99"' --include=packages.lock.json . ; then
echo "::error::A packages.lock.json contains the local-dev version 1.99.99. Regenerate lockfiles in CI mode (remove trax-local.props) before committing."
exit 1
fi
- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
dotnet-quality: preview

- name: Install dependencies
run: npm install

- name: Restore (locked)
run: dotnet restore --locked-mode
- name: Build
run: dotnet build --configuration Release

run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --configuration Release --no-build --collect:"XPlat Code Coverage" --results-directory ./TestResults

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5
with:
directory: ./TestResults
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

- name: Release
# ---------------------------------------------------------------------------
# Release/version zone. Holds the GitHub App token (GitHub API: tags + release)
# but NOT the NuGet publish key. Produces the .nupkg artifact.
# ---------------------------------------------------------------------------
release:
needs: build-test
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write # semantic-release: tags + GitHub release
issues: write # semantic-release github plugin: successComment
pull-requests: write # semantic-release github plugin: successComment
outputs:
released: ${{ steps.version.outputs.released }}
version: ${{ steps.version.outputs.version }}
steps:
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
# semantic-release authenticates via the GITHUB_TOKEN env below, so the
# token does not need to be persisted into .git/config.
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
- name: Setup .NET
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
dotnet-quality: preview
- name: Install release tooling (locked, no install scripts)
run: npm ci --ignore-scripts
- name: Run semantic-release
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release

- name: Get release version
- name: Resolve released version
id: version
run: |
if [ -f .release-version ]; then
echo "version=$(cat .release-version)" >> $GITHUB_OUTPUT
echo "released=true" >> $GITHUB_OUTPUT
echo "version=$(cat .release-version)" >> "$GITHUB_OUTPUT"
echo "released=true" >> "$GITHUB_OUTPUT"
else
echo "released=false" >> "$GITHUB_OUTPUT"
fi

- name: Restore (locked)
if: steps.version.outputs.released == 'true'
run: dotnet restore --locked-mode
- name: Pack
if: steps.version.outputs.released == 'true'
run: dotnet pack --configuration Release --output nupkg -p:Version=${{ steps.version.outputs.version }}
env:
# Bind the version to env and reference the shell var (it is pipeline-
# controlled, but this is the pattern to use for any ${{ }} value).
PKG_VERSION: ${{ steps.version.outputs.version }}
run: dotnet pack --configuration Release --no-restore --output nupkg -p:Version="$PKG_VERSION"
- name: Upload package artifact
if: steps.version.outputs.released == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: nupkg
path: nupkg/*.nupkg
if-no-files-found: error
retention-days: 1

# ---------------------------------------------------------------------------
# Publish zone. Consumes the built artifact and pushes it. Runs NO install /
# build / test code. Holds the only credential that can publish to NuGet.
# Gated behind the protected `release` environment (configure reviewers + ref
# restriction in repo settings - see the rollout checklist).
# ---------------------------------------------------------------------------
publish:
needs: release
if: needs.release.outputs.released == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
environment: release
permissions:
contents: read
id-token: write # OIDC token for keyless attestation signing (+ future NuGet OIDC)
attestations: write # write the SLSA build-provenance attestation
steps:
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Download package artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: nupkg
path: nupkg
- name: Attest build provenance (SLSA)
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: 'nupkg/*.nupkg'
- name: Setup .NET
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
dotnet-quality: preview
# NuGet trusted publishing: exchange the GitHub OIDC token for a short-lived
# (~1h, single-use) key. No stored NUGET_API_KEY. Requires a trusted-publishing
# policy on nuget.org scoped to this repo + workflow file + the `release` environment.
- name: NuGet login (OIDC -> short-lived key)
id: nuget-login
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
with:
user: ${{ secrets.NUGET_USER }}
- name: Push to NuGet
if: steps.version.outputs.released == 'true'
run: dotnet nuget push "nupkg/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
env:
NUGET_API_KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }}
run: dotnet nuget push "nupkg/*.nupkg" --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate
39 changes: 32 additions & 7 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,50 @@ on:
pull_request:
branches: [main]

# Default to no permissions; the job widens only what it needs.
permissions: {}

env:
DOTNET_VERSION: '10.0.x'

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- name: Harden runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Guard against local-dev lockfile versions
run: |
if grep -rln '"resolved": "1.99.99"' --include=packages.lock.json . ; then
echo "::error::A packages.lock.json contains the local-dev version 1.99.99. Regenerate lockfiles in CI mode (remove trax-local.props) before committing."
exit 1
fi
- name: Setup .NET
uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
dotnet-quality: preview
- run: dotnet tool restore
- run: dotnet csharpier check .
- run: dotnet build --configuration Release
- run: dotnet test --configuration Release --no-build --collect:"XPlat Code Coverage" --results-directory ./TestResults
- name: Restore tools
run: dotnet tool restore
- name: Check formatting
run: dotnet csharpier check .
- name: Restore (locked)
run: dotnet restore --locked-mode
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --configuration Release --no-build --collect:"XPlat Code Coverage" --results-directory ./TestResults
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5
with:
directory: ./TestResults
fail_ci_if_error: false
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,6 @@ node_modules/

# MSBuild static graph restore cache
*.csproj.lscache

# Trax local-feed override (generated by pack-local.sh)
trax-local.props
10 changes: 10 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<Project>
<PropertyGroup>
<Version>1.99.99</Version>
<!-- Audit direct + transitive dependencies and fail the build on any known
vulnerability (low through critical). -->
<NuGetAuditMode>all</NuGetAuditMode>
<WarningsAsErrors>$(WarningsAsErrors);NU1901;NU1902;NU1903;NU1904</WarningsAsErrors>
</PropertyGroup>

<!-- Propagate the Trax.Core analyzer to all projects in the solution -->
Expand All @@ -11,4 +15,10 @@
OutputItemType="Analyzer"
/>
</ItemGroup>

<!-- Deterministic/reproducible builds. CI source-path normalization
(ContinuousIntegrationBuild) is turned on by the package only when CI is detected. -->
<ItemGroup>
<PackageReference Include="DotNet.ReproducibleBuilds" PrivateAssets="All" />
</ItemGroup>
</Project>
33 changes: 33 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="2.0.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="FluentAssertions" Version="8.8.0" />
<PackageVersion Include="LanguageExt.Core" Version="4.4.9" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
<PackageVersion
Include="Microsoft.Extensions.DependencyInjection.Abstractions"
Version="10.0.3"
/>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.3" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.3" />
<PackageVersion
Include="Microsoft.Extensions.Logging.Debug"
Version="10.0.0-preview.2.25163.2"
/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="NUnit" Version="4.4.0" />
<PackageVersion Include="NUnit.Analyzers" Version="4.11.2" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
<PackageVersion Include="PublicApiGenerator" Version="11.4.5" />
</ItemGroup>
</Project>
Loading
Loading