From 29f2782b1b845ff8e56d2569e9a0bdca8b10a4e0 Mon Sep 17 00:00:00 2001 From: Amp Date: Thu, 20 Aug 2026 07:10:37 +0000 Subject: [PATCH] PB-3032: Snapshot checkout contracts per commit Admit frozen actions/checkout tag and release-branch history through generated per-commit contracts. Preserve declared inputs, runner-plugin fetch defaults, and output availability while continuing to reject future unknown commits. Amp-Thread-ID: https://ampcode.com/threads/T-01a01dd0-0453-7533-9505-6a8f4296dda7 Co-authored-by: Ming Guo --- docs/compatibility.md | 38 +-- internal/action/integration/checkout.go | 107 +++---- .../checkout_main_commits_generated.go | 103 ------ .../checkout_profiles_generated.go | 292 ++++++++++++++++++ internal/action/integration/checkout_test.go | 72 ++++- .../cmd/generate-checkout-profiles/main.go | 175 +++++++++++ internal/compiler/actions_test.go | 5 + internal/runtime/checkout_test.go | 7 +- mise.toml | 2 +- scripts/update-checkout-main-commits | 43 --- 10 files changed, 619 insertions(+), 225 deletions(-) delete mode 100644 internal/action/integration/checkout_main_commits_generated.go create mode 100644 internal/action/integration/checkout_profiles_generated.go create mode 100644 internal/action/integration/cmd/generate-checkout-profiles/main.go delete mode 100755 scripts/update-checkout-main-commits diff --git a/docs/compatibility.md b/docs/compatibility.md index ce8d26db..11171050 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -1050,7 +1050,7 @@ Pre conditions use the status and action-scoped environment available when prepa ### Checkout action -**🟡 Supported subset.** The final v1.2.0, v2.8.0, and v3.7.0 release commits are admitted exactly. Resolved commits in the v4-and-later range of the static [`actions/checkout` upstream `main` snapshot](https://github.com/actions/checkout/tree/f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a) are also admitted. The following known releases remain admitted even when their commits aren't reachable from that snapshot: +**🟡 Supported subset.** Immutable commits captured from frozen upstream tags, `main`, `master`, and `releases/v1` through `releases/v6` snapshots are admitted. The snapshot includes historical development and release commits across v1 through v7. These known releases identify the principal contracts: | Release | Commit | | --- | --- | @@ -1063,38 +1063,40 @@ Pre conditions use the status and action-scoped environment available when prepa | v7.0.0 corpus pin | [`9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0`](https://github.com/actions/checkout/tree/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0) | | v7.0.1 | [`3d3c42e5aac5ba805825da76410c181273ba90b1`](https://github.com/actions/checkout/tree/3d3c42e5aac5ba805825da76410c181273ba90b1) | -References such as `main` and `master` work only when they resolve to a supported release or commit from upstream `main`. Buildkite checks out the repository without running the action's JavaScript. Each release accepts only the inputs available in that release. Earlier releases reject later inputs. Other commits before v3.7.0 and unknown commits are unsupported. +Mutable refs work only while they resolve to a commit in the frozen snapshots. Every admitted commit uses the native adapter; the upstream JavaScript doesn't run. Each commit retains the inputs, full-history default, and outputs declared by its upstream contract. For example, early v2 commits reject later v2 inputs, and v4.0 and v4.1 commits don't expose the `ref` and `commit` outputs. Commits absent from the snapshots and manifests with unsupported output contracts remain unsupported. Compilation emits `W_CHECKOUT_LEGACY_RELEASE` for v1.2.0 and v2.8.0 to nudge an upgrade to v4 or later. -Buildkite runs v1.2.0 like v1 and v2.8.0 like v2, and warns about their differences from v4 and later. Neither release sets the `ref` or `commit` outputs added in v4.2.0. v1.2.0 also fetches full history by default when `fetch-depth` is omitted. Upgrade only if your workflow needs those outputs or different v1 history behavior. Otherwise, keep the current version. +Maintainers can refresh the frozen refs and per-commit profiles with `go generate ./internal/action/integration`. Regeneration admits only commits reachable from the selected upstream tags and branches at that time; it doesn't blanket-admit future commits. -Maintainers can update supported v4-and-later commits with `go generate ./internal/action/integration`. This does not add support for more numbered releases. +Buildkite runs v1.2.0 like v1 and v2.8.0 like v2, and warns about their differences from v4 and later. Neither release sets the `ref` or `commit` outputs added in v4.2.0. v1.2.0 also fetches full history by default when `fetch-depth` is omitted. Upgrade only if your workflow needs those outputs or different v1 history behavior. Otherwise, keep the current version. The adapter checks out a detached commit or static branch from the event repository at the workspace root or a clean nested directory. It uses Buildkite repository-provider Git credentials when the job provides them; otherwise, it fetches anonymously. Credentials are scoped to the Git commands that fetch repository, LFS, or submodule data and are never persisted. +An explicit input is accepted only when the snapshotted manifest for that commit declares it. The following value restrictions then apply: + | Input | Supported values | | --- | --- | | `repository` | Omitted, or the event `owner/repo`. | | `ref` | Omitted, empty, a lowercase 40-hex commit, or a static branch in the event repository. A direct `github.sha` or `needs..outputs.` expression must resolve at runtime to the exact event SHA. | | `token` | Omitted only. | -| `ssh-key`, `ssh-known-hosts` | v2.8.0 and later: omitted or empty. v1.2.0: omitted. | -| `ssh-strict` | v2.8.0 and later: omitted or `true`. v1.2.0: omitted. | -| `ssh-user` | v4 and later: omitted or `git`. Earlier releases: omitted. | -| `persist-credentials` | v2.8.0 and later: omitted or `false`. v1.2.0: omitted. | +| `ssh-key`, `ssh-known-hosts` | When declared by the commit: omitted or empty. Otherwise omitted. | +| `ssh-strict` | When declared by the commit: omitted or `true`. Otherwise omitted. | +| `ssh-user` | When declared by the commit: omitted or `git`. Otherwise omitted. | +| `persist-credentials` | When declared by the commit: omitted or `false`. Otherwise omitted. | | `path` | Omitted, empty, or a clean relative directory without a `.git` path segment. The resolved path stays inside the workspace and can't traverse symbolic-link parents. | | `clean` | Omitted, `true`, or `false`; the root workspace must be empty, or the selected path must be absent. Existing-directory reuse is unsupported, so `false` differs only by matching workflows that select a fresh target. | -| `filter` | v4 and later: omitted, empty, or one Git partial-clone filter without control characters. Earlier releases: omitted. | -| `sparse-checkout` | v3.7.0 and later: omitted, empty, or up to 1,000 non-empty patterns totaling at most 1 MiB. Earlier releases: omitted. | -| `sparse-checkout-cone-mode` | v3.7.0 and later: omitted, `true`, or `false`. Earlier releases: omitted. | -| `fetch-depth` | Omitted or a nonnegative integer; `0` fetches full history. v1.2.0 fetches full history when omitted. | -| `fetch-tags` | v3.7.0 and later: omitted, `true`, or `false`. Earlier releases: omitted. | -| `show-progress` | v4 and later: omitted, `true`, or `false`. Earlier releases: omitted. | +| `filter` | When declared by the commit: omitted, empty, or one Git partial-clone filter without control characters. Otherwise omitted. | +| `sparse-checkout` | When declared by the commit: omitted, empty, or up to 1,000 non-empty patterns totaling at most 1 MiB. Otherwise omitted. | +| `sparse-checkout-cone-mode` | When declared by the commit: omitted, `true`, or `false`. Otherwise omitted. | +| `fetch-depth` | Omitted or a nonnegative integer; `0` fetches full history. Historical runner-plugin commits fetch full history when omitted. | +| `fetch-tags` | When declared by the commit: omitted, `true`, or `false`. Otherwise omitted. | +| `show-progress` | When declared by the commit: omitted, `true`, or `false`. Otherwise omitted. | | `lfs` | Omitted, `true`, or `false`. `true` requires Git LFS in the job image. | | `submodules` | Omitted, `false`, `true`, or `recursive`; whitespace is trimmed and casing is ignored. | -| `set-safe-directory` | v2.8.0 and later: omitted or `true`. v1.2.0: omitted. | -| `github-server-url` | v3.7.0 and later: omitted, empty, or `https://github.com`. Earlier releases: omitted. | -| `allow-unsafe-pr-checkout` | v2.8.0 and later: omitted or `false`. v1.2.0: omitted. | +| `set-safe-directory` | When declared by the commit: omitted or `true`. Otherwise omitted. | +| `github-server-url` | When declared by the commit: omitted, empty, or `https://github.com`. Otherwise omitted. | +| `allow-unsafe-pr-checkout` | When declared by the commit: omitted or `false`. Otherwise omitted. | -The `ref` and `commit` outputs are unavailable for v1.2.0, v2.8.0, and v3.7.0. Upstream added them in v4.2.0. +The `ref` and `commit` outputs are available only for commits whose action manifest declares them. Upstream added both outputs in v4.2.0. The `false` value and omission do not run submodule commands. The `true` value runs native Git for direct children, and `recursive` includes nested children. Relative URLs and `fetch-depth` follow native Git behavior. Public and private GitHub submodules are supported under the job's repository access; external HTTPS submodules are anonymous. `git@github.com:` URLs are rewritten to HTTPS. Other SSH and non-HTTPS transports are unsupported. diff --git a/internal/action/integration/checkout.go b/internal/action/integration/checkout.go index 0f8c85a4..955bdf02 100644 --- a/internal/action/integration/checkout.go +++ b/internal/action/integration/checkout.go @@ -1,4 +1,4 @@ -//go:generate ../../../scripts/update-checkout-main-commits +//go:generate go run ./cmd/generate-checkout-profiles package integration @@ -12,10 +12,8 @@ import ( ) const ( - // CheckoutV1Commit through CheckoutV7Commit are the current audited release - // implementations. CheckoutV1Commit, CheckoutV2Commit, and CheckoutV3Commit - // are the final v1, v2, and v3 releases and are admitted exactly rather - // than extending the v4-and-later main-branch snapshot. + // CheckoutV1Commit through CheckoutV7Commit identify principal release + // contracts within the broader frozen upstream snapshots. // CheckoutV7InitialCommit is retained because it is pinned by the OSS // compatibility corpus; its later v7.0.1 changes do not affect the adapter's // bounded exact-event-SHA operation. @@ -40,73 +38,76 @@ var checkoutCommits = map[string]string{ CheckoutV7Commit: "v7.0.1", } +// checkoutContract records the adapter-visible contract declared by one +// immutable upstream action manifest. +type checkoutContract struct { + // inputs is a sorted, comma-separated set of names declared by the + // immutable upstream action manifest. + inputs string + fullHistory bool + refOutput, commitOutput bool +} + type checkoutInputRule struct { - generation int - valid func(value, repository string) bool + valid func(value, repository string) bool } -// checkoutInputRules owns the supported input routing, release contract, and -// value validation for the bounded native adapter. +// checkoutInputRules owns the supported value validation for the bounded +// native adapter. checkoutCommitContracts owns each immutable commit's input +// declarations. var checkoutInputRules = map[string]checkoutInputRule{ - "repository": {1, strings.EqualFold}, - "ref": {1, func(value, _ string) bool { + "repository": {strings.EqualFold}, + "ref": {func(value, _ string) bool { return value == "" || ValidCheckoutSHA(value) || validCheckoutBranch(value) }}, - "fetch-depth": {1, func(value, _ string) bool { + "fetch-depth": {func(value, _ string) bool { depth, err := strconv.ParseUint(value, 10, 31) return err == nil && depth <= 1<<31-1 }}, - "clean": {1, func(value, _ string) bool { return actionBoolean(value) }}, - "lfs": {1, func(value, _ string) bool { return actionBoolean(value) }}, - "submodules": {1, func(value, _ string) bool { + "clean": {func(value, _ string) bool { return actionBoolean(value) }}, + "lfs": {func(value, _ string) bool { return actionBoolean(value) }}, + "submodules": {func(value, _ string) bool { switch strings.ToLower(strings.TrimSpace(value)) { case "", "false", "true", "recursive": return true } return false }}, - "path": {1, func(value, _ string) bool { + "path": {func(value, _ string) bool { return value == "" || validCheckoutPath(value) }}, - "ssh-key": {2, func(value, _ string) bool { return value == "" }}, - "ssh-known-hosts": {2, func(value, _ string) bool { return value == "" }}, - "ssh-strict": {2, func(value, _ string) bool { return actionTrue(value) }}, - "persist-credentials": {2, func(value, _ string) bool { return actionFalse(value) }}, - "set-safe-directory": {2, func(value, _ string) bool { return actionTrue(value) }}, - "allow-unsafe-pr-checkout": {2, func(value, _ string) bool { return actionFalse(value) }}, - "fetch-tags": {3, func(value, _ string) bool { return actionBoolean(value) }}, - "sparse-checkout": {3, func(value, _ string) bool { return validSparseCheckout(value) }}, - "sparse-checkout-cone-mode": {3, func(value, _ string) bool { return actionBoolean(value) }}, - "github-server-url": {3, func(value, _ string) bool { + "ssh-key": {func(value, _ string) bool { return value == "" }}, + "ssh-known-hosts": {func(value, _ string) bool { return value == "" }}, + "ssh-strict": {func(value, _ string) bool { return actionTrue(value) }}, + "persist-credentials": {func(value, _ string) bool { return actionFalse(value) }}, + "set-safe-directory": {func(value, _ string) bool { return actionTrue(value) }}, + "allow-unsafe-pr-checkout": {func(value, _ string) bool { return actionFalse(value) }}, + "fetch-tags": {func(value, _ string) bool { return actionBoolean(value) }}, + "sparse-checkout": {func(value, _ string) bool { return validSparseCheckout(value) }}, + "sparse-checkout-cone-mode": {func(value, _ string) bool { return actionBoolean(value) }}, + "github-server-url": {func(value, _ string) bool { return value == "" || value == "https://github.com" }}, - "filter": {4, func(value, _ string) bool { return validCheckoutFilter(value) }}, - "show-progress": {4, func(value, _ string) bool { return actionBoolean(value) }}, - "ssh-user": {4, func(value, _ string) bool { return value == "git" }}, + "filter": {func(value, _ string) bool { return validCheckoutFilter(value) }}, + "show-progress": {func(value, _ string) bool { return actionBoolean(value) }}, + "ssh-user": {func(value, _ string) bool { return value == "git" }}, } -func checkoutGeneration(commit string) int { - switch commit { - case CheckoutV1Commit: - return 1 - case CheckoutV2Commit: - return 2 - case CheckoutV3Commit: - return 3 - } - return 4 +func (c checkoutContract) declaresInput(name string) bool { + return strings.Contains(","+c.inputs+",", ","+name+",") } // CheckoutSupportsOutputs reports whether the admitted release declares the // ref and commit outputs, added upstream in v4.2.0. func CheckoutSupportsOutputs(commit string) bool { - return checkoutGeneration(commit) >= 4 + contract, ok := checkoutCommitContracts[commit] + return ok && contract.refOutput && contract.commitOutput } // CheckoutDefaultsToFullHistory reports whether the admitted release fetched // full history when fetch-depth was omitted, as v1's runner plugin did. func CheckoutDefaultsToFullHistory(commit string) bool { - return commit == CheckoutV1Commit + return checkoutCommitContracts[commit].fullHistory } // LegacyCheckoutRelease reports the admitted release label for the v1 and v2 @@ -118,15 +119,12 @@ func LegacyCheckoutRelease(commit string) (string, bool) { return "", false } -// validateCheckoutCommit admits known releases and a static snapshot of -// commits reachable from upstream main. Mutable references are resolved before -// this check, so changes after the snapshot are rejected until regeneration. +// validateCheckoutCommit admits commits with contracts captured from frozen +// upstream release and main snapshots. Mutable references are resolved before +// this check, so changes after the snapshots are rejected until regeneration. func validateCheckoutCommit(commit string) error { - if _, ok := checkoutCommits[commit]; ok { - return nil - } - if _, ok := checkoutMainCommits[commit]; !ok { - supported := append(sortedCheckoutCommits(), "upstream main snapshot ("+checkoutMainSnapshotCommit+")") + if _, ok := checkoutCommitContracts[commit]; !ok { + supported := append(sortedCheckoutCommits(), "frozen upstream release and main snapshots (main "+checkoutMainSnapshotCommit+")") return versionError("actions/checkout", "native adapter", commit, supported) } return nil @@ -144,9 +142,12 @@ func sortedCheckoutCommits() []string { // ValidateCheckoutInputs enforces the release-specific input contract // implemented by the tokenless event-repository checkout adapter. func ValidateCheckoutInputs(commit string, inputs map[string]string, repository, sha string) error { + contract, ok := checkoutCommitContracts[commit] + if !ok { + return versionError("actions/checkout", "native adapter", commit, append(sortedCheckoutCommits(), "frozen upstream release and main snapshots (main "+checkoutMainSnapshotCommit+")")) + } names := sortedNames(inputs) seen := make(map[string]bool, len(names)) - generation := checkoutGeneration(commit) for _, name := range names { value := inputs[name] normalized := strings.ToLower(name) @@ -154,13 +155,13 @@ func ValidateCheckoutInputs(commit string, inputs map[string]string, repository, return fmt.Errorf("duplicate case-insensitive input %q is unsupported", name) } seen[normalized] = true + if !contract.declaresInput(normalized) { + return fmt.Errorf("explicit input %q is unsupported by this actions/checkout release", name) + } rule, ok := checkoutInputRules[normalized] if !ok { return fmt.Errorf("explicit input %q value is unsupported", name) } - if rule.generation > generation { - return fmt.Errorf("explicit input %q is unsupported by this actions/checkout release", name) - } if !rule.valid(value, repository) { return fmt.Errorf("explicit input %q value is unsupported", name) } diff --git a/internal/action/integration/checkout_main_commits_generated.go b/internal/action/integration/checkout_main_commits_generated.go deleted file mode 100644 index 1bc0b016..00000000 --- a/internal/action/integration/checkout_main_commits_generated.go +++ /dev/null @@ -1,103 +0,0 @@ -// Code generated by scripts/update-checkout-main-commits; DO NOT EDIT. - -package integration - -const checkoutMainSnapshotCommit = "f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a" - -var checkoutMainCommits = map[string]struct{}{ - "009b9ae9e446ad8d9b8c809870b0fbcc5e03573e": {}, - "033fa0dc0b82693d8986f1016a0ec2c5e7d9cbb1": {}, - "064fe7f3312418007dea2b49a19844a9ee378f49": {}, - "069c6959146423d11cd0184e6accf28f9d45f06e": {}, - "08c6903cd8c0fde910a37f88322edcfb5dd907a8": {}, - "08eba0b27e820071cde6df949e0beb9ba4906955": {}, - "09d2acae674a48949e3602304ab46fd20ae0c42f": {}, - "0ad4b8fadaa221de15dcec353f45205ec38ea70b": {}, - "0c366fd6a839edf440554fa01a7085ccba70ac98": {}, - "0f9f3aa320cb53abeb534aeb54048075d9697a0e": {}, - "1044a6dea927916f2c38ba5aeffbc0a847b1221a": {}, - "11bd71901bbe5b1630ceea73d27597364c9af683": {}, - "12cd2235efa0937479335606d7c3ac9f6c0973b1": {}, - "130a169078a413d3a5246a393625e8e742f387f6": {}, - "163217dfcd28294438ea1c1c149cfaf66eec283e": {}, - "1af3b93b6815bc44a9784bd300feb67ff0d1eeb3": {}, - "1cce3390c2bfda521930d01229c073c7ff920824": {}, - "1d96c772d19495a3b5c517cd2bc0cb401ea0529f": {}, - "24ed1a352802348c9e4e8d13de9177fb95b537ba": {}, - "2650dbd060003e3b5ae211e4358852f336b682a7": {}, - "28802689a136bfcdb721715abd713740beecbe07": {}, - "2d7d9f7ff5b310f983d059b68785b3c74d8b8edd": {}, - "37b082107ba410260a3aaddf93122e04801ce631": {}, - "3b9b8c884f6b4bb4d5be2779c26374abadae0871": {}, - "3d3c42e5aac5ba805825da76410c181273ba90b1": {}, - "3df4ab11eba7bda6032a0b82a6bb43b11571feac": {}, - "3f603f6d5e9f40714f97b2f017aa0df2a443192a": {}, - "43045ae669be728bd34ed56fcd1a230c0dc4d8e2": {}, - "44c2b7a8a4ea60a981eaca3cf939b5f4305c123b": {}, - "473055ba18d6d2da209cd46110aadb9275e3194e": {}, - "4f1f4aec02e41874fa0262ea8ff5172d7978ad1e": {}, - "537c7ef99cef6e5ddb5e7ff5d16d14510503801d": {}, - "556e4c3cb0b8b54b734286d5439adadcb0a8cb92": {}, - "5de26ee9b11f6ffa02406edecc0e750256af18b1": {}, - "62661c4e71a304b2823ed026347b8d34c3eac541": {}, - "631c7dc4f80f88219c5ee78fee08c6b62fac8da1": {}, - "631c942040754b6e095e929c1677c07e10ed4f87": {}, - "692973e3d937129bcbf40652eb9f2f61becf3332": {}, - "6b42224f41ee5dfe5395e27c8b2746f1f9955030": {}, - "6ccd57f4c5d15bdc2fef309bd9fb6cc9db2ef1c6": {}, - "6d193bf28034eafb982f37bd894289fe649468fc": {}, - "71cf2267d89c5cb81562390fa70a37fa40b1305e": {}, - "72f2cec99f417b1a1c5e2e88945068983b7965f9": {}, - "79102f25032f3f70eb9d8386b1451cf1ef55902d": {}, - "7cdaf2fbc075e6f3b9ca94cfd6cec5adc8a75622": {}, - "7d09575332117a40b46e5e020664df234cd416f3": {}, - "8410ad0602e1e429cee44a835ae9f77f654a6694": {}, - "8459bc0c7e3759cdf591f513d9f141a95fef0a8f": {}, - "8530928916aaef40f59e6f221989ccb31f5759e7": {}, - "85e6279cec87321a52edac9c87bce653a07cf6c2": {}, - "8ade135a41bc03ea155e62e844d188df1ea18608": {}, - "8b5e8b768746b50394015010d25e690bfab9dfbc": {}, - "8e8c483db84b4bee98b60c0593521ed34d9990e8": {}, - "8eb1f6a495037164bea451156472f35fdd6bafc0": {}, - "8edcb1bdb4e267140fa742c62e395cd74f332709": {}, - "900f2210b1d28bbbd0bd22d17926b9e224e8f231": {}, - "97a652b80035363df47baee5031ec8670b8878ac": {}, - "9839dc14a02ddc6b6995e69eb3ecb98132fc8b6b": {}, - "9a9194f87191a7e9055e3e9b95b8cfb13023bb08": {}, - "9b4c13b0bfa31b4514c14f74b5a166c2708f43c6": {}, - "9bb56186c3b09b4f86b1c65136769dd318469633": {}, - "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0": {}, - "9c1e94e0ad997d618b6113a2171b055037589028": {}, - "9f265659d3bb64ab1440b03b12f4d47a24320917": {}, - "a5ac7e51b41094c92402da3b24376905380afc29": {}, - "aadec899646c8e0f34c52d9219c2faac36626b55": {}, - "b17fe1e4d59a9d1d95a7aead5e6fcd13e50939a5": {}, - "b1ec3021b8fa02164da82ca1557d017d83b0e179": {}, - "b32f140b0c872d58512e0a66172253c302617b90": {}, - "b4ffde65f46336ab88eb53be808477a3936bae11": {}, - "b6849436894e144dbce29d7d7fda2ae3bf9d8365": {}, - "b80ff79f1755d06ba70441c368a6fe801f5f3a62": {}, - "b9e0990d219a03df7633c93f6f005a8fecbcab22": {}, - "ba097532fb203f7e88c9c3c0b899b49469908a92": {}, - "c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5": {}, - "c533a0a4cfc4962971818edcfac47a2899e69799": {}, - "cbb722410c2e876e24abbe8de2cc27693e501dcb": {}, - "cd7d8d697e10461458bc61a30d094dc601a8b017": {}, - "d632683dd7b4114ad314bca15554477dd762a938": {}, - "d914b262ffc244530a203ab40decab34c3abf34d": {}, - "db0cee9a514becbbd4a101a5fbbbf47865ee316c": {}, - "dd960bd3c3f080561a1810e32349ac211ecec7d4": {}, - "de0fac2e4500dabe0009e67214ff5f5447ce83dd": {}, - "de5a000abf73b6f4965bd1bcdf8f8d94a56ea815": {}, - "df0bcddf6d6823307c716b56a7ef9c3b25078874": {}, - "df4cb1c069e1874edd31b4311f1884172cec0e10": {}, - "e3d2460bbb42d7710191569f88069044cfb9d8cf": {}, - "e8cb398be4a550817e382abf69e4c12c76fce1f2": {}, - "e8d4307400f9427dba7cb98e488d6ab85f1cec5f": {}, - "eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871": {}, - "f0282184c7ce73ab54c7e4ab5a617122602e575f": {}, - "f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a": {}, - "f9e715a95fcd1f9253f77dd28f11e88d2d6460c7": {}, - "fd084cde189b7b76ec305d52e27be545a0172823": {}, - "ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493": {}, -} diff --git a/internal/action/integration/checkout_profiles_generated.go b/internal/action/integration/checkout_profiles_generated.go new file mode 100644 index 00000000..d00598f8 --- /dev/null +++ b/internal/action/integration/checkout_profiles_generated.go @@ -0,0 +1,292 @@ +// Code generated by internal/action/integration/cmd/generate-checkout-profiles; DO NOT EDIT. + +package integration + +const checkoutMainSnapshotCommit = "f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a" + +var checkoutSnapshotTips = map[string]string{ + "main": "f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a", + "master": "61b9e3751b92087fd0b06925ba6dd6314e06f089", + "releases/v1": "0b496e91ec7ae4428c3ed2eeb4c3a40df431f2cc", + "releases/v2": "0717577d45739eb3c851188b29f50ed6c0b2194e", + "releases/v3": "a37ce9120846195fa4ece8f58b268e6043cb2f26", + "releases/v4": "11d5960a326750d5838078e36cf38b85af677262", + "releases/v5": "fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09", + "releases/v6": "d23441a48e516b6c34aea4fa41551a30e30af803", +} + +var ( + checkoutProfile01 = checkoutContract{inputs: "allow-unsafe-pr-checkout,clean,fetch-depth,fetch-tags,filter,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,show-progress,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,ssh-user,submodules,token", fullHistory: false, refOutput: true, commitOutput: true} + checkoutProfile02 = checkoutContract{inputs: "allow-unsafe-pr-checkout,clean,fetch-depth,fetch-tags,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile03 = checkoutContract{inputs: "allow-unsafe-pr-checkout,clean,fetch-depth,lfs,path,persist-credentials,ref,repository,set-safe-directory,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile04 = checkoutContract{inputs: "clean,fetch-depth,fetch-tags,filter,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,show-progress,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,ssh-user,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile05 = checkoutContract{inputs: "clean,fetch-depth,fetch-tags,filter,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,show-progress,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,ssh-user,submodules,token", fullHistory: false, refOutput: true, commitOutput: true} + checkoutProfile06 = checkoutContract{inputs: "clean,fetch-depth,fetch-tags,filter,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,show-progress,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile07 = checkoutContract{inputs: "clean,fetch-depth,fetch-tags,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,show-progress,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile08 = checkoutContract{inputs: "clean,fetch-depth,fetch-tags,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile09 = checkoutContract{inputs: "clean,fetch-depth,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,sparse-checkout,sparse-checkout-cone-mode,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile10 = checkoutContract{inputs: "clean,fetch-depth,github-server-url,lfs,path,persist-credentials,ref,repository,set-safe-directory,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile11 = checkoutContract{inputs: "clean,fetch-depth,lfs,path,persist-credentials,ref,repository,set-safe-directory,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile12 = checkoutContract{inputs: "clean,fetch-depth,lfs,path,persist-credentials,ref,repository,ssh-key,ssh-known-hosts,ssh-strict,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile13 = checkoutContract{inputs: "clean,fetch-depth,lfs,path,persist-credentials,ref,repository,submodules,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile14 = checkoutContract{inputs: "clean,fetch-depth,lfs,path,persist-credentials,ref,repository,token", fullHistory: false, refOutput: false, commitOutput: false} + checkoutProfile15 = checkoutContract{inputs: "clean,fetch-depth,lfs,path,ref,repository,submodules,token", fullHistory: true, refOutput: false, commitOutput: false} + checkoutProfile16 = checkoutContract{inputs: "clean,fetch-depth,lfs,path,ref,repository,token", fullHistory: false, refOutput: false, commitOutput: false} +) + +var checkoutCommitContracts = map[string]checkoutContract{ + "009b9ae9e446ad8d9b8c809870b0fbcc5e03573e": checkoutProfile05, + "00a3be89340a3ce8d704f82f44a5e7f9e3a84dfe": checkoutProfile12, + "01a434328acfaec94cbfc1cd07b3373e4693d132": checkoutProfile16, + "01aecccf739ca6ff86c0539fbc67a7a5007bbc81": checkoutProfile12, + "0299a0d2b67d48224ce047d03c69693b37fe77fe": checkoutProfile12, + "033fa0dc0b82693d8986f1016a0ec2c5e7d9cbb1": checkoutProfile05, + "06218e4404b044c23590a921fa858fb632a41afe": checkoutProfile14, + "064fe7f3312418007dea2b49a19844a9ee378f49": checkoutProfile05, + "069c6959146423d11cd0184e6accf28f9d45f06e": checkoutProfile05, + "0717577d45739eb3c851188b29f50ed6c0b2194e": checkoutProfile03, + "08c6903cd8c0fde910a37f88322edcfb5dd907a8": checkoutProfile05, + "08eba0b27e820071cde6df949e0beb9ba4906955": checkoutProfile05, + "090d9c9dfda6bb13508d978c6be93801de84f967": checkoutProfile14, + "0963d3b35f4826b619c2436e1bdce37c147daeaf": checkoutProfile15, + "096e9277500008410ac4dd98a7bb0d9052330de8": checkoutProfile14, + "09d2acae674a48949e3602304ab46fd20ae0c42f": checkoutProfile05, + "0ad4b8fadaa221de15dcec353f45205ec38ea70b": checkoutProfile04, + "0b496e91ec7ae4428c3ed2eeb4c3a40df431f2cc": checkoutProfile15, + "0c366fd6a839edf440554fa01a7085ccba70ac98": checkoutProfile05, + "0f9f3aa320cb53abeb534aeb54048075d9697a0e": checkoutProfile01, + "0ffe6f9c5599e73776da5b7f113e994bc0a76ede": checkoutProfile11, + "1044a6dea927916f2c38ba5aeffbc0a847b1221a": checkoutProfile01, + "11bd71901bbe5b1630ceea73d27597364c9af683": checkoutProfile05, + "11d5960a326750d5838078e36cf38b85af677262": checkoutProfile01, + "12cd2235efa0937479335606d7c3ac9f6c0973b1": checkoutProfile01, + "130a169078a413d3a5246a393625e8e742f387f6": checkoutProfile01, + "1433f62caac9c18949f9a498f6f28c05388ea443": checkoutProfile12, + "163217dfcd28294438ea1c1c149cfaf66eec283e": checkoutProfile05, + "1af3b93b6815bc44a9784bd300feb67ff0d1eeb3": checkoutProfile05, + "1cce3390c2bfda521930d01229c073c7ff920824": checkoutProfile05, + "1d96c772d19495a3b5c517cd2bc0cb401ea0529f": checkoutProfile04, + "1e204e9a9253d643386038d443f96446fa156a97": checkoutProfile12, + "1e31de5234b9f8995739874a8ce0492dc87873e2": checkoutProfile07, + "1f9a0c22da41e6ebfa534300ef656657ea2c6707": checkoutProfile10, + "2036a08e25fa78bbd946711a407b529a0a1204bf": checkoutProfile12, + "204620207c9669dc859681b83d09302c7deb97ce": checkoutProfile14, + "21dc310f1948a06cc989491cb1b4a86777f22918": checkoutProfile12, + "230611dbd0eb52da1e1f4f7bc8bb0c3a339fc8b7": checkoutProfile12, + "24cb9080177205b6e8c946b17badbe402adc938f": checkoutProfile10, + "24ed1a352802348c9e4e8d13de9177fb95b537ba": checkoutProfile04, + "2541b1294d2704b0964813337f33b291d3f8596b": checkoutProfile11, + "25a956c84d5dd820d28caab9f86b8d183aeeff3d": checkoutProfile12, + "262cdb5f1c2e469ea6a6810758c890b43e82bd2c": checkoutProfile03, + "2650dbd060003e3b5ae211e4358852f336b682a7": checkoutProfile06, + "26d48e8ea150211a9bc3b1f0c20448599687d926": checkoutProfile10, + "27135e314dd1818f797af1db9dae03a9f045786b": checkoutProfile10, + "28802689a136bfcdb721715abd713740beecbe07": checkoutProfile01, + "28c7f3d2b5162b5ddd3dfd9a45aa55eaf396478b": checkoutProfile12, + "299dd5064ede81803aeb1ce63fee4e150d9ae5f1": checkoutProfile14, + "2bd2911be9963da3ff84b7b09a28872059aa0564": checkoutProfile12, + "2d1c1198e79c30cca5c3957b1e3b65ce95b5356e": checkoutProfile12, + "2d7d9f7ff5b310f983d059b68785b3c74d8b8edd": checkoutProfile04, + "2ff2fbdea48a8f5da77a31e7dd5ecb46c017ffc3": checkoutProfile12, + "34e114876b0b11c390a56381ad16ebd13914f8d5": checkoutProfile05, + "3537747199ad29df25693bc607e99df5d7726ffd": checkoutProfile14, + "37b082107ba410260a3aaddf93122e04801ce631": checkoutProfile04, + "3b9b8c884f6b4bb4d5be2779c26374abadae0871": checkoutProfile05, + "3ba5ee6fac7e0e30e2ea884e236f282d3a775891": checkoutProfile10, + "3d3c42e5aac5ba805825da76410c181273ba90b1": checkoutProfile01, + "3d677ac575eac4b370e52131024fa99ee754def1": checkoutProfile12, + "3df4ab11eba7bda6032a0b82a6bb43b11571feac": checkoutProfile07, + "3df79e0276f4013ea6ed57534f3478cd2b1ef8c0": checkoutProfile15, + "3f603f6d5e9f40714f97b2f017aa0df2a443192a": checkoutProfile04, + "3fc17f8645e9648158a6d23b033ab5f62df29f3c": checkoutProfile12, + "40a16ebeed7da831425b665e600750cb36b38d06": checkoutProfile10, + "422dc4567157f4d62b665a8a288310365b1d194b": checkoutProfile13, + "43045ae669be728bd34ed56fcd1a230c0dc4d8e2": checkoutProfile04, + "442567ba5761652b13c5c842a2f959ac9da6be57": checkoutProfile12, + "44c2b7a8a4ea60a981eaca3cf939b5f4305c123b": checkoutProfile04, + "453ee27fca95fa9e03a24c1969a92c82e1a9b15e": checkoutProfile12, + "473055ba18d6d2da209cd46110aadb9275e3194e": checkoutProfile06, + "47fbe2df0ad0e27efb67a70beac3555f192b062f": checkoutProfile10, + "4817b449b0ed7c775a0bcecaa398041ab5d09b51": checkoutProfile16, + "4f1f4aec02e41874fa0262ea8ff5172d7978ad1e": checkoutProfile01, + "50fbc622fc4ef5163becd7fab6573eac35f8462e": checkoutProfile15, + "5126516654c75f76bca1de45dd82a3006d8890f9": checkoutProfile12, + "537c7ef99cef6e5ddb5e7ff5d16d14510503801d": checkoutProfile01, + "53bed0742eb3f0455187c7c7042d27f51b856f02": checkoutProfile15, + "556e4c3cb0b8b54b734286d5439adadcb0a8cb92": checkoutProfile06, + "56c00a7b1f53d3094df328ad4c2cd2b2d385c569": checkoutProfile12, + "574281d34cf49767d4b75b691c4c4f4655e0c93f": checkoutProfile12, + "58070a9fc3a91197fc9cbf24841ea31a2ab19980": checkoutProfile12, + "58246fdaeb114bc8d7e5ba1c0dd4504aa481f1c6": checkoutProfile02, + "5881116d181dc80f3ed5f395296a5579ee6fc6a4": checkoutProfile16, + "592cf69a223b04e75ddf345919130b91010eb2a6": checkoutProfile12, + "5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f": checkoutProfile12, + "5c3ccc22eb2c950a0fa5bc7c47190d8e3f7e681a": checkoutProfile10, + "5de26ee9b11f6ffa02406edecc0e750256af18b1": checkoutProfile01, + "6026fb2ad3aa556d78b517cf65c8c1f832075860": checkoutProfile01, + "61b9e3751b92087fd0b06925ba6dd6314e06f089": checkoutProfile12, + "61fd8fd0c7a28ab9f73c23c595edbd0550bf0e78": checkoutProfile14, + "62661c4e71a304b2823ed026347b8d34c3eac541": checkoutProfile01, + "631c7dc4f80f88219c5ee78fee08c6b62fac8da1": checkoutProfile05, + "631c942040754b6e095e929c1677c07e10ed4f87": checkoutProfile01, + "65865e15a14a3de9378a18a026ce6548b17a39ed": checkoutProfile12, + "689bf84be4a745196a5c809a6afb12708ee43c3c": checkoutProfile16, + "692973e3d937129bcbf40652eb9f2f61becf3332": checkoutProfile04, + "6a84743051be17cee477b0a26bd866b5dba996e4": checkoutProfile10, + "6b42224f41ee5dfe5395e27c8b2746f1f9955030": checkoutProfile05, + "6ccd57f4c5d15bdc2fef309bd9fb6cc9db2ef1c6": checkoutProfile04, + "6d193bf28034eafb982f37bd894289fe649468fc": checkoutProfile05, + "6e6328ef28ba9c951379a07c83913e2acc8bc9a0": checkoutProfile15, + "71cf2267d89c5cb81562390fa70a37fa40b1305e": checkoutProfile05, + "722adc63f1aa60a57ec37892e133b1d319cae598": checkoutProfile14, + "72f2cec99f417b1a1c5e2e88945068983b7965f9": checkoutProfile07, + "7523e237893f02412c876c5511929ce0c74c348d": checkoutProfile12, + "755da8c3cf115ac066823e79a1e1788f8940201b": checkoutProfile10, + "7739b9ba2efcda9dde65ad1e3c2dbe65b41dfba7": checkoutProfile08, + "77904fd4316d60fc138fe2b8286ca220f763853e": checkoutProfile14, + "7884fcad6b5d53d10323aee724dc68d8b9096a2e": checkoutProfile11, + "79102f25032f3f70eb9d8386b1451cf1ef55902d": checkoutProfile01, + "7990b10a0ca6be1ddd27e4d84e6dbbe788d86662": checkoutProfile16, + "7b187184d12a8f064f797aeb51e4873c109637c7": checkoutProfile10, + "7cdaf2fbc075e6f3b9ca94cfd6cec5adc8a75622": checkoutProfile06, + "7d09575332117a40b46e5e020664df234cd416f3": checkoutProfile01, + "7f00b66d06eed909da8e56729955e53d186d95ed": checkoutProfile12, + "7f0669ca1fd955c0e0fd85ee8d5b8d16978be97e": checkoutProfile15, + "80602fafba6e982172195b721791020f4f17a227": checkoutProfile13, + "8230315d06ad95c617244d2f265d237a1682d445": checkoutProfile10, + "826ba42d6c06e4d78b1b33478af7b54277e60b52": checkoutProfile12, + "83b7061638ee4956cf7545a6f7efe594e5ad0247": checkoutProfile10, + "8410ad0602e1e429cee44a835ae9f77f654a6694": checkoutProfile06, + "8459bc0c7e3759cdf591f513d9f141a95fef0a8f": checkoutProfile04, + "8461dbfed36a8af202384a5b1ee0f7f1bf947821": checkoutProfile16, + "8530928916aaef40f59e6f221989ccb31f5759e7": checkoutProfile06, + "85b1f35505da871133b65f059e96210c65650a8b": checkoutProfile12, + "85e47d1a2bef5be8023f6dce02e0e8451938924f": checkoutProfile12, + "85e6279cec87321a52edac9c87bce653a07cf6c2": checkoutProfile05, + "86f86b36ef15e6570752e7175f451a512eac206b": checkoutProfile12, + "885641592076c27bfb56c028cd5612cdad63e16d": checkoutProfile10, + "8ade135a41bc03ea155e62e844d188df1ea18608": checkoutProfile06, + "8b5e8b768746b50394015010d25e690bfab9dfbc": checkoutProfile07, + "8e5e7e5ab8b370d6c329ec480221332ada57f0ab": checkoutProfile10, + "8e8c483db84b4bee98b60c0593521ed34d9990e8": checkoutProfile05, + "8eb1f6a495037164bea451156472f35fdd6bafc0": checkoutProfile06, + "8edcb1bdb4e267140fa742c62e395cd74f332709": checkoutProfile05, + "8f4b7f84864484a7bf31766abe9204da3cbe65b3": checkoutProfile10, + "8f9e05e482293f862823fcca12d9eddfb3723131": checkoutProfile12, + "900f2210b1d28bbbd0bd22d17926b9e224e8f231": checkoutProfile05, + "93cb6efe18208431cddfb8368fd83d5badbf9bfd": checkoutProfile05, + "93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8": checkoutProfile10, + "94c2de77cccf605d74201a8aec6dd8fc0717ad66": checkoutProfile12, + "94d077c24971944d312dd9197c1bdfba62b39878": checkoutProfile15, + "96f53100ba2a5449eb71d2e6604bbcd94b9449b5": checkoutProfile09, + "97a652b80035363df47baee5031ec8670b8878ac": checkoutProfile08, + "97b30c411cc8e273e8f90d632b8e53d2604a90ca": checkoutProfile12, + "9839dc14a02ddc6b6995e69eb3ecb98132fc8b6b": checkoutProfile04, + "9a3a9ade8222dcdf9d3c77710b10df47ee7c7c89": checkoutProfile12, + "9a9194f87191a7e9055e3e9b95b8cfb13023bb08": checkoutProfile04, + "9b4c13b0bfa31b4514c14f74b5a166c2708f43c6": checkoutProfile04, + "9bb56186c3b09b4f86b1c65136769dd318469633": checkoutProfile06, + "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0": checkoutProfile01, + "9c1e94e0ad997d618b6113a2171b055037589028": checkoutProfile04, + "9f265659d3bb64ab1440b03b12f4d47a24320917": checkoutProfile05, + "a12a3943b4bdde767164f792f33f40b04645d846": checkoutProfile12, + "a14471d838f6a7ce15cab8740f25e337c51e7cad": checkoutProfile15, + "a37ce9120846195fa4ece8f58b268e6043cb2f26": checkoutProfile02, + "a4b69b48862e969425d8dc115dcb965f288ea29b": checkoutProfile12, + "a572f640b07e96fc5837b3adfa0e5a2ddd8dae21": checkoutProfile16, + "a5ac7e51b41094c92402da3b24376905380afc29": checkoutProfile04, + "a6747255bd19d7a757dbdda8c654a9f84db19839": checkoutProfile14, + "a81bbbf8298c0fa03ea29cdc473d45769f953675": checkoutProfile12, + "aabbfeb2ce60b5bd82389903509092c4648a9713": checkoutProfile12, + "aadec899646c8e0f34c52d9219c2faac36626b55": checkoutProfile06, + "ac455590d1debd05854e62f352cf7a59e27328bc": checkoutProfile12, + "ac593985615ec2ede58e132d2e21d2b1cbd6127c": checkoutProfile10, + "add3486cc3b55d4a5e11c8045058cef96538edc7": checkoutProfile12, + "ae525b22625099736a2909d0eb22ec50cbe398fc": checkoutProfile14, + "af513c7a016048ae468971c52ed77d9562c7c819": checkoutProfile15, + "afe4af09a72596f47d806ee5f8b2674ec07fdc73": checkoutProfile12, + "b17fe1e4d59a9d1d95a7aead5e6fcd13e50939a5": checkoutProfile04, + "b1ec3021b8fa02164da82ca1557d017d83b0e179": checkoutProfile04, + "b2e6b7ed13bcde9d37c9e3e6967cd3ecfd2807ad": checkoutProfile12, + "b2eb13baee0ef6ef21737c8cf4a6a32f4e002442": checkoutProfile11, + "b32f140b0c872d58512e0a66172253c302617b90": checkoutProfile06, + "b4483adec309c0d01a5435c5e24eb40de5773ad9": checkoutProfile12, + "b4626ce19ce1106186ddf9bb20e706842f11a7c3": checkoutProfile13, + "b4b537b06a577732e04b29acc7294f645c135da0": checkoutProfile15, + "b4ffde65f46336ab88eb53be808477a3936bae11": checkoutProfile06, + "b6849436894e144dbce29d7d7fda2ae3bf9d8365": checkoutProfile05, + "b80ff79f1755d06ba70441c368a6fe801f5f3a62": checkoutProfile04, + "b9e0990d219a03df7633c93f6f005a8fecbcab22": checkoutProfile01, + "ba097532fb203f7e88c9c3c0b899b49469908a92": checkoutProfile01, + "bc50a995b88ec9334cb2f3b1c49502d9834ed2c5": checkoutProfile14, + "be0f44845645e415725af198163a96fea9e54334": checkoutProfile12, + "be6c44d969b1b004a9e0f7853e9cc9977ea0f7f0": checkoutProfile12, + "bf085276cecdb0cc76fbbe0687a5a0e786646936": checkoutProfile10, + "bf4af63534d79cb0712d8c86d8f2404844fa5a79": checkoutProfile15, + "c170eefc2657d93cc91397be50a299bff978a052": checkoutProfile14, + "c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5": checkoutProfile05, + "c49af7ca1f339b07a5baac8c8bfc49a5248f31d3": checkoutProfile12, + "c533a0a4cfc4962971818edcfac47a2899e69799": checkoutProfile06, + "c85684db76ba6ef08713c10cf4befe3318887415": checkoutProfile14, + "c85c95e3d7251135ab7dc9ce3241c5835cc595a9": checkoutProfile09, + "c915c33a16f01166c17c4e35fe1d4085a2d71adb": checkoutProfile01, + "c952173edf28a2bd22e1a4926590c1ac39630461": checkoutProfile12, + "cab31617d857bf9e70dc35fd9e4dafe350794082": checkoutProfile16, + "cacfc4155de4db33162bcb6c82700751fda6bd91": checkoutProfile15, + "cbb722410c2e876e24abbe8de2cc27693e501dcb": checkoutProfile05, + "cc70598ce853d5d678b2440190cb18368e5cee8c": checkoutProfile16, + "cd6a9fd49371476d813e892956e2e920fcc3fb7e": checkoutProfile10, + "cd7d8d697e10461458bc61a30d094dc601a8b017": checkoutProfile06, + "d106d4669b3bfcb17f11f83f98e1cab478e9f635": checkoutProfile09, + "d23441a48e516b6c34aea4fa41551a30e30af803": checkoutProfile01, + "d50f8ea76748df49594d9b109b614f3b4db63c71": checkoutProfile12, + "d632683dd7b4114ad314bca15554477dd762a938": checkoutProfile05, + "d914b262ffc244530a203ab40decab34c3abf34d": checkoutProfile01, + "dac8cc78a1c612c854c383833b39e5a3f357b1f5": checkoutProfile12, + "db0cee9a514becbbd4a101a5fbbbf47865ee316c": checkoutProfile05, + "db41740e12847bb616a339b75eb9414e711417df": checkoutProfile14, + "dc323e67f16fb5f7663d20ff7941f27f5809e9b6": checkoutProfile11, + "dcd71f646680f2efd8db4afa5ad64fdcba30e748": checkoutProfile12, + "dd960bd3c3f080561a1810e32349ac211ecec7d4": checkoutProfile04, + "de0fac2e4500dabe0009e67214ff5f5447ce83dd": checkoutProfile05, + "de5a000abf73b6f4965bd1bcdf8f8d94a56ea815": checkoutProfile05, + "df0bcddf6d6823307c716b56a7ef9c3b25078874": checkoutProfile06, + "df4cb1c069e1874edd31b4311f1884172cec0e10": checkoutProfile05, + "df86c829ebbc4e80aa9885a4762d84e11e0eeacb": checkoutProfile12, + "dfd70d4a2dece5f4a1af8dc99e1508a16e916b60": checkoutProfile14, + "e2f20e631ae6d7dd3b768f56a5d2af784dd54791": checkoutProfile11, + "e347bba93bdcadab0b55e4b333254f9bb40bdb0c": checkoutProfile16, + "e3bc06d98631ce7e0e3db6bd158fafe028709e9f": checkoutProfile12, + "e3d2460bbb42d7710191569f88069044cfb9d8cf": checkoutProfile05, + "e52d022eb52c224e5f2201beb687a66849e3b200": checkoutProfile12, + "e6d535c99c374d0c3f6d8cd8086a57b43c6c700a": checkoutProfile10, + "e8bd1dffb6451bb0d84dbcd3ed059daca1371180": checkoutProfile15, + "e8cb398be4a550817e382abf69e4c12c76fce1f2": checkoutProfile01, + "e8d4307400f9427dba7cb98e488d6ab85f1cec5f": checkoutProfile01, + "eb35239ec22e9029a5be28f8c41e67452f615f0f": checkoutProfile10, + "eb8a193c1dbf4bbb2053320cef52bacc1a485839": checkoutProfile12, + "ec3a7ce113134d7a93b817d10a8272cb61118579": checkoutProfile12, + "eccf386318b560bdd401913a9fe3cca56dc369d6": checkoutProfile12, + "ee0669bd1cc54295c223e0bb666b733df41de1c5": checkoutProfile11, + "eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871": checkoutProfile05, + "f0282184c7ce73ab54c7e4ab5a617122602e575f": checkoutProfile01, + "f095bcc56b7c2baf48f3ac70d6d6782f4f553222": checkoutProfile10, + "f2190623701cfebaaf26554b39e1e29cc4a1f2fb": checkoutProfile14, + "f25a3a9f25bd5f4c5d77189cab02ff357b5aedeb": checkoutProfile12, + "f43a0e5ff2bd294095638e18286ca9a3d1956744": checkoutProfile08, + "f466b96953a8e78166c9b97a44a70220f1a3e77e": checkoutProfile14, + "f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a": checkoutProfile01, + "f67ee5d6224ccd5af1909d53ea1faa5efb91db29": checkoutProfile11, + "f6ce2afa7079cb075a124c93c79d61779d845782": checkoutProfile15, + "f858c22e963bc60bc9d01c3d105c52a45a7deb6e": checkoutProfile14, + "f90c7b395dac7c5a277c1a6d93d5057c1cddb74e": checkoutProfile14, + "f93ca50bde35f63a65c67fc94cc8821ead20e631": checkoutProfile01, + "f95f2a38561736d1542cb9fbf736eea3d00ab5a6": checkoutProfile14, + "f9e715a95fcd1f9253f77dd28f11e88d2d6460c7": checkoutProfile01, + "fb6f360df236bd2026c7963cf88c8ddf20b4f0e2": checkoutProfile12, + "fbb30c60ab3f94a2c03755a7fb875120c137bef7": checkoutProfile12, + "fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09": checkoutProfile01, + "fd084cde189b7b76ec305d52e27be545a0172823": checkoutProfile04, + "fd47087372161c6f2a7b96d2ef87e944d89023ed": checkoutProfile12, + "ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493": checkoutProfile05, +} diff --git a/internal/action/integration/checkout_test.go b/internal/action/integration/checkout_test.go index 7d5c70a6..5e7dbc8f 100644 --- a/internal/action/integration/checkout_test.go +++ b/internal/action/integration/checkout_test.go @@ -6,24 +6,55 @@ import ( ) func TestCheckoutCommitAdmission(t *testing.T) { - for _, commit := range []string{CheckoutV1Commit, CheckoutV2Commit, CheckoutV3Commit, CheckoutV4Commit, CheckoutV5Commit, CheckoutV6Commit, CheckoutV7InitialCommit, CheckoutV7Commit} { + for _, commit := range []string{ + CheckoutV1Commit, CheckoutV2Commit, CheckoutV3Commit, CheckoutV4Commit, + CheckoutV5Commit, CheckoutV6Commit, CheckoutV7InitialCommit, CheckoutV7Commit, + "af513c7a016048ae468971c52ed77d9562c7c819", // v1.0.0 + "722adc63f1aa60a57ec37892e133b1d319cae598", // v2.0.0 + "a12a3943b4bdde767164f792f33f40b04645d846", // v3.0.0 + "f43a0e5ff2bd294095638e18286ca9a3d1956744", // v3.6.0 + "1e31de5234b9f8995739874a8ce0492dc87873e2", // v4.0.0 + } { if err := validateCheckoutCommit(commit); err != nil { - t.Fatalf("audited commit %s rejected: %v", commit, err) + t.Fatalf("snapshotted commit %s rejected: %v", commit, err) } } if err := validateCheckoutCommit("de0fac2e4500dabe0009e67214ff5f5447ce83dd"); err != nil { t.Fatalf("upstream main commit rejected: %v", err) } - for _, commit := range []string{ - "f43a0e5ff2bd294095638e18286ca9a3d1956744", // v3.6.0 is an ancestor of upstream main. - strings.Repeat("0", 40), - } { + for _, commit := range []string{strings.Repeat("0", 40)} { if err := validateCheckoutCommit(commit); err == nil || !strings.Contains(err.Error(), "does not admit") || !strings.Contains(err.Error(), CheckoutV7Commit) || !strings.Contains(err.Error(), checkoutMainSnapshotCommit) { t.Fatalf("unrecognized checkout commit %s error = %v", commit, err) } } } +func TestCheckoutSnapshotContracts(t *testing.T) { + if len(checkoutCommitContracts) < 254 { + t.Fatalf("snapshotted checkout commits = %d, want at least 254", len(checkoutCommitContracts)) + } + for branch, tip := range checkoutSnapshotTips { + if _, ok := checkoutCommitContracts[tip]; !ok { + t.Errorf("snapshot branch %s tip %s has no admitted contract", branch, tip) + } + } + for commit, contract := range checkoutCommitContracts { + if contract.inputs == "" { + t.Errorf("snapshotted commit %s has no declared inputs", commit) + } + names := strings.Split(contract.inputs, ",") + for i, name := range names { + if name != strings.ToLower(name) || i > 0 && names[i-1] >= name { + t.Errorf("snapshotted commit %s has noncanonical inputs %q", commit, contract.inputs) + break + } + } + if contract.refOutput != contract.commitOutput { + t.Errorf("snapshotted commit %s declares only one checkout output", commit) + } + } +} + func TestValidateCheckoutInputs(t *testing.T) { repository, sha := "buildkite/buildkite-gha", strings.Repeat("a", 40) for _, inputs := range []map[string]string{ @@ -137,3 +168,32 @@ func TestValidateCheckoutLegacyInputsRejectLaterContracts(t *testing.T) { }) } } + +func TestValidateCheckoutInputsUsesPerCommitContract(t *testing.T) { + repository, sha := "buildkite/buildkite-gha", strings.Repeat("a", 40) + for _, test := range []struct { + name, commit, input string + wantAccepted bool + }{ + {name: "v2.0 before submodules", commit: "722adc63f1aa60a57ec37892e133b1d319cae598", input: "submodules"}, + {name: "v2.3.4 with submodules", commit: "5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f", input: "submodules", wantAccepted: true}, + {name: "v3.0 before fetch-tags", commit: "a12a3943b4bdde767164f792f33f40b04645d846", input: "fetch-tags"}, + {name: "v3.7 with fetch-tags", commit: CheckoutV3Commit, input: "fetch-tags", wantAccepted: true}, + {name: "v4.0 before filter", commit: "1e31de5234b9f8995739874a8ce0492dc87873e2", input: "filter"}, + {name: "v4.2 with filter", commit: "d632683dd7b4114ad314bca15554477dd762a938", input: "filter", wantAccepted: true}, + } { + t.Run(test.name, func(t *testing.T) { + value := "false" + if test.input == "filter" { + value = "" + } + err := ValidateCheckoutInputs(test.commit, map[string]string{test.input: value}, repository, sha) + if test.wantAccepted && err != nil { + t.Fatalf("ValidateCheckoutInputs() = %v", err) + } + if !test.wantAccepted && (err == nil || !strings.Contains(err.Error(), "unsupported by this actions/checkout release")) { + t.Fatalf("ValidateCheckoutInputs() = %v, want contract rejection", err) + } + }) + } +} diff --git a/internal/action/integration/cmd/generate-checkout-profiles/main.go b/internal/action/integration/cmd/generate-checkout-profiles/main.go new file mode 100644 index 00000000..4b9b1b13 --- /dev/null +++ b/internal/action/integration/cmd/generate-checkout-profiles/main.go @@ -0,0 +1,175 @@ +package main + +import ( + "bytes" + "fmt" + "go/format" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" + + "go.yaml.in/yaml/v4" +) + +const repository = "https://github.com/actions/checkout.git" + +var snapshotBranches = []string{"main", "master", "releases/v1", "releases/v2", "releases/v3", "releases/v4", "releases/v5", "releases/v6"} + +type actionManifest struct { + Inputs map[string]yaml.Node `yaml:"inputs"` + Outputs map[string]yaml.Node `yaml:"outputs"` + Runs struct { + Plugin string `yaml:"plugin"` + } `yaml:"runs"` +} + +type contract struct { + Inputs string + FullHistory bool + RefOutput bool + CommitOutput bool +} + +func main() { + output := "checkout_profiles_generated.go" + if len(os.Args) > 2 { + fatalf("usage: go run ./cmd/generate-checkout-profiles [output]") + } + if len(os.Args) == 2 { + output = os.Args[1] + } + + checkout, err := os.MkdirTemp("", "buildkite-gha-checkout-profiles-") + if err != nil { + fatalf("create temporary checkout: %v", err) + } + defer func() { _ = os.RemoveAll(checkout) }() + run(checkout, "git", "init", "--quiet") + run(checkout, "git", "remote", "add", "origin", repository) + refspecs := []string{"--quiet", "--no-tags", "origin", "+refs/tags/*:refs/tags/*"} + for _, branch := range snapshotBranches { + refspecs = append(refspecs, "+refs/heads/"+branch+":refs/remotes/origin/"+branch) + } + run(checkout, "git", append([]string{"fetch"}, refspecs...)...) + + tips := make(map[string]string, len(snapshotBranches)) + for _, branch := range snapshotBranches { + tips[branch] = gitOutput(checkout, "rev-parse", "refs/remotes/origin/"+branch+"^{commit}") + } + commits := strings.Fields(gitOutput(checkout, "rev-list", "--all")) + contracts := make(map[string]contract) + for _, commit := range commits { + source, err := exec.Command("git", "-C", checkout, "show", commit+":action.yml").Output() + if err != nil { + source, err = exec.Command("git", "-C", checkout, "show", commit+":action.yaml").Output() + } + if err != nil { + continue + } + var manifest actionManifest + if yaml.Unmarshal(source, &manifest) != nil { + continue + } + outputs := sortedKeys(manifest.Outputs) + if len(outputs) != 0 && (len(outputs) != 2 || outputs[0] != "commit" || outputs[1] != "ref") { + continue + } + inputs := sortedKeys(manifest.Inputs) + contracts[commit] = contract{ + Inputs: strings.Join(inputs, ","), + FullHistory: manifest.Runs.Plugin != "", + RefOutput: contains(outputs, "ref"), + CommitOutput: contains(outputs, "commit"), + } + } + + profilesByContract := map[contract]string{} + uniqueContracts := make([]contract, 0) + for _, profile := range contracts { + if _, ok := profilesByContract[profile]; ok { + continue + } + uniqueContracts = append(uniqueContracts, profile) + profilesByContract[profile] = "" + } + sort.Slice(uniqueContracts, func(i, j int) bool { + return contractKey(uniqueContracts[i]) < contractKey(uniqueContracts[j]) + }) + for i, profile := range uniqueContracts { + profilesByContract[profile] = fmt.Sprintf("checkoutProfile%02d", i+1) + } + + var generated bytes.Buffer + fmt.Fprintln(&generated, "// Code generated by internal/action/integration/cmd/generate-checkout-profiles; DO NOT EDIT.") + fmt.Fprintln(&generated, "\npackage integration") + fmt.Fprintf(&generated, "\nconst checkoutMainSnapshotCommit = %q\n", tips["main"]) + fmt.Fprintln(&generated, "\nvar checkoutSnapshotTips = map[string]string{") + for _, branch := range snapshotBranches { + fmt.Fprintf(&generated, "\t%q: %q,\n", branch, tips[branch]) + } + fmt.Fprintln(&generated, "}") + fmt.Fprintln(&generated, "\nvar (") + for _, profile := range uniqueContracts { + fmt.Fprintf(&generated, "\t%s = checkoutContract{inputs: %q, fullHistory: %t, refOutput: %t, commitOutput: %t}\n", + profilesByContract[profile], profile.Inputs, profile.FullHistory, profile.RefOutput, profile.CommitOutput) + } + fmt.Fprintln(&generated, ")") + fmt.Fprintln(&generated, "\nvar checkoutCommitContracts = map[string]checkoutContract{") + commitKeys := sortedKeys(contracts) + for _, commit := range commitKeys { + fmt.Fprintf(&generated, "\t%q: %s,\n", commit, profilesByContract[contracts[commit]]) + } + fmt.Fprintln(&generated, "}") + + formatted, err := format.Source(generated.Bytes()) + if err != nil { + fatalf("format generated source: %v", err) + } + if err := os.WriteFile(filepath.Clean(output), formatted, 0o644); err != nil { + fatalf("write generated source: %v", err) + } +} + +func sortedKeys[T any](values map[string]T) []string { + keys := make([]string, 0, len(values)) + for key := range values { + keys = append(keys, strings.ToLower(key)) + } + sort.Strings(keys) + return keys +} + +func contains(values []string, target string) bool { + index := sort.SearchStrings(values, target) + return index < len(values) && values[index] == target +} + +func contractKey(profile contract) string { + return fmt.Sprintf("%s\x00%t\x00%t\x00%t", profile.Inputs, profile.FullHistory, profile.RefOutput, profile.CommitOutput) +} + +func gitOutput(directory string, args ...string) string { + command := exec.Command("git", append([]string{"-C", directory}, args...)...) + output, err := command.Output() + if err != nil { + fatalf("git %s: %v", strings.Join(args, " "), err) + } + return strings.TrimSpace(string(output)) +} + +func run(directory, name string, args ...string) { + command := exec.Command(name, args...) + command.Dir = directory + command.Stdout = os.Stdout + command.Stderr = os.Stderr + if err := command.Run(); err != nil { + fatalf("%s %s: %v", name, strings.Join(args, " "), err) + } +} + +func fatalf(format string, args ...any) { + fmt.Fprintf(os.Stderr, format+"\n", args...) + os.Exit(1) +} diff --git a/internal/compiler/actions_test.go b/internal/compiler/actions_test.go index 0ffff7a0..0981ff58 100644 --- a/internal/compiler/actions_test.go +++ b/internal/compiler/actions_test.go @@ -1270,9 +1270,14 @@ func TestCheckoutAdapterInputBoundary(t *testing.T) { func TestCheckoutAdapterCommitBoundary(t *testing.T) { workspace, remote := t.TempDir(), t.TempDir() for version, commit := range map[string]string{ + "v1.0.0": "af513c7a016048ae468971c52ed77d9562c7c819", "v1.2.0": actionintegration.CheckoutV1Commit, + "v2.0.0": "722adc63f1aa60a57ec37892e133b1d319cae598", "v2.8.0": actionintegration.CheckoutV2Commit, + "v3.0.0": "a12a3943b4bdde767164f792f33f40b04645d846", + "v3.6.0": "f43a0e5ff2bd294095638e18286ca9a3d1956744", "v3.7.0": actionintegration.CheckoutV3Commit, + "v4.0.0": "1e31de5234b9f8995739874a8ce0492dc87873e2", "v4": actionintegration.CheckoutV4Commit, "v5": actionintegration.CheckoutV5Commit, "v6": actionintegration.CheckoutV6Commit, diff --git a/internal/runtime/checkout_test.go b/internal/runtime/checkout_test.go index 88e7b222..c381f028 100644 --- a/internal/runtime/checkout_test.go +++ b/internal/runtime/checkout_test.go @@ -378,7 +378,10 @@ func TestCheckoutOutputsMatchReleaseContract(t *testing.T) { {name: "v1.2.0", commit: actionintegration.CheckoutV1Commit, want: map[string]string{}}, {name: "v2.8.0", commit: actionintegration.CheckoutV2Commit, want: map[string]string{}}, {name: "v3.7.0", commit: actionintegration.CheckoutV3Commit, want: map[string]string{}}, - {name: "v4 and later", commit: actionintegration.CheckoutV4Commit, want: map[string]string{"ref": "refs/heads/main", "commit": strings.Repeat("a", 40)}}, + {name: "v4.0.0 before outputs", commit: "1e31de5234b9f8995739874a8ce0492dc87873e2", want: map[string]string{}}, + {name: "v4.1.7 before outputs", commit: "692973e3d937129bcbf40652eb9f2f61becf3332", want: map[string]string{}}, + {name: "v4.2.0 with outputs", commit: "d632683dd7b4114ad314bca15554477dd762a938", want: map[string]string{"ref": "refs/heads/main", "commit": strings.Repeat("a", 40)}}, + {name: "current v4", commit: actionintegration.CheckoutV4Commit, want: map[string]string{"ref": "refs/heads/main", "commit": strings.Repeat("a", 40)}}, } { t.Run(test.name, func(t *testing.T) { outputs := map[string]string{} @@ -397,6 +400,8 @@ func TestCheckoutInputsWithReleaseDefaults(t *testing.T) { inputs map[string]string want map[string]string }{ + {name: "v1.0 defaults to full history", commit: "af513c7a016048ae468971c52ed77d9562c7c819", inputs: nil, want: map[string]string{"fetch-depth": "0"}}, + {name: "v1.1 defaults to full history", commit: "0b496e91ec7ae4428c3ed2eeb4c3a40df431f2cc", inputs: nil, want: map[string]string{"fetch-depth": "0"}}, {name: "v1 defaults to full history", commit: actionintegration.CheckoutV1Commit, inputs: nil, want: map[string]string{"fetch-depth": "0"}}, {name: "v1 keeps explicit depth", commit: actionintegration.CheckoutV1Commit, inputs: map[string]string{"Fetch-Depth": "5"}, want: map[string]string{"Fetch-Depth": "5"}}, {name: "v2 keeps shallow default", commit: actionintegration.CheckoutV2Commit, inputs: nil, want: nil}, diff --git a/mise.toml b/mise.toml index 141f46eb..4a4dd1d5 100644 --- a/mise.toml +++ b/mise.toml @@ -35,7 +35,7 @@ run = "echo '--- Lint Go'; golangci-lint run" [tasks."lint:shell"] description = "Lint shell scripts" -run = "echo '--- Lint shell'; shellcheck -x .buildkite/upload-examples.sh scripts/ci-buildkite-release scripts/compare-example scripts/compatibility-gaps scripts/starter-workflows-corpus scripts/update-checkout-main-commits scripts/validate-public-workflow-corpus scripts/verify-source-checkout scripts/hosted-docker-probe scripts/container-runtime-probe scripts/verify-summary-annotation scripts/verify-workflow-annotations scripts/verify-upload-artifact scripts/verify-artifact-roundtrip scripts/release scripts/release-test scripts/smoke-local testdata/actions/docker/entrypoint.sh testdata/container-runtime/.github/actions/docker/entrypoint.sh" +run = "echo '--- Lint shell'; shellcheck -x .buildkite/upload-examples.sh scripts/ci-buildkite-release scripts/compare-example scripts/compatibility-gaps scripts/starter-workflows-corpus scripts/validate-public-workflow-corpus scripts/verify-source-checkout scripts/hosted-docker-probe scripts/container-runtime-probe scripts/verify-summary-annotation scripts/verify-workflow-annotations scripts/verify-upload-artifact scripts/verify-artifact-roundtrip scripts/release scripts/release-test scripts/smoke-local testdata/actions/docker/entrypoint.sh testdata/container-runtime/.github/actions/docker/entrypoint.sh" [tasks.lint] description = "Lint Go code and shell scripts" diff --git a/scripts/update-checkout-main-commits b/scripts/update-checkout-main-commits deleted file mode 100755 index 79b93a6f..00000000 --- a/scripts/update-checkout-main-commits +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repository=https://github.com/actions/checkout.git -v4_boundary_commit=f43a0e5ff2bd294095638e18286ca9a3d1956744 -requested_commit=${1:-} -output=${2:-checkout_main_commits_generated.go} -checkout=$(mktemp -d) -trap 'rm -rf "$checkout"' EXIT - -git -C "$checkout" init --quiet -git -C "$checkout" remote add origin "$repository" -git -C "$checkout" fetch --quiet --filter=blob:none origin main -main_commit=$(git -C "$checkout" rev-parse FETCH_HEAD) - -if [[ -n "$requested_commit" ]] && ! git -C "$checkout" cat-file -e "$requested_commit^{commit}" 2>/dev/null; then - git -C "$checkout" fetch --quiet --filter=blob:none origin "$requested_commit" -fi -snapshot_commit=$(git -C "$checkout" rev-parse "${requested_commit:-$main_commit}^{commit}") -if ! git -C "$checkout" merge-base --is-ancestor "$snapshot_commit" "$main_commit"; then - echo "$snapshot_commit is not reachable from actions/checkout upstream main" >&2 - exit 1 -fi -if ! git -C "$checkout" merge-base --is-ancestor "$v4_boundary_commit" "$snapshot_commit"; then - echo "$snapshot_commit predates the actions/checkout v4 development boundary" >&2 - exit 1 -fi - -{ - cat <"$output" - -gofmt -w "$output"