Repository navigation
Conversation
PowerShell variable names are case-insensitive, so a check spelled `$LastExitCode` reads the same status as `$LASTEXITCODE`; the pwsh contract only matched the upper-case spelling and would flag the native command before it. `break`, `continue`, `begin`, `process`, `end`, `param` and `trap` are pwsh keywords, not programs, but a statement opening with one read as a native command. They join PWSH_KEYWORDS, all of whose entries pwsh's tokenizer reports as keywords. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
`run_block` only recognised the literal `run: |` header, so a pwsh step written with `run: |-`, `run: >` or `run: >-` fell through to the one-line reading, whose value is the bare indicator, and escaped the contract whatever its script did. Every block-scalar header now counts, with any chomping or indentation indicator and an optional trailing comment. A folded `>` block is read as YAML folds it before the statements are split: two lines at the block's indent are one line of script, so they are one command, not a native command followed by an unchecked one. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
The pwsh contract matched `shell: pwsh` and `run: |` only as sibling keys. Written as the step's first key, `- shell: pwsh` was never found as a pwsh step, and `- run: |` read as no script at all; both took the step out of the contract. A quoted `shell: 'pwsh'` is matched too. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
…empt
The pwsh contract accepted an exit-code check only in the statement right
after a native command, so a `Write-Output` or a plain assignment before
the check flagged a command whose status was in fact read.
A check further on now counts when every statement between leaves
`$LASTEXITCODE` alone. That is defined narrowly: outside single quotes,
no `(`, `{`, `&` or expanding `@"` here-string, and a pipeline of a
plain value or assignment of one, followed only by cmdlets that write
output or files. An assignment to `$LASTEXITCODE` is not a read. Running
off the end of the body is one, since GitHub's wrapper exits with
`$LASTEXITCODE`, so a last native command followed only by a
`Write-Output` is no longer flagged either. Every case in the tests,
on both sides of the rule, was run through pwsh first.
Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows up juhovainio's three non-blocking notes on #658. The
shell: pwshcontract inxtask/src/workflow_contract.rsfails a step whose native command can fail without the step noticing. Each note was a case where it misjudged a step: either it flagged a step whose status was read, or it never looked at a step that should be checked.A
$LASTEXITCODEcheck later in the step. Until now a check counted only in the statement directly after the native command, so putting aWrite-Outputor an assignment before the check flagged a command whose status was in fact read. A later check now counts if every statement in between leaves$LASTEXITCODEalone. The rule is deliberately narrow. Text inside single quotes is ignored, since it is literal. Outside it, the statement may not contain((a$(…)or(…)runs a command, even inside double quotes),{(a script block, or a hashtable whose values run),&(the call operator,&&) or an expanding@"here-string. It must also be a pipeline that starts with a plain value, or an assignment of one, or with one of a short list of output/file cmdlets (Write-Output,Write-Host,Out-File,Add-Content, …), and every later element must be one of those cmdlets. Assigning$LASTEXITCODEis not a read. Anything else counts as possibly resetting the status, includingexit, a function call, a loop's closing}and another native command. Reaching the end of the body also counts as a read. GitHub's pwsh wrapper appendsif ((Test-Path -LiteralPath variable:\LASTEXITCODE)) { exit $LASTEXITCODE }, so a last native command followed only by aWrite-Outputis no longer flagged.Every
run:block form.run_blockmatched only the literalrun: |header. A pwsh step written asrun: |-,run: >orrun: >-fell through to the one-line reading, whose value is the bare indicator, so the step escaped the contract. Every block-scalar header now matches, with any chomping or indentation indicator and an optional trailing comment. A folded>block is read the way YAML folds it: two lines at the block's indent become one line of script, so they are one command. The folding was compared against PyYAML on the same fixtures. Two more ways a step escaped turned up while doing this, and they are fixed in a separate commit:shell:orrun:written as the step's first key (- shell: pwsh,- run: |).run_blockis shared, so the other workflow checks that use it read these forms too. No workflow in the repository uses any of them, so those checks behave as before.Case and keywords. PowerShell variable names are case-insensitive, so
$LastExitCodenow counts as a check.break,continue,begin,process,end,paramandtrapjoinPWSH_KEYWORDS. pwsh's own tokenizer confirms that every entry is a keyword.Verification:
$a = $b = git …), the end-of-body read, and both the single- and double-quote handling. One mutant survives: allowing a plain value after a pipe. pwsh rejects that at parse time ("Expressions are only allowed as the first element of a pipeline"), so no real step can take that form.Write-Output 'x' || git …, which is rejected conservatively.Out of scope, and still not seen by the contract: a step that inherits pwsh from
defaults: run: shell: pwsh(no workflow sets one); a native command whose output is assigned ($out = cargo metadata), which is not classed as native; and the literal-onlyrun: |matching inmultiline_run_blocksandpreflight_step_shape, which are separate from this contract.This is test-only (xtask contract tests) and changes nothing a CLI user can observe, so it needs no Gherkin scenario.
tests/e2e-cucumber/expectations.tomlfor the fixed ticket ID and removed/narrowed any now-stale xfail rows. (No ticket and no xfail rows involved.)docs/architecture.md. (Not applicable.)$LASTEXITCODEbefore anything that could run another program or reset it, which is the rule the tests exercise.)