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
26 changes: 22 additions & 4 deletions .github/workflows/package-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ on:
schedule:
- cron: '17 3 * * *'
workflow_dispatch:
pull_request:
paths:
- '.github/workflows/package-production.yml'
- 'executors/**'
- 'implementations/**'
- 'release/**'
- 'scenarios/**'
- 'scripts/package/**'
- 'templates/**'

permissions:
contents: read
Expand All @@ -15,15 +24,15 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x

- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
go-version: 1.26.x
cache-dependency-path: |
Expand Down Expand Up @@ -56,12 +65,21 @@ jobs:
echo "CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc" >> "$GITHUB_ENV"
echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=$rust_lld" >> "$GITHUB_ENV"

- name: Verify Docker Engine
shell: pwsh
run: |
$serverVersion = @(docker version --format '{{.Server.Version}}' 2>$null)
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace(($serverVersion -join ' ').Trim())) {
throw 'Docker Engine is required for the full ProtocolLab component catalog build.'
}
Write-Host "Docker Engine $($serverVersion -join ' ') is available."

- name: Build all ProtocolLab component packages (never publishes)
shell: pwsh
run: ./scripts/package/Build-AllProtocolLabComponentPackages.ps1 -Clean

- name: Upload package production artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: protocol-lab-component-packages
if-no-files-found: error
Expand Down
118 changes: 118 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: package-release

on:
workflow_dispatch:
inputs:
ref:
description: 'Commit or branch to build'
required: true
default: main
type: string
release_tag:
description: 'Immutable release tag to create when dry_run is false'
required: true
type: string
release_intent:
description: 'Reviewed release intent path in this checkout'
required: true
type: string
dry_run:
description: 'Build and validate assets without creating a GitHub release'
required: true
default: true
type: boolean

permissions:
contents: write

jobs:
package-release:
runs-on: ubuntu-latest
env:
RELEASE_TAG: ${{ inputs.release_tag }}
RELEASE_INTENT: ${{ inputs.release_intent }}

steps:
- name: Checkout requested source
uses: actions/checkout@v7
with:
ref: ${{ inputs.ref }}

- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x

- name: Setup Go
uses: actions/setup-go@v7
with:
go-version: 1.26.x
cache-dependency-path: |
implementations/quic-go-http3/go.sum
executors/quic-go-raw-load/source/go.sum

- name: Verify Docker Engine
shell: pwsh
run: |
$serverVersion = @(docker version --format '{{.Server.Version}}' 2>$null)
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace(($serverVersion -join ' ').Trim())) {
throw 'Docker Engine is required for the full ProtocolLab component catalog build.'
}
Write-Host "Docker Engine $($serverVersion -join ' ') is available."

- name: Validate reviewed release intent
shell: pwsh
run: |
$intentPath = Join-Path (Get-Location) $env:RELEASE_INTENT
if (-not (Test-Path -LiteralPath $intentPath -PathType Leaf)) {
throw "Release intent was not found: $intentPath"
}
$intent = Get-Content -LiteralPath $intentPath -Raw | ConvertFrom-Json
if ([string]$intent.schemaVersion -ne 'protocol-lab.release-intent.v1') { throw 'Release intent has an unsupported schemaVersion.' }
if ([string]$intent.classification -ne 'release') { throw 'Package publication requires classification=release.' }
if ([string]$intent.status -ne 'approved') { throw 'Package publication requires status=approved.' }
if (@($intent.components).Count -eq 0) { throw 'A release intent must name at least one component.' }
if ([string]::IsNullOrWhiteSpace($env:RELEASE_TAG)) { throw 'release_tag must not be empty.' }
if ($env:RELEASE_TAG -notmatch '^[A-Za-z0-9][A-Za-z0-9._/-]*$') { throw "release_tag contains unsupported characters: $env:RELEASE_TAG" }

- name: Build and validate all package artifacts
shell: pwsh
run: ./scripts/package/Build-AllProtocolLabComponentPackages.ps1 -Clean

