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
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- name: install the repository Rust toolchain
run: rustup toolchain install "$RUST_VERSION" --profile minimal --no-self-update
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
Expand Down Expand Up @@ -63,7 +64,10 @@ jobs:
| tar xz -C /tmp ./typos
echo /tmp >> "$GITHUB_PATH"
- name: run the ordinary quality gate
run: npx --yes --package="@biomejs/biome@2.6.2" -- just check
env:
CI_BASE_REVISION: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }}
CI_HEAD_REVISION: ${{ github.sha }}
run: npx --yes --package="@biomejs/biome@2.6.2" -- just check-changes

conventional-commits:
name: conventional commits
Expand Down
62 changes: 38 additions & 24 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -1,48 +1,62 @@
# CodeQL security analysis (Advanced Setup). Scans the Rust codec source and the
# repository's own GitHub Actions workflows, uploading results to Security > Code
# scanning. Advanced (not Default) setup so the language set, query suite, and
# SHA-pinned action versions live in-repo under the same review as the code —
# matching this repo's "pin every action" convention.
#
# Languages:
# - rust the codec itself (build-mode: none — the only mode the Rust analysis
# supports; it resolves the cargo workspace without a traced build).
# - actions the workflows under .github/workflows (catches injection / unsafe
# checkout / token-scope issues in CI YAML).
# Analyze affected Rust and GitHub Actions source.
name: CodeQL

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Weekly re-scan (Mon 03:27 UTC) so newly-published CodeQL queries run against
# unchanged code, not only on new commits.
- cron: "27 3 * * 1"

permissions:
contents: read
contents: read # check out the source

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
RUST_VERSION: 1.99.1

jobs:
plan:
name: select analysis
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
matrix: ${{ steps.selection.outputs.matrix }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
- name: install the repository Rust toolchain
run: rustup toolchain install "$RUST_VERSION" --profile minimal --no-self-update
- name: select affected analysis languages
id: selection
shell: bash
env:
CI_BASE_REVISION: ${{ github.event.pull_request.base.sha }}
CI_HEAD_REVISION: ${{ github.sha }}
run: |
matrix=$(cargo run --locked --quiet -p webpkit-ci-select --bin ci-select -- codeql-matrix)
printf 'matrix=%s\n' "$matrix" >> "$GITHUB_OUTPUT"

analyze:
name: analyze (${{ matrix.language }})
needs: [plan]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
security-events: write # upload the SARIF results to code scanning
actions: read
contents: read
actions: read # inspect workflow metadata
contents: read # check out the source
strategy:
fail-fast: false
matrix:
include:
- language: rust
build-mode: none
- language: actions
build-mode: none
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: initialize CodeQL
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-plz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ jobs:
- name: prove the production policy module
env:
POLICY_TOOL: ${{ steps.policy.outputs.executable }}
REQUIRED_HARNESSES: '["production::proofs::commit_identity_is_exact","production::proofs::candidate_selection_preserves_source","production::proofs::ordinary_summary_requires_complete_work","production::proofs::publication_requires_complete_same_source_qualification"]'
REQUIRED_HARNESSES: '["production::proofs::commit_identity_is_exact","production::proofs::candidate_selection_preserves_source","production::proofs::ordinary_summary_requires_complete_work","production::proofs::publication_requires_complete_same_source_qualification","production::proofs::affected_work_routes_recognized_workflow_boundaries","production::proofs::affected_work_routes_recognized_rust_and_action_inputs","production::proofs::affected_work_unknown_and_empty_inputs_keep_full_selection"]'
COUNTEREXAMPLE: production::proofs::reject_incomplete_qualification_probe
run: '"$POLICY_TOOL" prove-source --source xtask/src/ci.rs --require-json "$REQUIRED_HARNESSES" --counterexample "$COUNTEREXAMPLE"'
- name: verify the candidate source after proofs
Expand Down
12 changes: 10 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ Use Git Bash for Just recipes on Windows.

## Development

Run `mise x -- just check` before pushing.
Run `mise x -- just check-changes` before pushing.
The pre-push hook runs the same command.
The gate checks formatting, Clippy, dependencies, spelling, workflows, workspace tests and doctests, documentation, the image feature, and the host `no_std` build.
The selector compares a clean checkout with `origin/main`, or the event's base commit in CI.
A nonempty change confined to workflow YAML runs Actionlint, YAML formatting, spelling, and workflow contract tests.
Changes to `.github/workflows/ci.yml` and uncertain comparisons run the complete gate.
Use `just check` to run the complete gate directly.
The complete gate checks formatting, Clippy, dependencies, spelling, workflows, workspace tests and doctests, documentation, the image feature, and the host `no_std` build.
Workspace checks explicitly enable `webpkit/image` and `webpkit/rayon`.
Workspace tests include conformance ledgers, stable fuzz seeds, image conversions, and the Rayon paths.
The revision corpus replay shares acceptance and round-trip assertions with its release-only fuzz target.
Pull requests and merge groups run one Linux quality job instead of repeating those suites in separate jobs.
CodeQL selects Rust or Actions analysis for recognized changes and analyzes both for unknown or mixed changes.
A change to the CodeQL workflow analyzes both languages.
Main-branch analysis runs both languages to maintain the pull-request comparison baseline without a scheduled scan.
Read [affected-work selection](docs/adr/affected-work-selection.md) for the comparison boundary and fallback behavior.

Use `just lint`, `just test`, or `just doc` for a narrower development check.
The supported Rust floor is 1.88.
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"crates/webpkit-samples",
"xtask",
"xtask/ci-core",
"xtask/ci-select",
"xtask/fuzz",
]
default-members = ["crates/webpkit"]
Expand Down
15 changes: 13 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ typos-fix:
typos --write-changes

