# Porting notes for package workflow `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 '' 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= 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. - [x] **`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). - [x] **`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. - [x] 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`.