Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- ".github/ISSUE_TEMPLATE/**"
- ".github/pull_request_template.md"
- ".github/workflows/ci.yml"
- ".github/workflows/downstream.yml"
- "CHANGELOG.md"
- "Cargo.lock"
- "Cargo.toml"
Expand All @@ -35,6 +36,7 @@ on:
- ".github/ISSUE_TEMPLATE/**"
- ".github/pull_request_template.md"
- ".github/workflows/ci.yml"
- ".github/workflows/downstream.yml"
- "CHANGELOG.md"
- "Cargo.lock"
- "Cargo.toml"
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/downstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Pinned downstream builds

on:
schedule:
- cron: "17 4 * * 1"
workflow_dispatch:

permissions:
contents: read

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

defaults:
run:
shell: bash

jobs:
downstream:
name: ${{ matrix.trial }} pinned downstream build
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
max-parallel: 1
matrix:
trial:
- rust-i18n
- cfn-guard
- navi
- stackable-operator
- pingora
- figment
- uaparser
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Install stable Rust
run: |
rustup toolchain install stable --profile minimal
rustup default stable

- name: Run pinned downstream trial
run: scripts/downstream-build-trials.sh "${{ matrix.trial }}"
21 changes: 19 additions & 2 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,30 @@ The migration claims are executable, not aspirational:
`tests/external_downstream_migration.rs` — pinned real-world configs and
reduced fixtures from real `serde_yaml` users (Pingora, rust-i18n, cfn-guard,
navi, Stackable).
- `scripts/downstream-build-trials.sh` — packages this crate and builds those
downstreams with their `serde_yaml` dependency rewritten to it.
- `scripts/downstream-build-trials.sh` — packages this crate and provides two
separate downstream checks:
- `smoke-only` runs deterministic package-alias and fixture smokes without
cloning or building an upstream repository; this is the bounded smoke gate
used by the normal CI and release-evidence workflows.
- The named trial modes clone pinned upstream commits and build the real
downstream manifests with their `serde_yaml` dependency rewritten to this
crate. The separate [`Pinned downstream builds`](https://github.com/jskoiz/saneyaml/blob/main/.github/workflows/downstream.yml)
workflow runs all named modes on its weekly schedule or by manual dispatch.

```sh
cargo test --test serde_yaml_swap_harness --test downstream_migration_harness
cargo test --test external_downstream_migration
# Deterministic fixture/package-alias smoke used by normal CI.
scripts/downstream-build-trials.sh smoke-only

# Real pinned downstream build trials (also run by the separate CI workflow).
scripts/downstream-build-trials.sh rust-i18n
scripts/downstream-build-trials.sh cfn-guard
scripts/downstream-build-trials.sh navi
scripts/downstream-build-trials.sh stackable-operator
scripts/downstream-build-trials.sh pingora
scripts/downstream-build-trials.sh figment
scripts/downstream-build-trials.sh uaparser
```

Real-world gates currently cover 33 files / 39 documents across GitHub Actions,
Expand Down
82 changes: 82 additions & 0 deletions tests/trust_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use saneyaml::Value;
const CARGO_TOML: &str = include_str!("../Cargo.toml");
const FUZZ_CARGO_TOML: &str = include_str!("../fuzz/Cargo.toml");
const CI_WORKFLOW: &str = include_str!("../.github/workflows/ci.yml");
const DOWNSTREAM_WORKFLOW: &str = include_str!("../.github/workflows/downstream.yml");
const DOWNSTREAM_TRIAL_SCRIPT: &str = include_str!("../scripts/downstream-build-trials.sh");
const README: &str = include_str!("../README.md");
const ARCHITECTURE: &str = include_str!("../docs/ARCHITECTURE.md");
const BENCHMARKS: &str = include_str!("../docs/BENCHMARKS.md");
Expand Down Expand Up @@ -131,6 +133,85 @@ fn migration_release_wording_tracks_manifest_metadata() {
);
}

#[test]
fn downstream_workflow_keeps_real_trials_separate_and_bounded() {
let workflow = saneyaml::parse_str(DOWNSTREAM_WORKFLOW)
.unwrap_or_else(|err| panic!(".github/workflows/downstream.yml parses as YAML: {err}"))
.into_value();
let triggers = workflow["on"]
.as_mapping()
.expect("downstream workflow triggers");

assert!(triggers.contains_key("schedule"));
assert!(triggers.contains_key("workflow_dispatch"));
assert!(!triggers.contains_key("push"));
assert!(!triggers.contains_key("pull_request"));

let job = &workflow["jobs"]["downstream"];
assert_eq!(job["runs-on"].as_str(), Some("ubuntu-latest"));
assert_eq!(job["timeout-minutes"].as_u64(), Some(30));
assert_eq!(job["strategy"]["fail-fast"].as_bool(), Some(false));
assert_eq!(job["strategy"]["max-parallel"].as_u64(), Some(1));

let actual_trials = job["strategy"]["matrix"]["trial"]
.as_sequence()
.expect("downstream trial matrix")
.iter()
.map(|trial| {
trial
.as_str()
.expect("downstream matrix trial is a string")
.to_owned()
})
.collect::<BTreeSet<_>>();
let expected_trials = [
"cfn-guard",
"figment",
"navi",
"pingora",
"rust-i18n",
"stackable-operator",
"uaparser",
]
.into_iter()
.map(str::to_owned)
.collect::<BTreeSet<_>>();
assert_eq!(actual_trials, expected_trials);

let run_steps = job["steps"]
.as_sequence()
.expect("downstream workflow steps")
.iter()
.filter_map(|step| step.get("run").and_then(Value::as_str))
.collect::<Vec<_>>();
assert!(
run_steps.iter().any(|run| {
run.contains("scripts/downstream-build-trials.sh \"${{ matrix.trial }}\"")
})
);
assert!(
!DOWNSTREAM_WORKFLOW.contains("smoke-only"),
"real downstream workflow must not silently fall back to fixture smoke"
);
assert_contains(CI_WORKFLOW, "scripts/downstream-build-trials.sh smoke-only");
for trial in &expected_trials {
assert_contains(DOWNSTREAM_TRIAL_SCRIPT, &format!(" {trial})"));
}
}

#[test]
fn migration_proof_distinguishes_fixture_smoke_from_real_trials() {
for term in [
"separate downstream checks",
"`smoke-only` runs deterministic package-alias and fixture smokes without",
"The named trial modes clone pinned upstream commits and build the real",
"`Pinned downstream builds`](https://github.com/jskoiz/saneyaml/blob/main/.github/workflows/downstream.yml)",
"scripts/downstream-build-trials.sh uaparser",
] {
assert_contains(MIGRATION, term);
}
}

#[test]
fn public_dependency_snippets_track_manifest_version() {
let manifest = package_manifest();
Expand Down Expand Up @@ -326,6 +407,7 @@ fn trust_metadata_input_filters() -> BTreeSet<String> {
".github/ISSUE_TEMPLATE/**".to_owned(),
".github/pull_request_template.md".to_owned(),
".github/workflows/ci.yml".to_owned(),
".github/workflows/downstream.yml".to_owned(),
])
}

Expand Down
Loading