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
22 changes: 22 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Summary

<!-- Describe the contract/runtime change and why it is needed. -->

## SemVer declaration

- [ ] Exactly one PR label is set: `semver:patch`, `semver:minor`, or `semver:major`.
- [ ] The selected impact follows `docs/SECURITY-AND-RELEASE-GOVERNANCE.md`.

## Security and contract checklist

- [ ] Existing versioned prompt, skill, Markdown part, and output-schema files were not edited.
- [ ] New or changed sensitive variables are marked `redacted_in_logs: true`.
- [ ] No raw arguments, rendered messages, documents, responses, or secrets are logged.
- [ ] Schema, manifest, aliases, pins, changelog, and task index are updated when applicable.
- [ ] Security-sensitive changes have a security-reviewer assigned.
- [ ] Local build, tests, CLI validation, and relevant sample succeeded.

## Validation evidence

<!-- List commands and results. -->

47 changes: 32 additions & 15 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

## Prompt contract validation

`validate-prompts.yml` runs for every pull request, on manual dispatch, and as a
reusable workflow. Its `prompt-contract` job performs the same deterministic
sequence on every run:
`validate-prompts.yml` runs for every pull request, for every push to `main`, on
manual dispatch, and as a reusable workflow. Its `prompt-contract` job performs
the same deterministic sequence on every run:

1. restore and build the solution in Release mode;
2. run the CLI `validate` command;
3. test manifest and embedded-resource consistency;
4. test schemas, published-artifact immutability, and render fixtures;
5. test CLI exit codes and diagnostics;
6. run the end-to-end consumer sample in Production with directory override
3. test PromptKit runtime and redaction security contracts;
4. test manifest and embedded-resource consistency;
5. test schemas, published-artifact immutability, and render fixtures;
6. test CLI exit codes and diagnostics;
7. run the end-to-end consumer sample in Production with directory override
suppression enabled.

