diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64d8bf4..07460ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,3 +30,27 @@ jobs: size=$(wc -c < target/release/polygo); echo "release binary: $size bytes" [ "$size" -lt 15728640 ] time target/release/polygo --help > /dev/null + + # GitHub loads action/action.yml before a step runs, and a manifest it cannot parse + # fails every job that uses the Action with a YAML error instead of a polygo message. + # Nothing else here loads it, so this job does: it uses the Action from this checkout. + action: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - name: A project with a broken translation + run: | + mkdir -p fixture/locales + printf '{\n "greet": "Hi {{name}}"\n}\n' > fixture/locales/en.json + printf '{\n "greet": "Hallo {{nome}}"\n}\n' > fixture/locales/de.json + - id: check + uses: ./action + continue-on-error: true + with: + mode: check + path: fixture/locales + - name: The Action ran and found the placeholder + run: | + [ "${{ steps.check.outcome }}" = failure ] || { echo "the Action should have failed on the error"; exit 1; } + [ "${{ steps.check.outputs.errors }}" = 1 ] || { echo "expected 1 error, got '${{ steps.check.outputs.errors }}'"; exit 1; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 128d567..90975bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to polygo. Versions follow [SemVer](https://semver.org); dates are ISO. +## Unreleased + +- The GitHub Action's manifest could not be parsed at all: one input description held an unquoted `(default: …)`, which YAML reads as a mapping, so every workflow using `Na5co/polygo/action@v0` failed at "Set up job" with a YAML error before polygo ran. Found by using the Action on a real pull request. Quoted, plus two guards: a test that lints every plain scalar in the manifest for the same trap, and a CI job that uses the Action from this checkout on a fixture and asserts it finds the planted error. + ## 0.1.8 — 2026-09-22 - `syntax` (error): a string file that does not parse — a comma dropped from a JSON catalog, an unclosed ``, a file in the wrong encoding — with the line the parser stopped at. It used to be invisible: the detector could not read the file, so the locale disappeared from the run and `check` reported the project as fine. Found while testing the reviewer on a pull request that corrupted a locale file and got a clean review. Every locale file the `locale_path` template matches is scanned, including locales the configuration never lists; when the broken file is one the project must read, the run says so and stops instead of failing with a stack of parser context. diff --git a/action/action.yml b/action/action.yml index f9e148a..38d339d 100644 --- a/action/action.yml +++ b/action/action.yml @@ -23,7 +23,7 @@ inputs: description: polygo release to install (tag), or "latest" default: latest locales: - description: Comma-separated locales to translate (default: all targets in polygo.toml) + description: "Comma-separated locales to translate (default: all targets in polygo.toml)" default: "" args: description: Extra arguments for `polygo translate` (e.g. --no-context) diff --git a/tests/action.rs b/tests/action.rs index 74e2fae..0fed110 100644 --- a/tests/action.rs +++ b/tests/action.rs @@ -238,3 +238,45 @@ fn action_yml_has_a_check_mode() { assert!(yml.contains(needle), "action.yml lacks {needle}"); } } + +/// The Action's manifest is YAML GitHub has to load before anything runs, and a broken one +/// fails every job that uses it with a parser error rather than a polygo message. polygo's +/// own CI never loads it, so it is linted here instead: a plain scalar holding `: ` (a +/// description with a parenthetical, say) is read as a mapping and breaks the file. +#[test] +fn action_manifest_is_loadable_yaml() { + let text = fs::read_to_string( + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("action/action.yml"), + ) + .unwrap(); + let mut bad = Vec::new(); + for (n, line) in text.lines().enumerate() { + let Some((key, value)) = line.split_once(": ") else { + continue; + }; + let key = key.trim(); + if !matches!(key, "description" | "default" | "value" | "name" | "title") { + continue; + } + let value = value.trim(); + let quoted = (value.starts_with('"') && value.ends_with('"')) + || (value.starts_with('\'') && value.ends_with('\'')) + || value.starts_with("${{") + || value.starts_with('|'); + if !quoted && (value.contains(": ") || value.ends_with(':')) { + bad.push(format!("{}: {line}", n + 1)); + } + } + assert!( + bad.is_empty(), + "action.yml has {} value(s) YAML reads as a mapping; quote them:\n{}", + bad.len(), + bad.join("\n") + ); + // The inputs the README and the tests rely on are all there. + for input in [ + "mode", "path", "strict", "sarif", "review", "version", "token", + ] { + assert!(text.contains(&format!("\n {input}:")), "no {input} input"); + } +} diff --git a/tests/release_layout.rs b/tests/release_layout.rs index 50dce70..d8980a0 100644 --- a/tests/release_layout.rs +++ b/tests/release_layout.rs @@ -60,6 +60,9 @@ fn release_assets_match_every_consumer() { "{ci}" ); assert!(ci.contains("cargo test --workspace"), "{ci}"); + // A job that actually loads action/action.yml: a manifest GitHub cannot parse fails + // every job that uses the Action, and nothing else in CI reads it. + assert!(ci.contains("uses: ./action"), "{ci}"); // The formula is generated, not checked in. assert!(!std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/packaging")).exists()); }