Parse is a port of model/convert and model/converter/* of
@actions/workflow-parser 0.3.61 on top of package template. The reference's
errors are replayed in TestConvertAgainstReference (153 files in
testdata/convert, answers in testdata/convert.json, generated by
gen/convert.mjs from the unmodified npm package).
The parts below are not in that package. They are written from GitHub's
documentation and from what is known of the official runner, and each needs a
recording of github.com (ermite-conformance) or a look at the C# runner
before being trusted. Every one carries a TODO(verify with recording) in the
code.
Differences from the reference
branches with branches-ignore (and tags, paths): the reference
accepts them; GitHub refuses the file. Parse reports
Event 'push' cannot use both 'branches' and 'branches-ignore'. Use only one of them. (our wording, to replace with GitHub's). Applied to every event.
A filter list made only of ! patterns (branches: ['!main']) is an
error (Event 'push' filter 'branches' has only patterns that start with '!'...), and so is a pattern that does not compile. Both are ours; GitHub's
texts are unknown. Applied to every event and every filter.
Matrix exclude with a key that is not a vector of the matrix is
Matrix exclude key '<k>' does not match any key within the matrix
(GitHub's text as remembered, not recorded).
Limits that GitHub does not state: the cross product of the vectors may
not exceed MaxMatrixProduct (65 536) before exclusions, include and exclude
have at most MaxMatrixEntries (256) entries each, and the context is
checked every 1 024 combinations. They keep a hostile matrix from running
for long; a legitimate one (at most 256 jobs) is far below them. The
product limit also refuses a matrix GitHub would accept by excluding most
of a huge product.
Reusable workflows are parsed (uses, with, secrets, inherit) but
not followed, and the called workflow's inputs and secrets are not checked.
Unknown event types are reported by the schema (Unexpected value 'x').
permissions are modelled but not validated beyond the schema.
Job.If, Step.If: the status-function wrapping is the reference's
(success() && (cond) when the condition calls none of success,
failure, cancelled, always, looking through nested calls).
Recorded (github.com, ermite-conformance corpus)
Opt-in check against the corpus itself: ERMITE_CORPUS=<ermite-conformance checkout> go test ./workflow -run Corpus (corpus_test.go) replays the
scenario files and compares the runs SkipCI, MatchPush and Parse predict with
the recordings. The cases copied into the unit tests stay.
Status functions (success(), failure(), always(), cancelled()) in
env or run expressions make the workflow invalid (recorded:
expr-status-in-env, expr-status-in-run, each a failed run with no job). The
text Unrecognized function: 'success'. Located at position 1 within expression: success() is the one observed on the github.com run page, not in
the recordings; TestRecordedInvalidWorkflows.
Invalid workflow messages (the recordings hold the failed run only, not the
text; the messages are those of the reference parser, which refuses the same
files): The workflow must contain at least one job with no dependencies. (no root job: needs on an unknown job or a cycle with no
other root), Job 'x' depends on unknown job 'y'., Job 'x' depends on job 'y' which creates a cycle in the dependency graph., Required property is missing: runs-on, Unexpected value 'unknown-key' (invalid-needs,
invalid-schema).
To verify (recordings)
Glob syntax (glob.go): *, **, ?, +, [], \, !. The
cheat sheet's examples pass; **/ matching nothing, ? and + after a
], * or an escaped character, [!x], an unclosed [ (literal here)
and /-prefixed patterns are assumed.
MatchPush (recorded: push-tags-limit, filter-tags,
filter-branches-ignore, filter-tag-branches-only, filter-paths-tag,
filter-paths*, push-delete-*): deletes never run; more than 3 tags run
nothing for tags (not for the branch); branches-only filter and a tag
push (no run) and the reverse; with both filters each ref uses its own;
paths only for branches; the first 3000 files, also for
paths-ignore (not 300). Still assumed: a push whose diff is empty
never matches a paths filter and always matches a paths-ignore one;
unknown ref kinds never match; truncated list passes.
PushInfo.Created is not used: a new branch's file list is the
caller's job (diff against the default branch merge-base).
SkipCI (recorded: skip-directives, skip-directives-placement,
skip-checks-trailer, skip-directive-tag): markers are case-sensitive,
anywhere; skip-checks: true needs two blank lines before it and
nothing after it, in exact case. The tag case is the caller's: the
message of the commit the tag points at. Assumed, not recorded: only
the head commit counts (the scenarios push one commit at a time); a
line of spaces or tabs is not a blank line; more than two blank lines
count like two.
ExpandMatrix: the unknown exclude key error (once per offending
entry) and the over-256 message (configurationLimitError, which matches
ErrTooManyConfigurations with errors.Is) are the
texts observed on the github.com run page for matrix-edge-values and
matrix-over-256, not in the recordings (a failed run with no job). To
verify: order of the product (first key slowest), include
semantics (added to every matching combination, never overwriting
original values, later includes overwrite earlier added values, a
non-matching include is a new combination, match by vector keys only),
exclude (partial match, applied before include), the 256 limit counting
combinations after include, key case-insensitivity, value comparison
(same kind; objects by containment), the error when all combinations are
excluded (GitHub may fail the run differently), empty vector error.
A null or empty ('') matrix value is not shown in the job name
(recorded: matrix-edge-values).
Job display names (MatrixCombo.DisplayName, Job.DisplayName):
recorded in matrix-job-name and matrix-edge-values: name (v1, v2)
listing the values in key order, numbers and booleans as
CoerceString shows them, a name that does not read matrix followed
by the values. Assumed: the keys added by include come after the vector
keys; arrays and objects are left out; a name that reads the matrix
context (found in the expression's syntax tree, not by searching the
text) is used as is.
strategy context (Strategy.Context): fail-fast, job-index,
job-total, max-parallel (the job count when unlimited).
fail-fast and max-parallel accept strings ("false", "2") after
evaluation; max-parallel must be a whole number ≥ 1.
The 1 000-commit and 3 000-file limits themselves are the caller's.
New in the second pass
Job.EvaluateRunsOn, Job.EvaluateIf, Step.EvaluateIf and
Workflow.JobLevels are conveniences for the planner and are not in the
reference. EvaluateRunsOn applies the reference's runs-on checks to the
evaluated value (labels must be strings; group names as in the converter).
JobLevels is a layering of needs by depth, file order inside a level.
The reference versions and the regeneration of its answers are in
../template/PORTING.md.
source
1# Porting notes for package workflow23`Parse` is a port of `model/convert` and `model/converter/*` of4`@actions/workflow-parser` 0.3.61 on top of package `template`. The reference's5errors are replayed in `TestConvertAgainstReference` (153 files in6`testdata/convert`, answers in `testdata/convert.json`, generated by7`gen/convert.mjs` from the unmodified npm package).89The parts below are **not** in that package. They are written from GitHub's10documentation and from what is known of the official runner, and each needs a11recording of github.com (`ermite-conformance`) or a look at the C# runner12before being trusted. Every one carries a `TODO(verify with recording)` in the13code.1415## Differences from the reference1617- **`branches` with `branches-ignore`** (and `tags`, `paths`): the reference18 accepts them; GitHub refuses the file. `Parse` reports19`Event 'push' cannot use both 'branches' and 'branches-ignore'. Use only one20 of them.` (our wording, to replace with GitHub's). Applied to every event.21- **A filter list made only of `!` patterns** (`branches: ['!main']`) is an22 error (`Event 'push' filter 'branches' has only patterns that start with23 '!'...`), and so is a pattern that does not compile. Both are ours; GitHub's24 texts are unknown. Applied to every event and every filter.25- **Matrix exclude** with a key that is not a vector of the matrix is26`Matrix exclude key '<k>' does not match any key within the matrix`27 (GitHub's text as remembered, not recorded).28- **Limits that GitHub does not state**: the cross product of the vectors may29 not exceed `MaxMatrixProduct` (65 536) before exclusions, include and exclude30 have at most `MaxMatrixEntries` (256) entries each, and the context is31 checked every 1 024 combinations. They keep a hostile matrix from running32 for long; a legitimate one (at most 256 jobs) is far below them. The33 product limit also refuses a matrix GitHub would accept by excluding most34 of a huge product.35- **Reusable workflows** are parsed (`uses`, `with`, `secrets`, `inherit`) but36 not followed, and the called workflow's inputs and secrets are not checked.37- Unknown event types are reported by the schema (`Unexpected value 'x'`).38-`permissions` are modelled but not validated beyond the schema.39-`Job.If`, `Step.If`: the status-function wrapping is the reference's40 (`success() && (cond)` when the condition calls none of `success`,41`failure`, `cancelled`, `always`, looking through nested calls).4243## Recorded (github.com, ermite-conformance corpus)4445Opt-in check against the corpus itself: `ERMITE_CORPUS=<ermite-conformance46checkout> go test ./workflow -run Corpus` (`corpus_test.go`) replays the47scenario files and compares the runs SkipCI, MatchPush and Parse predict with48the recordings. The cases copied into the unit tests stay.4950- Status functions (`success()`, `failure()`, `always()`, `cancelled()`) in51`env` or `run` expressions make the workflow invalid (recorded:52 expr-status-in-env, expr-status-in-run, each a failed run with no job). The53 text `Unrecognized function: 'success'. Located at position 1 within54 expression: success()` is the one observed on the github.com run page, not in55 the recordings; `TestRecordedInvalidWorkflows`.56- Invalid workflow messages (the recordings hold the failed run only, not the57 text; the messages are those of the reference parser, which refuses the same58 files): `The workflow must contain at least one job with59 no dependencies.` (no root job: needs on an unknown job or a cycle with no60 other root), `Job 'x' depends on unknown job 'y'.`, `Job 'x' depends on job61 'y' which creates a cycle in the dependency graph.`, `Required property is62 missing: runs-on`, `Unexpected value 'unknown-key'` (invalid-needs,63 invalid-schema).6465## To verify (recordings)6667- [ ]**Glob syntax** (`glob.go`): `*`, `**`, `?`, `+`, `[]`, `\`, `!`. The68 cheat sheet's examples pass; `**/` matching nothing, `?` and `+` after a69`]`, `*` or an escaped character, `[!x]`, an unclosed `[` (literal here)70 and `/`-prefixed patterns are assumed.71- [x]**`MatchPush`** (recorded: push-tags-limit, filter-tags,72 filter-branches-ignore, filter-tag-branches-only, filter-paths-tag,73 filter-paths*, push-delete-*): deletes never run; more than 3 tags run74 nothing for tags (not for the branch); branches-only filter and a tag75 push (no run) and the reverse; with both filters each ref uses its own;76`paths` only for branches; the first **3000** files, also for77`paths-ignore` (not 300). Still assumed: a push whose diff is empty78 never matches a `paths` filter and always matches a `paths-ignore` one;79 unknown ref kinds never match; truncated list passes.80- [ ]**`PushInfo.Created`** is not used: a new branch's file list is the81 caller's job (diff against the default branch merge-base).82- [x]**`SkipCI`** (recorded: skip-directives, skip-directives-placement,83 skip-checks-trailer, skip-directive-tag): markers are case-sensitive,84 anywhere; `skip-checks: true` needs two blank lines before it and85 nothing after it, in exact case. The tag case is the caller's: the86 message of the commit the tag points at. Assumed, not recorded: only87 the head commit counts (the scenarios push one commit at a time); a88 line of spaces or tabs is not a blank line; more than two blank lines89 count like two.90- [ ]**`ExpandMatrix`**: the unknown exclude key error (once per offending91 entry) and the over-256 message (`configurationLimitError`, which matches92`ErrTooManyConfigurations` with `errors.Is`) are the93 texts observed on the github.com run page for matrix-edge-values and94 matrix-over-256, not in the recordings (a failed run with no job). To95 verify: order of the product (first key slowest), include96 semantics (added to every matching combination, never overwriting97 original values, later includes overwrite earlier added values, a98 non-matching include is a new combination, match by vector keys only),99 exclude (partial match, applied before include), the 256 limit counting100 combinations after include, key case-insensitivity, value comparison101 (same kind; objects by containment), the error when all combinations are102 excluded (GitHub may fail the run differently), empty vector error.103- [x] A **null or empty (`''`) matrix value** is not shown in the job name104 (recorded: matrix-edge-values).105- [ ]**Job display names** (`MatrixCombo.DisplayName`, `Job.DisplayName`):106 recorded in matrix-job-name and matrix-edge-values: `name (v1, v2)`107 listing the values in key order, numbers and booleans as108`CoerceString` shows them, a name that does not read `matrix` followed109 by the values. Assumed: the keys added by include come after the vector110 keys; arrays and objects are left out; a name that reads the `matrix`111 context (found in the expression's syntax tree, not by searching the112 text) is used as is.113- [ ]**`strategy` context** (`Strategy.Context`): `fail-fast`, `job-index`,114`job-total`, `max-parallel` (the job count when unlimited).115- [ ]`fail-fast` and `max-parallel` accept strings (`"false"`, `"2"`) after116 evaluation; `max-parallel` must be a whole number ≥ 1.117- [ ] The 1 000-commit and 3 000-file limits themselves are the caller's.118119## New in the second pass120121-`Job.EvaluateRunsOn`, `Job.EvaluateIf`, `Step.EvaluateIf` and122`Workflow.JobLevels` are conveniences for the planner and are not in the123 reference. `EvaluateRunsOn` applies the reference's runs-on checks to the124 evaluated value (labels must be strings; group names as in the converter).125`JobLevels` is a layering of `needs` by depth, file order inside a level.126- The reference versions and the regeneration of its answers are in127`../template/PORTING.md`.