- name: Run release-system validation
shell: pwsh
run: |
./scripts/package/Test-ProtocolLabComponentReleaseGraph.ps1
./scripts/package/Test-ProtocolLabReleaseIntents.ps1
$attestationScript = Join-Path (Get-Location) 'scripts/package/Test-ProtocolLabPackageBuildAttestation.ps1'
$packages = @(Get-ChildItem -LiteralPath artifacts/packages -File -Filter '*.plabpkg' | Sort-Object Name)
if ($packages.Count -eq 0) { throw 'No package artifacts were produced.' }
foreach ($package in $packages) {
$attestationPath = "$($package.FullName).build-attestation.json"
if (-not (Test-Path -LiteralPath $attestationPath -PathType Leaf)) { throw "Missing build attestation for $($package.Name)." }
& $attestationScript -PackagePath $package.FullName -AttestationPath $attestationPath -RequireParityEligible
}

- name: Upload validated package assets
uses: actions/upload-artifact@v7
with:
name: protocol-lab-component-release-assets
if-no-files-found: error
path: |
artifacts/packages/*.plabpkg
artifacts/packages/*.plabpkg.build-attestation.json
artifacts/packages/package-index.json
artifacts/packages/package-index.md
artifacts/packages/SHA256SUMS.txt
artifacts/packages/package-validation-summary.json
artifacts/packages/package-validation-summary.md

- name: Create GitHub release
if: ${{ inputs.dry_run == false }}
env:
GH_TOKEN: ${{ github.token }}
shell: pwsh
run: |
$assets = @(Get-ChildItem -LiteralPath artifacts/packages -File | Where-Object { $_.Name -match '\.(plabpkg|json|md|txt)$' } | ForEach-Object FullName)
if ($assets.Count -eq 0) { throw 'No validated package release assets were produced.' }
& gh release create $env:RELEASE_TAG --target '${{ inputs.ref }}' --title $env:RELEASE_TAG --notes-file artifacts/packages/package-index.md @assets
28 changes: 24 additions & 4 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,31 @@
- main

jobs:
documentation:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Validate public documentation entrypoints
shell: pwsh
run: ./scripts/package/Test-ProtocolLabComponentDocumentation.ps1

scenario-packages:
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment on lines +11 to +20
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Validate scenario package authority and declarations
shell: pwsh
run: ./scripts/package/Test-ProtocolLabScenarioPackages.ps1

manifests:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Validate component manifests
shell: pwsh
Expand All @@ -22,17 +42,17 @@

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x

- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
go-version: 1.26.x

Expand Down
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ Run the focused package validation before review:

```powershell
pwsh ./scripts/package/Validate-ProtocolLabComponentManifests.ps1
pwsh ./scripts/package/Test-ProtocolLabComponentReleaseGraph.ps1
pwsh ./scripts/package/Test-ProtocolLabComponentDocumentation.ps1
```

When changing package builders, also run the affected wrapper under
`scripts/package/` and confirm the generated `.plabpkg` remains under
`artifacts/packages/`.
`artifacts/packages/`. Run the full-catalog builder for changes to shared
packaging or release behavior; it requires a running Docker Engine and the
toolchains listed in [scripts/package/README.md](scripts/package/README.md).

## Style

Expand Down
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ This repository owns ProtocolLab components that are useful outside the core pub
- shared toolchain pins used to build those packages
- package scripts and manifest templates

## Start Here

Choose the path that matches what you are trying to do:

| Goal | Start here |
| --- | --- |
| Understand ProtocolLab's public contracts | [`incursa/protocol-lab`](https://github.com/incursa/protocol-lab) |
| Verify a package or build one from source | [Third-party package guide](docs/third-party-package-consumption.md) |
| Add or update a component | [Adding a component](#adding-a-component) and [CONTRIBUTING.md](CONTRIBUTING.md) |
| Run a hosted experiment | [lab.incursa.com](https://lab.incursa.com/) |

Public package availability is visible on the
[Releases page](https://github.com/incursa/protocol-lab-components/releases).
If no release is listed, use the source-build quickstart in the third-party
guide; a GitHub Actions artifact or locally built package is not a published
release.

The default ownership model is a component monorepo. Kestrel HTTP/1, Kestrel HTTP/2, Caddy HTTP/1, and small alternate executors should not each become a new repository just because they produce separate ProtocolLab packages. They share package conventions, release plumbing, validation scripts, and usually the same maintainers.

Separate repositories should be created only when there is a concrete boundary that makes shared operation more expensive than useful:
Expand Down Expand Up @@ -108,6 +125,14 @@ Package IDs should use a stable dotted namespace:

Versioning is per package. A Caddy HTTP/1 wrapper can ship `0.2.0` while Kestrel HTTP/1 remains `0.1.0`.

Third-party consumers should start with
[`docs/third-party-package-consumption.md`](docs/third-party-package-consumption.md).
It documents how to obtain a release artifact, verify its hash and build
attestation, inspect the package manifests, and pin the package in an
immutable ProtocolLab run plan. The manual release workflow is
[`release.yml`](.github/workflows/release.yml); it is dry-run
by default and requires an approved release intent before it can publish.

Shared scripts may build all packages, but publish and release metadata must preserve each package ID and version. Do not replace per-package identity with one repository-wide package version.

## Adding A Component
Expand All @@ -120,9 +145,15 @@ Shared scripts may build all packages, but publish and release metadata must pre

Adding Kestrel HTTP/1 or Caddy HTTP/1 is a normal component addition in this repository. It does not require creating another repository.

## Current Lane Packages
## Package Catalog

The authoritative inventory is the set of component-local
`protocol-lab-package.json` files and the reviewed
[`release/component-graph.v1.json`](release/component-graph.v1.json). Run the
manifest validator to obtain the current package count. The lists below are
selected lane examples, not a complete catalog.

Implementation packages:
Selected implementation packages:

- `org.protocol-lab.components.implementation.kestrel-http1`
- `org.protocol-lab.components.implementation.kestrel-http2`
Expand All @@ -147,7 +178,7 @@ Implementation packages:
- `org.protocol-lab.components.implementation.aioquic-raw`
- `org.protocol-lab.components.implementation.quiche-raw`

Test-executor packages:
Selected test-executor packages:

- `org.protocol-lab.components.executor.http1-reference`
- `org.protocol-lab.components.executor.http1-go-smoke`
Expand All @@ -157,7 +188,7 @@ Test-executor packages:
- `org.protocol-lab.components.executor.h3spec-http3-qpack`
- `org.protocol-lab.components.executor.aioquic-rfc9220-websocket`

Scenario-pack packages:
Selected scenario-pack packages:

- `org.protocol-lab.components.scenario.raw-quic-transport`
- `org.protocol-lab.components.scenario.h3spec-http3-qpack`
Expand Down
14 changes: 13 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

This documentation supports the component package repository.

## Start Here

- [Third-party package consumption](third-party-package-consumption.md) gives
the shortest verified path to build, inspect, attest, and pin a package.
- [Package scripts](../scripts/package/README.md) documents individual and
full-catalog builders, prerequisites, and generated evidence.
- [Root README](../README.md) explains repository ownership and package
boundaries.
- [Releases](https://github.com/incursa/protocol-lab-components/releases) is
the authoritative public distribution surface. If it is empty, build from
source; workflow artifacts are not releases.

## Repository Surfaces

- [Root README](../README.md) explains the monorepo boundary, package layout,
Expand Down Expand Up @@ -32,7 +44,7 @@ This documentation supports the component package repository.
- [Contributor agreement automation](contributor-agreement-automation.md)
records the owner setup required for the CLA workflow.
- The QUIC/HTTP/3 parity matrix lives in
`C:\shared\src\incursa\quic-dotnet\docs\protocol-lab\quic-http3-component-parity-matrix.md`
[`incursa/quic-dotnet`](https://github.com/incursa/quic-dotnet/blob/main/docs/protocol-lab/quic-http3-component-parity-matrix.md)
because `quic-dotnet` owns the Incursa support and proof story.

## Manifest Names
Expand Down
Loading
Loading