diff --git a/.github/instructions/hve-core/workflows.instructions.md b/.github/instructions/hve-core/workflows.instructions.md index ac7e7a9c..3bf9d648 100644 --- a/.github/instructions/hve-core/workflows.instructions.md +++ b/.github/instructions/hve-core/workflows.instructions.md @@ -269,6 +269,37 @@ All workflows MUST pass the following validation checks: fi ``` +## YAML Expression Quoting + +GitHub Actions expression single quotes (`'...'`) inside `${{ }}` are NOT YAML quotes. The YAML parser treats them as literal characters in a plain scalar. If the expression text contains a colon followed by whitespace (`:` ), the YAML parser interprets it as a mapping value indicator and fails with `mapping values are not allowed in this context`. + +**Problem pattern:** + +```yaml +# FAILS: ': release' contains colon-space, YAML parser chokes +with: + my-input: ${{ startsWith(value, 'prefix: release') }} +``` + +**Acceptable solutions (in preference order):** + +1. **Shorten the literal to avoid colon-space.** Use a prefix that does not contain a colon followed by whitespace when the specificity tradeoff is acceptable. + + ```yaml + with: + my-input: ${{ startsWith(value, 'prefix') }} + ``` + +2. **Double-quote the entire expression.** This makes the value a YAML quoted scalar, preventing the parser from interpreting internal colon-space as structure. Add a comment explaining why quotes are required. + + ```yaml + with: + # Quotes required: expression literal contains ': ' which breaks YAML plain scalars + my-input: "${{ startsWith(value, 'prefix: release') }}" + ``` + +Avoid `format()` workarounds or environment variable indirection when the simpler options above apply. + ## Enforcement Statement The following scripts enforce compliance: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f9ff3d4d..49012ee2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -95,12 +95,20 @@ jobs: token: ${{ steps.app-token.outputs.token }} config-file: release-please-config.json manifest-file: .release-please-manifest.json + # On release commits, skip PR creation to prevent bogus PRs + # from draft release lazy tag timing. Release commits have zero + # unreleased changes so skipping loses nothing. The tag is + # created in the next step, ensuring subsequent runs find it. + skip-github-pull-request: ${{ github.event_name == 'push' && startsWith(github.event.head_commit.message, 'chore(main)') }} # Workaround: release-please with "draft": true uses lazy tag - # creation — the git tag is not materialized until the release is + # creation. The git tag is not materialized until the release is # published. Without the tag, release-please cannot find the draft - # on subsequent runs, breaking version anchoring. Create the tag - # explicitly via API. Replace with "force-tag-creation": true in + # on subsequent runs, breaking version anchoring and causing bogus + # major-version PRs. The skip-github-pull-request conditional above + # prevents PR creation on release commits (zero unreleased changes), + # and this step creates the tag so subsequent runs find the release. + # Replace both workarounds with "force-tag-creation": true in # release-please-config.json once release-please-action ships a # version that includes googleapis/release-please#2627. - name: Create git tag for draft release