Any failed command stops the job and leaves the check unsuccessful. Configure
Expand All @@ -24,19 +25,35 @@ The equivalent local commands are:
dotnet restore DevComputaria.Prompting.sln
dotnet build DevComputaria.Prompting.sln --configuration Release --no-restore
dotnet run --project tools/DevComputaria.Prompts.Cli --configuration Release --no-build -- validate
dotnet test tests/DevComputaria.PromptKit.Tests/DevComputaria.PromptKit.Tests.csproj --configuration Release --no-build --no-restore
dotnet test tests/DevComputaria.Prompts.Tests/DevComputaria.Prompts.Tests.csproj --configuration Release --no-build --no-restore
dotnet test tests/DevComputaria.Prompts.Contract.Tests/DevComputaria.Prompts.Contract.Tests.csproj --configuration Release --no-build --no-restore
dotnet test tests/DevComputaria.Prompts.Cli.Tests/DevComputaria.Prompts.Cli.Tests.csproj --configuration Release --no-build --no-restore
DOTNET_ENVIRONMENT=Production PROMPTS_DIRECTORY_OVERRIDE=./prompts dotnet run --project samples/Consumer.ImageAnalysis --configuration Release --no-build
```

## Prompts package
## Release governance

`pack-prompts.yml` can only execute its `pack prompts` job after the reusable
prompt contract gate succeeds. A successful run uploads the generated `.nupkg`
as a workflow artifact tied to the commit SHA. A failed gate therefore produces
no releasable package artifact.
`release-governance.yml` requires exactly one `semver:patch`, `semver:minor`, or
`semver:major` label on every pull request. It runs the CLI `release-check`
against the base branch to calculate the minimum impact and reject in-place
changes to published artifacts. Configure branch protection to require
`release-governance / semver-and-immutability`.

The workflow intentionally does not push directly to NuGet. Publishing that
artifact to a package registry requires the release credentials and governance
defined by GH-015.
Manual release runs provide a previous Git ref, previous package version, next
package version, and declared impact. The pack workflows reject a version
transition that does not exactly match that declaration.

## Package artifacts

`pack-promptkit.yml` and `pack-prompts.yml` can only execute their packaging jobs
after the reusable prompt contract gate succeeds, including all four test
projects. A successful run uploads the generated `.nupkg` as a workflow artifact
tied to the commit SHA and publishes that same file to the repository owner's
GitHub Packages NuGet registry. A failed test or contract check therefore creates
neither a releasable artifact nor a registry version.

Registry publication runs through the `production` environment with the
workflow-scoped `GITHUB_TOKEN` and the least-privilege `packages: write`
permission. Configure required reviewers on that environment as defined in
`docs/SECURITY-AND-RELEASE-GOVERNANCE.md`.
86 changes: 82 additions & 4 deletions .github/workflows/pack-promptkit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,91 @@ name: pack-promptkit

on:
workflow_dispatch:
inputs:
base-ref:
description: Previous release tag or Git ref
required: true
previous-version:
description: Previously published package version (X.Y.Z)
required: true
package-version:
description: Package version to create (X.Y.Z)
required: true
semver-impact:
description: Declared release impact
required: true
type: choice
options: [patch, minor, major]

permissions:
contents: read
packages: write

jobs:
contract-gate:
name: validate prompt contract
uses: ./.github/workflows/validate-prompts.yml

pack:
name: pack PromptKit
needs: contract-gate
runs-on: ubuntu-latest
environment: production
timeout-minutes: 15
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
cache: true
cache-dependency-path: |
Directory.Packages.props
src/DevComputaria.PromptKit/DevComputaria.PromptKit.csproj
tools/DevComputaria.Prompts.Cli/DevComputaria.Prompts.Cli.csproj

- name: Restore package dependencies
run: dotnet restore tools/DevComputaria.Prompts.Cli/DevComputaria.Prompts.Cli.csproj

- name: Validate release version and impact
run: >-
dotnet run --project tools/DevComputaria.Prompts.Cli
--configuration Release --no-restore --
release-check --base "${{ inputs['base-ref'] }}"
--impact "${{ inputs['semver-impact'] }}"
--previous-version "${{ inputs['previous-version'] }}"
--next-version "${{ inputs['package-version'] }}"

- name: Pack validated runtime
run: >-
dotnet pack src/DevComputaria.PromptKit/DevComputaria.PromptKit.csproj
--configuration Release --no-restore --output artifacts
-p:PackageVersion="${{ inputs['package-version'] }}"
-p:RepositoryUrl="${{ github.server_url }}/${{ github.repository }}"
-p:RepositoryType=git
-p:PublishRepositoryUrl=true
-p:ContinuousIntegrationBuild=true

- name: Publish package artifact
uses: actions/upload-artifact@v4
with:
dotnet-version: 8.0.x
- run: dotnet pack src/DevComputaria.PromptKit/DevComputaria.PromptKit.csproj -c Release
name: DevComputaria.PromptKit-${{ inputs['package-version'] }}-${{ github.sha }}
path: artifacts/*.nupkg
if-no-files-found: error
retention-days: 14

- name: Publish package to GitHub Packages
env:
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >-
dotnet nuget push "artifacts/*.nupkg"
--source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
--api-key "$NUGET_AUTH_TOKEN"
--skip-duplicate
46 changes: 44 additions & 2 deletions .github/workflows/pack-prompts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@ name: pack-prompts

on:
workflow_dispatch:
inputs:
base-ref:
description: Previous release tag or Git ref
required: true
previous-version:
description: Previously published package version (X.Y.Z)
required: true
package-version:
description: Package version to create (X.Y.Z)
required: true
semver-impact:
description: Declared release impact
required: true
type: choice
options: [patch, minor, major]

permissions:
contents: read
packages: write

jobs:
contract-gate:
Expand All @@ -15,13 +31,16 @@ jobs:
name: pack prompts
needs: contract-gate
runs-on: ubuntu-latest
environment: production
timeout-minutes: 15
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_NOLOGO: true
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
Expand All @@ -34,17 +53,40 @@ jobs:
src/DevComputaria.Prompts/DevComputaria.Prompts.csproj

- name: Restore package dependencies
run: dotnet restore src/DevComputaria.Prompts/DevComputaria.Prompts.csproj
run: dotnet restore tools/DevComputaria.Prompts.Cli/DevComputaria.Prompts.Cli.csproj

- name: Validate release version and impact
run: >-
dotnet run --project tools/DevComputaria.Prompts.Cli
--configuration Release --no-restore --
release-check --base "${{ inputs['base-ref'] }}"
--impact "${{ inputs['semver-impact'] }}"
--previous-version "${{ inputs['previous-version'] }}"
--next-version "${{ inputs['package-version'] }}"

- name: Pack validated catalog
run: >-
dotnet pack src/DevComputaria.Prompts/DevComputaria.Prompts.csproj
--configuration Release --no-restore --output artifacts
-p:PackageVersion="${{ inputs['package-version'] }}"
-p:RepositoryUrl="${{ github.server_url }}/${{ github.repository }}"
-p:RepositoryType=git
-p:PublishRepositoryUrl=true
-p:ContinuousIntegrationBuild=true

- name: Publish package artifact
uses: actions/upload-artifact@v4
with:
name: DevComputaria.Prompts-${{ github.sha }}
name: DevComputaria.Prompts-${{ inputs['package-version'] }}-${{ github.sha }}
path: artifacts/*.nupkg
if-no-files-found: error
retention-days: 14

- name: Publish package to GitHub Packages
env:
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >-
dotnet nuget push "artifacts/*.nupkg"
--source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json"
--api-key "$NUGET_AUTH_TOKEN"
--skip-duplicate
79 changes: 79 additions & 0 deletions .github/workflows/release-governance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: release-governance

on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
workflow_dispatch:
inputs:
base-ref:
description: Git ref used as the governance comparison baseline
required: true
semver-impact:
description: Declared release impact
required: true
type: choice
options: [patch, minor, major]

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
governance:
name: semver-and-immutability
runs-on: ubuntu-latest
timeout-minutes: 10
env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
DOTNET_NOLOGO: true
steps:
- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Resolve declared impact and base ref
id: release
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_BASE_REF: ${{ inputs['base-ref'] }}
INPUT_IMPACT: ${{ inputs['semver-impact'] }}
LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }}
PR_BASE_REF: ${{ github.base_ref }}
run: |
if [[ "$EVENT_NAME" == "pull_request" ]]; then
mapfile -t semver_labels < <(jq -r '.[] | select(test("^semver:(patch|minor|major)$"))' <<< "$LABELS_JSON")
if [[ "${#semver_labels[@]}" -ne 1 ]]; then
echo "Exactly one semver:patch, semver:minor, or semver:major label is required." >&2
exit 1
fi
echo "impact=${semver_labels[0]#semver:}" >> "$GITHUB_OUTPUT"
echo "base=origin/$PR_BASE_REF" >> "$GITHUB_OUTPUT"
else
echo "impact=$INPUT_IMPACT" >> "$GITHUB_OUTPUT"
echo "base=$INPUT_BASE_REF" >> "$GITHUB_OUTPUT"
fi

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
cache: true
cache-dependency-path: |
Directory.Packages.props
tools/DevComputaria.Prompts.Cli/DevComputaria.Prompts.Cli.csproj

- name: Restore release tooling
run: dotnet restore tools/DevComputaria.Prompts.Cli/DevComputaria.Prompts.Cli.csproj

- name: Validate release impact
run: >-
dotnet run --project tools/DevComputaria.Prompts.Cli
--configuration Release --no-restore --
release-check --base "${{ steps.release.outputs.base }}"
--impact "${{ steps.release.outputs.impact }}"
8 changes: 8 additions & 0 deletions .github/workflows/validate-prompts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: validate-prompts

on:
pull_request:
push:
branches:
- main
workflow_call:
workflow_dispatch:

Expand Down Expand Up @@ -42,6 +45,11 @@ jobs:
- name: Validate repository contract with CLI
run: dotnet run --project tools/DevComputaria.Prompts.Cli --configuration Release --no-build -- validate

- name: Test PromptKit security and runtime contracts
run: >-
dotnet test tests/DevComputaria.PromptKit.Tests/DevComputaria.PromptKit.Tests.csproj
--configuration Release --no-build --no-restore

- name: Test manifest and packed-resource consistency
run: >-
dotnet test tests/DevComputaria.Prompts.Tests/DevComputaria.Prompts.Tests.csproj
Expand Down
Loading
Loading