actionlint:
actionlint .github/workflows/*.yml
actionlint

# Build rustdoc under `RUSTDOCFLAGS=-D warnings` (mirrors CI's doc gate).
doc:
Expand All @@ -127,13 +127,24 @@ lint:
just typos
just actionlint

# The ordinary PR and merge-group gate, kept to checks that are useful on every change.
# The complete source gate, also required for release qualification.
check:
just lint
just test
just doc
just no-std-host

# Workflow syntax and production workflow contracts.
check-workflows:
just actionlint
yamlfmt --lint .github/workflows
just typos
cargo test --locked -p xtask --test ci_workflow

# Select work from a clean checkout and a complete Git comparison.
check-changes:
cargo run --locked --quiet -p webpkit-ci-select --bin ci-select -- check

# ----- conformance / verification (webp) -----

# Regenerate golden fixtures from libwebp cwebp/dwebp (needs cwebp/dwebp on PATH).
Expand Down
62 changes: 62 additions & 0 deletions docs/adr/affected-work-selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
date: 2026-10-28
---

# Affected-work selection

## Status

Proposed.

## Context

The ordinary quality gate runs codec tests, documentation, and compilation even when a change only edits workflow YAML.
CodeQL also runs both Rust and Actions analysis regardless of the affected source.
A failed or incomplete path comparison must preserve full verification.
Local hooks and hosted jobs need the same selection rules.

## Decision

Keep `just check` as the complete source gate and release qualification unchanged.
Use `just check-changes` for the local pre-push hook and ordinary hosted quality job.
The `ci-select` binary in the `webpkit-ci-select` package validates the Git comparison and calls the dependency-free production decision in `xtask/src/ci.rs`.
The `webpkit-ci-core` package stays independent of the standard library and external dependencies.
The quality gate and CodeQL language set are separate typed decisions.

Select `just check-workflows` only for a complete, nonempty set of recognized workflow YAML paths.
The workflow gate runs Actionlint, workflow YAML formatting, spelling, and the source-bound workflow contract tests.
The ordinary CI workflow always selects the complete gate because its configuration controls compilation and tests.
Other Rust, action-definition, unknown, or mixed changes select the complete gate.

Select Rust analysis for recognized Rust workspace inputs and Actions analysis for workflow YAML or nested action metadata.
Changes to the ordinary CI workflow, CodeQL workflow, unknown inputs, or mixed language inputs analyze both languages.
Every successful analysis plan contains at least one language.
Run both languages on main to publish the pull-request comparison baseline.
A successful later main run covers both categories when it cancels an earlier run.
Remove the weekly scan and cancel superseded runs of the same ref.
Renovate updates to the pinned CodeQL action change the scanner workflow and run both analyses.

Bind an explicit expected head to the checkout's complete commit identity before selection.
A contradictory or malformed expected identity is an error.
Default local comparisons use `origin/main`.
An unavailable or empty base, failed Git comparison, dirty checkout, empty difference, malformed path, or undecodable path selects the complete gate and both analyses.
Use `--no-renames` so a move contributes both the old and new paths.
Adding an unknown path can never reduce verification.
Dispatch and selection failures remain failures rather than successful skips.

Source-bound Kani harnesses cover the production classification and conservative fallback.
Symbolic names use eight safe ASCII bytes, with up to four preceding paths selected from the recognized categories.
Malformed-path cases cover seven explicit shapes.
Real Git fixtures cover comparison and dispatch behavior outside the verifier's semantics.
Workflow contract tests bind local and hosted commands to the selector and preserve strict ordinary summaries and release qualification.
The proof inventory runs at the release gate.
An unrun proof supplies no verified release result.

## Consequences

Workflow maintenance avoids unrelated codec tests and Rust analysis when the comparison proves their absence.
Uncertain changes run the complete gate.
The selector trusts successful Git command results, the checkout filesystem, and the configured Rust and Just executables.
Local checks cover the selector and adapters.
GitHub event delivery, action behavior, and remote publication require separate evidence.
New source categories require explicit classification and corresponding boundary tests before they can reduce work.
2 changes: 1 addition & 1 deletion lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ pre-push:
commands:
check:
tags: check
run: just check
run: just check-changes
5 changes: 3 additions & 2 deletions xtask/ci-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
mod decisions;

pub use decisions::{
Association, Candidate, Event, JobReport, Outcome, Qualified, Refusal, Repository, Revision,
Role, SourceFacts, candidate, final_summary, ordinary_summary, qualify, source,
AffectedWork, Association, Candidate, CodeqlScope, Event, JobReport, Outcome, Qualified,
QualityWork, Refusal, Repository, Revision, Role, SourceFacts, affected_work, candidate,
final_summary, ordinary_summary, qualify, source,
};

#[cfg(test)]
Expand Down
23 changes: 23 additions & 0 deletions xtask/ci-select/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "webpkit-ci-select"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description = "Checked Git and command adapter for webpkit verification selection."
publish = false

[[bin]]
name = "ci-select"
path = "src/main.rs"

[dependencies]
webpkit-ci-core = { path = "../ci-core" }

[dev-dependencies]
tempfile.workspace = true

[lints]
workspace = true
Loading
Loading