@patronage/factory-ci 1.0.0-alpha.21 → 1.0.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -1
- package/dist/index.d.ts +196 -6
- package/dist/index.js +303 -90
- package/package.json +1 -1
- package/src/index.ts +17 -0
- package/src/merge-freeze-job.ts +230 -0
- package/src/pr-status-hud-workflow.ts +6 -8
- package/src/preview-proof-inventory.ts +20 -2
- package/src/proof-reuse-gate.ts +148 -81
- package/src/push-identity-workflow.ts +11 -10
package/dist/index.js
CHANGED
|
@@ -519,6 +519,22 @@ const FACTORY_PROOF_GATE_GUARD = `steps.${FACTORY_PROOF_GATE_STEP_ID}.outputs.${
|
|
|
519
519
|
*/
|
|
520
520
|
const FACTORY_PROOF_GATE_IF = "github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))";
|
|
521
521
|
/**
|
|
522
|
+
* Pull requests only — the condition a consumer gets with
|
|
523
|
+
* `reuse: "pull-request"` (#873).
|
|
524
|
+
*
|
|
525
|
+
* A repository may want its default-branch pushes to always execute the full
|
|
526
|
+
* hosted suite, whatever proof exists: the merged commit is what the fleet
|
|
527
|
+
* deploys, so a periodic unconditional run of every command is a deliberate
|
|
528
|
+
* cost some consumers choose to pay. This condition is that choice, expressed
|
|
529
|
+
* once here rather than as a consumer-written override string. The mode also
|
|
530
|
+
* skips the push-event merge fallback, so no push path can reuse proof even if
|
|
531
|
+
* the workflow reaches the step through some other trigger.
|
|
532
|
+
*
|
|
533
|
+
* Which branches trigger the workflow at all stays repository-owned. This
|
|
534
|
+
* condition only keeps the gate from consulting proof outside a pull request.
|
|
535
|
+
*/
|
|
536
|
+
const FACTORY_PROOF_GATE_PULL_REQUEST_IF = "github.event_name == 'pull_request'";
|
|
537
|
+
/**
|
|
522
538
|
* The complete refusal vocabulary. Deliberately few, because these are the
|
|
523
539
|
* only distinctions the gate can honestly make from its Checks API reads.
|
|
524
540
|
* The merge fallback adds no words: a fallback that establishes nothing
|
|
@@ -814,7 +830,92 @@ const safeLabel$1 = (surface) => {
|
|
|
814
830
|
const cleaned = (typeof surface === "string" ? surface : "").replaceAll(/[^\w -]/gu, "").trim().slice(0, 60);
|
|
815
831
|
return cleaned.length > 0 ? cleaned : "verification";
|
|
816
832
|
};
|
|
817
|
-
|
|
833
|
+
/**
|
|
834
|
+
* The push-event merge fallback (#611), emitted only for the default reuse
|
|
835
|
+
* mode. `reuse: "pull-request"` omits it: that mode's whole point is that a
|
|
836
|
+
* default-branch push executes everything, so a push path that could still
|
|
837
|
+
* reuse proof would contradict the condition above it.
|
|
838
|
+
*/
|
|
839
|
+
const MERGE_FALLBACK_BLOCK = String.raw`# Merge fallback (#611). A squash merge mints a new commit, so the direct
|
|
840
|
+
# read at a pushed merge-target head finds nothing even when the factory
|
|
841
|
+
# proved the producing pull request head. Only when the direct read found no
|
|
842
|
+
# generation at all on a push event, look up the producing pull request and
|
|
843
|
+
# reuse its head proof — and only when the merge commit's TREE id equals the
|
|
844
|
+
# proven head's tree id, which makes the pushed content byte-identical to
|
|
845
|
+
# what was verified (a clean squash of an unchanged tip). Patch identity was
|
|
846
|
+
# considered and rejected as the comparator: git patch-id normalizes
|
|
847
|
+
# whitespace and ignores base motion, so an identical patch can still
|
|
848
|
+
# produce an integrated tree that was never tested. Everything else — no
|
|
849
|
+
# unique merged producing PR, an unreadable commit, a proof that is anything
|
|
850
|
+
# but proven, or tree drift from a dirty or stale merge — leaves the direct
|
|
851
|
+
# "none" refusal standing, so the full suite runs (fail open).
|
|
852
|
+
if [ "$reason" = 'none' ] && [ "${shellExpansion$1("GITHUB_EVENT_NAME:-")}" = 'push' ]; then
|
|
853
|
+
producing_head=''
|
|
854
|
+
merge_tree=''
|
|
855
|
+
head_tree=''
|
|
856
|
+
if ! producing_pulls=$(gh api --method GET --paginate \
|
|
857
|
+
"repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/pulls" \
|
|
858
|
+
-f per_page=100 2>&1); then
|
|
859
|
+
detail="producing pull request unreadable: $producing_pulls"
|
|
860
|
+
elif ! producing_head=$(printf '%s' "$producing_pulls" | jq -r -s \
|
|
861
|
+
--arg sha "$HEAD_SHA" \
|
|
862
|
+
'[ add[]?
|
|
863
|
+
| select((.merged_at // null) != null)
|
|
864
|
+
| select((.merge_commit_sha // "") == $sha)
|
|
865
|
+
| ((.head.sha // "") | tostring) ]
|
|
866
|
+
| if length == 1 then .[0] else "" end' 2>&1); then
|
|
867
|
+
detail="producing pull request unreadable: $producing_head"
|
|
868
|
+
producing_head=''
|
|
869
|
+
elif ! [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]]; then
|
|
870
|
+
detail='no single merged producing pull request at this commit'
|
|
871
|
+
producing_head=''
|
|
872
|
+
elif ! merge_commit=$(gh api --method GET \
|
|
873
|
+
"repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA" 2>&1); then
|
|
874
|
+
detail="merge commit unreadable: $merge_commit"
|
|
875
|
+
elif ! merge_tree=$(printf '%s' "$merge_commit" | jq -r \
|
|
876
|
+
'(.commit.tree.sha // "") | tostring' 2>&1); then
|
|
877
|
+
detail="merge commit unreadable: $merge_tree"
|
|
878
|
+
merge_tree=''
|
|
879
|
+
elif ! [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]]; then
|
|
880
|
+
detail='merge commit carries no readable tree id'
|
|
881
|
+
merge_tree=''
|
|
882
|
+
elif ! head_commit=$(gh api --method GET \
|
|
883
|
+
"repos/$GITHUB_REPOSITORY/commits/$producing_head" 2>&1); then
|
|
884
|
+
detail="producing head commit unreadable: $head_commit"
|
|
885
|
+
elif ! head_tree=$(printf '%s' "$head_commit" | jq -r \
|
|
886
|
+
'(.commit.tree.sha // "") | tostring' 2>&1); then
|
|
887
|
+
detail="producing head commit unreadable: $head_tree"
|
|
888
|
+
head_tree=''
|
|
889
|
+
elif ! [[ "$head_tree" =~ ^[0-9a-f]{40}$ ]]; then
|
|
890
|
+
detail='producing head commit carries no readable tree id'
|
|
891
|
+
head_tree=''
|
|
892
|
+
elif [ "$merge_tree" != "$head_tree" ]; then
|
|
893
|
+
detail='tree drift: the merge result is not the proven head tree'
|
|
894
|
+
merge_tree=''
|
|
895
|
+
fi
|
|
896
|
+
if [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]] \
|
|
897
|
+
&& [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]] \
|
|
898
|
+
&& [ "$merge_tree" = "$head_tree" ]; then
|
|
899
|
+
gate_lookup "$producing_head"
|
|
900
|
+
if [ "$lookup_reason" = 'proven' ]; then
|
|
901
|
+
reason=proven
|
|
902
|
+
mode="$lookup_mode"
|
|
903
|
+
missing=''
|
|
904
|
+
source_url="$lookup_url"
|
|
905
|
+
proof_head="$producing_head"
|
|
906
|
+
merge_note="the merge result's tree is byte-identical to the tree proven at pull request head $producing_head"
|
|
907
|
+
else
|
|
908
|
+
detail="producing pull request proof not reusable ($lookup_reason)"
|
|
909
|
+
fi
|
|
910
|
+
fi
|
|
911
|
+
fi`;
|
|
912
|
+
/**
|
|
913
|
+
* The fallback with the blank lines that surround it, or nothing at all. Kept
|
|
914
|
+
* as one piece so the default mode emits the exact script it emitted before
|
|
915
|
+
* this option existed.
|
|
916
|
+
*/
|
|
917
|
+
const mergeFallbackSection = (reuse) => reuse === "pull-request" ? "\n" : `\n${MERGE_FALLBACK_BLOCK}\n\n`;
|
|
918
|
+
const gateScript = (required, surface, reuse) => String.raw`
|
|
818
919
|
set -uo pipefail
|
|
819
920
|
|
|
820
921
|
CHECK_NAME=${shellSingleQuote(FACTORY_PROOF_GATE_CHECK_NAME)}
|
|
@@ -901,82 +1002,7 @@ else
|
|
|
901
1002
|
missing="$lookup_missing"
|
|
902
1003
|
source_url="$lookup_url"
|
|
903
1004
|
fi
|
|
904
|
-
|
|
905
|
-
# Merge fallback (#611). A squash merge mints a new commit, so the direct
|
|
906
|
-
# read at a pushed merge-target head finds nothing even when the factory
|
|
907
|
-
# proved the producing pull request head. Only when the direct read found no
|
|
908
|
-
# generation at all on a push event, look up the producing pull request and
|
|
909
|
-
# reuse its head proof — and only when the merge commit's TREE id equals the
|
|
910
|
-
# proven head's tree id, which makes the pushed content byte-identical to
|
|
911
|
-
# what was verified (a clean squash of an unchanged tip). Patch identity was
|
|
912
|
-
# considered and rejected as the comparator: git patch-id normalizes
|
|
913
|
-
# whitespace and ignores base motion, so an identical patch can still
|
|
914
|
-
# produce an integrated tree that was never tested. Everything else — no
|
|
915
|
-
# unique merged producing PR, an unreadable commit, a proof that is anything
|
|
916
|
-
# but proven, or tree drift from a dirty or stale merge — leaves the direct
|
|
917
|
-
# "none" refusal standing, so the full suite runs (fail open).
|
|
918
|
-
if [ "$reason" = 'none' ] && [ "${shellExpansion$1("GITHUB_EVENT_NAME:-")}" = 'push' ]; then
|
|
919
|
-
producing_head=''
|
|
920
|
-
merge_tree=''
|
|
921
|
-
head_tree=''
|
|
922
|
-
if ! producing_pulls=$(gh api --method GET --paginate \
|
|
923
|
-
"repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA/pulls" \
|
|
924
|
-
-f per_page=100 2>&1); then
|
|
925
|
-
detail="producing pull request unreadable: $producing_pulls"
|
|
926
|
-
elif ! producing_head=$(printf '%s' "$producing_pulls" | jq -r -s \
|
|
927
|
-
--arg sha "$HEAD_SHA" \
|
|
928
|
-
'[ add[]?
|
|
929
|
-
| select((.merged_at // null) != null)
|
|
930
|
-
| select((.merge_commit_sha // "") == $sha)
|
|
931
|
-
| ((.head.sha // "") | tostring) ]
|
|
932
|
-
| if length == 1 then .[0] else "" end' 2>&1); then
|
|
933
|
-
detail="producing pull request unreadable: $producing_head"
|
|
934
|
-
producing_head=''
|
|
935
|
-
elif ! [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]]; then
|
|
936
|
-
detail='no single merged producing pull request at this commit'
|
|
937
|
-
producing_head=''
|
|
938
|
-
elif ! merge_commit=$(gh api --method GET \
|
|
939
|
-
"repos/$GITHUB_REPOSITORY/commits/$HEAD_SHA" 2>&1); then
|
|
940
|
-
detail="merge commit unreadable: $merge_commit"
|
|
941
|
-
elif ! merge_tree=$(printf '%s' "$merge_commit" | jq -r \
|
|
942
|
-
'(.commit.tree.sha // "") | tostring' 2>&1); then
|
|
943
|
-
detail="merge commit unreadable: $merge_tree"
|
|
944
|
-
merge_tree=''
|
|
945
|
-
elif ! [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]]; then
|
|
946
|
-
detail='merge commit carries no readable tree id'
|
|
947
|
-
merge_tree=''
|
|
948
|
-
elif ! head_commit=$(gh api --method GET \
|
|
949
|
-
"repos/$GITHUB_REPOSITORY/commits/$producing_head" 2>&1); then
|
|
950
|
-
detail="producing head commit unreadable: $head_commit"
|
|
951
|
-
elif ! head_tree=$(printf '%s' "$head_commit" | jq -r \
|
|
952
|
-
'(.commit.tree.sha // "") | tostring' 2>&1); then
|
|
953
|
-
detail="producing head commit unreadable: $head_tree"
|
|
954
|
-
head_tree=''
|
|
955
|
-
elif ! [[ "$head_tree" =~ ^[0-9a-f]{40}$ ]]; then
|
|
956
|
-
detail='producing head commit carries no readable tree id'
|
|
957
|
-
head_tree=''
|
|
958
|
-
elif [ "$merge_tree" != "$head_tree" ]; then
|
|
959
|
-
detail='tree drift: the merge result is not the proven head tree'
|
|
960
|
-
merge_tree=''
|
|
961
|
-
fi
|
|
962
|
-
if [[ "$producing_head" =~ ^[0-9a-f]{40}$ ]] \
|
|
963
|
-
&& [[ "$merge_tree" =~ ^[0-9a-f]{40}$ ]] \
|
|
964
|
-
&& [ "$merge_tree" = "$head_tree" ]; then
|
|
965
|
-
gate_lookup "$producing_head"
|
|
966
|
-
if [ "$lookup_reason" = 'proven' ]; then
|
|
967
|
-
reason=proven
|
|
968
|
-
mode="$lookup_mode"
|
|
969
|
-
missing=''
|
|
970
|
-
source_url="$lookup_url"
|
|
971
|
-
proof_head="$producing_head"
|
|
972
|
-
merge_note="the merge result's tree is byte-identical to the tree proven at pull request head $producing_head"
|
|
973
|
-
else
|
|
974
|
-
detail="producing pull request proof not reusable ($lookup_reason)"
|
|
975
|
-
fi
|
|
976
|
-
fi
|
|
977
|
-
fi
|
|
978
|
-
|
|
979
|
-
# The binding is App-verified, but it still reaches markdown. Only a plain
|
|
1005
|
+
${mergeFallbackSection(reuse)}# The binding is App-verified, but it still reaches markdown. Only a plain
|
|
980
1006
|
# lowercase token is quoted back; anything else is reported as unknown.
|
|
981
1007
|
case "$mode" in
|
|
982
1008
|
'') ;;
|
|
@@ -1099,9 +1125,9 @@ fi
|
|
|
1099
1125
|
* `FACTORY_PROOF_GATE_SHELL` — so the script is also written to reach that
|
|
1100
1126
|
* write under errexit, and the tests execute it both ways.
|
|
1101
1127
|
*/
|
|
1102
|
-
const factoryProofGateScript = ({ commands, surface }) => {
|
|
1128
|
+
const factoryProofGateScript = ({ commands, reuse = "pull-request-and-default-branch", surface }) => {
|
|
1103
1129
|
const required = proofReuseRequiredCommands(commands);
|
|
1104
|
-
return required ? gateScript(required, safeLabel$1(surface)) : UNUSABLE_SELECTION_SCRIPT;
|
|
1130
|
+
return required ? gateScript(required, safeLabel$1(surface), reuse) : UNUSABLE_SELECTION_SCRIPT;
|
|
1105
1131
|
};
|
|
1106
1132
|
/**
|
|
1107
1133
|
* The step itself, structurally accepted by gagen's `step()` without adding a
|
|
@@ -1111,7 +1137,9 @@ const factoryProofGateScript = ({ commands, surface }) => {
|
|
|
1111
1137
|
* `checks: read`; the push-event merge fallback additionally reads the
|
|
1112
1138
|
* producing pull request (`pull-requests: read`) and the two commit objects
|
|
1113
1139
|
* whose tree ids it compares (`contents: read`). A job that grants less
|
|
1114
|
-
* loses only the fallback — the failed read degrades to the full suite.
|
|
1140
|
+
* loses only the fallback — the failed read degrades to the full suite. A
|
|
1141
|
+
* consumer that passes `reuse: "pull-request"` emits neither the push clause
|
|
1142
|
+
* nor the fallback, and needs only `checks: read` (#873).
|
|
1115
1143
|
*
|
|
1116
1144
|
* A **step, not a job**, and that is not a style preference. A separate gate
|
|
1117
1145
|
* job that errored would leave the guarded job `skipped`, and a summary job
|
|
@@ -1127,7 +1155,7 @@ const factoryProofGateStep = (options) => Object.freeze({
|
|
|
1127
1155
|
HEAD_SHA: githubExpression$1("github.event.pull_request.head.sha || github.sha")
|
|
1128
1156
|
}),
|
|
1129
1157
|
id: FACTORY_PROOF_GATE_STEP_ID,
|
|
1130
|
-
if: FACTORY_PROOF_GATE_IF,
|
|
1158
|
+
if: options.reuse === "pull-request" ? FACTORY_PROOF_GATE_PULL_REQUEST_IF : FACTORY_PROOF_GATE_IF,
|
|
1131
1159
|
name: FACTORY_PROOF_GATE_STEP_NAME,
|
|
1132
1160
|
run: factoryProofGateScript(options),
|
|
1133
1161
|
shell: FACTORY_PROOF_GATE_SHELL
|
|
@@ -1185,6 +1213,20 @@ const assertProofReuseCoverage = (input) => {
|
|
|
1185
1213
|
*/
|
|
1186
1214
|
/** Check-run name that carries one registration JSON payload. Not a gate. */
|
|
1187
1215
|
const PREVIEW_PROOF_INVENTORY_CHECK_NAME = "patronage-factory/preview-proof";
|
|
1216
|
+
/**
|
|
1217
|
+
* Least privilege for a job whose `GITHUB_TOKEN` calls `list`. A
|
|
1218
|
+
* `permissions` block zeroes every unlisted scope, and `list` reads five
|
|
1219
|
+
* endpoints: `pulls/{pr}`, `pulls/{pr}/commits`, and the GraphQL force-push
|
|
1220
|
+
* timeline (`pull-requests`), `compare` (`contents`), and
|
|
1221
|
+
* `commits/{sha}/check-runs` (`checks`). A job
|
|
1222
|
+
* that omits `pull-requests: read` fails with 403 on a same-repository PR
|
|
1223
|
+
* and skips every destroy matrix (#859). Give the discovery job this object.
|
|
1224
|
+
*/
|
|
1225
|
+
const PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS = Object.freeze({
|
|
1226
|
+
checks: "read",
|
|
1227
|
+
contents: "read",
|
|
1228
|
+
"pull-requests": "read"
|
|
1229
|
+
});
|
|
1188
1230
|
const INVENTORY_KIND = "patronage-factory-preview-proof";
|
|
1189
1231
|
const INVENTORY_SCHEMA_VERSION = 1;
|
|
1190
1232
|
const DEFAULT_TIMEOUT_MS = 5e3;
|
|
@@ -1311,6 +1353,7 @@ const assertPersistableRegistration = (registration) => {
|
|
|
1311
1353
|
const registrationKey = (row) => `${row.stack}:${row.stage}`;
|
|
1312
1354
|
const replaceRegistration = (rows, next) => [...rows.filter((row) => registrationKey(row) !== registrationKey(next)), next];
|
|
1313
1355
|
const memoryKey = (owner, repo, pr) => `${owner}/${repo}#${pr}`;
|
|
1356
|
+
/** Reached by callers as `previewProofInventory.memoryStore`. */
|
|
1314
1357
|
const memoryStore = () => {
|
|
1315
1358
|
const byPr = /* @__PURE__ */ new Map();
|
|
1316
1359
|
return {
|
|
@@ -1601,6 +1644,7 @@ const takeNewestRegistrations = (runsBySha, pr) => {
|
|
|
1601
1644
|
}
|
|
1602
1645
|
return [...byKey.values()];
|
|
1603
1646
|
};
|
|
1647
|
+
/** Reached by callers as `previewProofInventory.githubStore`. */
|
|
1604
1648
|
const githubStore = (transport) => {
|
|
1605
1649
|
const request = transport.fetch ?? fetch;
|
|
1606
1650
|
const timeoutMs = transport.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
@@ -1695,6 +1739,7 @@ const recordCleanup = async (input, access) => {
|
|
|
1695
1739
|
const previewProofInventory = {
|
|
1696
1740
|
githubStore,
|
|
1697
1741
|
list,
|
|
1742
|
+
listPermissions: PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS,
|
|
1698
1743
|
memoryStore,
|
|
1699
1744
|
persist,
|
|
1700
1745
|
recordCleanup
|
|
@@ -2106,6 +2151,173 @@ const factoryLifecycleContractLane = (declaration) => {
|
|
|
2106
2151
|
});
|
|
2107
2152
|
};
|
|
2108
2153
|
//#endregion
|
|
2154
|
+
//#region src/merge-freeze-job.ts
|
|
2155
|
+
/**
|
|
2156
|
+
* The merge-freeze writer job (#356, ADR 0016 as amended; #429 for the
|
|
2157
|
+
* merge-target namespace; #872 for this package).
|
|
2158
|
+
*
|
|
2159
|
+
* A consumer's generated merge-target-push Verify workflow is the ONLY
|
|
2160
|
+
* producer of `patronage-factory/merge-freeze` generations: a red
|
|
2161
|
+
* push-triggered Verify completes a generation active, a green one completes
|
|
2162
|
+
* it inactive, and no command, scheduled job, or second workflow writes this
|
|
2163
|
+
* check run. The operator override (`demand:waive --demand merge-freeze`)
|
|
2164
|
+
* waives the demand for one candidate during a fix-forward; it never writes
|
|
2165
|
+
* here.
|
|
2166
|
+
*
|
|
2167
|
+
* The write mirrors what the factory package's merge-freeze reader parses:
|
|
2168
|
+
* create the run `in_progress` with no `started_at` (GitHub stamps the
|
|
2169
|
+
* generation clock — the ordering rule itself is owned by the reader), then
|
|
2170
|
+
* PATCH it completed with the reader-shaped state payload in `output.text`.
|
|
2171
|
+
* The consumer's workflow test executes this exact script against a stubbed
|
|
2172
|
+
* `gh` and parses the emitted payload with `validateMergeFreezeState`, so
|
|
2173
|
+
* writer and reader cannot drift silently.
|
|
2174
|
+
*
|
|
2175
|
+
* Failure posture is deliberately fail-closed and the opposite of the
|
|
2176
|
+
* proof-reuse gate's fail-open shell: this step runs under the runner's
|
|
2177
|
+
* default `bash -e {0}`, so any API refusal aborts the step, the generation
|
|
2178
|
+
* stays absent or `in_progress`, and every `pr:ready` arming-time read of
|
|
2179
|
+
* that tip (#477) refuses until a later push writes a green generation.
|
|
2180
|
+
*
|
|
2181
|
+
* **Ownership line.** This package owns the check name, the App identity, the
|
|
2182
|
+
* `create-github-app-token` inputs, the result fold, the merge-target `if`,
|
|
2183
|
+
* and the job's `permissions`. The caller owns the runner, the `needs` list
|
|
2184
|
+
* of every authoritative verification leaf, the timeout, and any extra env.
|
|
2185
|
+
*/
|
|
2186
|
+
/** The one check-run name every merge-freeze reader pins. */
|
|
2187
|
+
const FACTORY_MERGE_FREEZE_CHECK_NAME = "patronage-factory/merge-freeze";
|
|
2188
|
+
const FACTORY_MERGE_FREEZE_JOB_ID = "freeze";
|
|
2189
|
+
const FACTORY_MERGE_FREEZE_JOB_NAME = "Report the merge freeze";
|
|
2190
|
+
/** Step id the token step exposes its installation token under. */
|
|
2191
|
+
const FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID = "factory-app-token";
|
|
2192
|
+
/**
|
|
2193
|
+
* The GitHub Actions expression that folds the needed jobs' results into the
|
|
2194
|
+
* one word the script branches on. It mirrors the `verify` summary job's
|
|
2195
|
+
* failure condition exactly: any failed or cancelled needed job is a red
|
|
2196
|
+
* merge target.
|
|
2197
|
+
*/
|
|
2198
|
+
const FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION = `\${{ (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) && 'failure' || 'success' }}`;
|
|
2199
|
+
/**
|
|
2200
|
+
* The literal factory merge-target namespace, as a `github.ref` predicate
|
|
2201
|
+
* (#429).
|
|
2202
|
+
*
|
|
2203
|
+
* `main` plus the repository-owned `epic/**` integration branches, and
|
|
2204
|
+
* nothing else. Deliberately literal rather than profile-declared: one
|
|
2205
|
+
* implementation per contract, no configuration machinery for a two-element
|
|
2206
|
+
* namespace whose second element is a namespace prefix the repository itself
|
|
2207
|
+
* owns. `epic/` needs the trailing slash — `startsWith` would otherwise admit
|
|
2208
|
+
* `epicness/…`, which is not a merge target.
|
|
2209
|
+
*
|
|
2210
|
+
* A caller's `push` trigger branch list names the same namespace. That list
|
|
2211
|
+
* decides which events produce a run at all; this predicate decides which of
|
|
2212
|
+
* those runs may write a generation.
|
|
2213
|
+
*/
|
|
2214
|
+
const FACTORY_MERGE_TARGET_REF_CONDITION = ["github.ref == 'refs/heads/main'", "startsWith(github.ref, 'refs/heads/epic/')"].join(" || ");
|
|
2215
|
+
/**
|
|
2216
|
+
* `always()` is load-bearing: the red path is the whole point, so the job must
|
|
2217
|
+
* run when a needed verification job failed. `github.event_name == 'push'` is
|
|
2218
|
+
* load-bearing twice over: a `pull_request` run must never write a generation
|
|
2219
|
+
* (its `github.sha` is a synthetic merge commit that no base tip is ever read
|
|
2220
|
+
* at), and a `pull_request` trigger with an `epic/**` base list would
|
|
2221
|
+
* otherwise reach this job.
|
|
2222
|
+
*/
|
|
2223
|
+
const FACTORY_MERGE_FREEZE_IF = `always() && github.event_name == 'push' && (${FACTORY_MERGE_TARGET_REF_CONDITION})`;
|
|
2224
|
+
/**
|
|
2225
|
+
* Least privilege for the writer job: no `GITHUB_TOKEN` scope at all.
|
|
2226
|
+
*
|
|
2227
|
+
* Both Checks API calls run under the App installation token minted in the
|
|
2228
|
+
* first step (`GH_TOKEN: steps.factory-app-token.outputs.token`), and the job
|
|
2229
|
+
* never checks out the repository. So the job uses no `GITHUB_TOKEN` scope,
|
|
2230
|
+
* and an empty block is the honest declaration. A `checks: write` here would
|
|
2231
|
+
* hand every adopter an unused write scope (#884).
|
|
2232
|
+
*/
|
|
2233
|
+
const FACTORY_MERGE_FREEZE_PERMISSIONS = Object.freeze({});
|
|
2234
|
+
/**
|
|
2235
|
+
* The script the freeze step runs. Environment contract:
|
|
2236
|
+
* - `GH_TOKEN`: an installation token for the pinned Patronage Factory App —
|
|
2237
|
+
* the check run must carry that App's identity or every reader rejects it.
|
|
2238
|
+
* - `VERIFY_RESULT`: `success` or `failure` (see the fold expression above).
|
|
2239
|
+
* - `GITHUB_SHA` / `GITHUB_REPOSITORY` / `GITHUB_SERVER_URL` /
|
|
2240
|
+
* `GITHUB_RUN_ID` / `GITHUB_REF_NAME`: runner-provided.
|
|
2241
|
+
*
|
|
2242
|
+
* `GITHUB_REF_NAME` is the merge target this run pushed to — `main` or an
|
|
2243
|
+
* `epic/**` integration branch (#429). The job is gated to `push` events on
|
|
2244
|
+
* exactly those refs, so the branch name is always the short name of a
|
|
2245
|
+
* repository-owned merge target and never a pull-request merge ref. It
|
|
2246
|
+
* appears in both recovery sentences because a reader of an epic-targeting
|
|
2247
|
+
* candidate must be told which branch to fix forward on: the generation lives
|
|
2248
|
+
* on that branch's tip, and only a later green push to THAT branch clears it.
|
|
2249
|
+
*/
|
|
2250
|
+
const factoryMergeFreezeScript = () => [
|
|
2251
|
+
`sha="$GITHUB_SHA"`,
|
|
2252
|
+
`branch="$GITHUB_REF_NAME"`,
|
|
2253
|
+
`repo="$GITHUB_REPOSITORY"`,
|
|
2254
|
+
`run_url="$GITHUB_SERVER_URL/$repo/actions/runs/$GITHUB_RUN_ID"`,
|
|
2255
|
+
`created=$(gh api --method POST "repos/$repo/check-runs" \\`,
|
|
2256
|
+
` -f "name=${FACTORY_MERGE_FREEZE_CHECK_NAME}" \\`,
|
|
2257
|
+
` -f "head_sha=$sha" \\`,
|
|
2258
|
+
` -f "status=in_progress" \\`,
|
|
2259
|
+
` -f "details_url=$run_url" \\`,
|
|
2260
|
+
` -f "output[title]=merge freeze generation in progress" \\`,
|
|
2261
|
+
` -f "output[summary]=This generation blocks factory merge handoffs until the merge-target Verify result is recorded.")`,
|
|
2262
|
+
`id=$(jq -r '.id' <<<"$created")`,
|
|
2263
|
+
`case "$id" in`,
|
|
2264
|
+
` ''|*[!0-9]*) echo "unusable check-run id: $id" >&2; exit 1;;`,
|
|
2265
|
+
`esac`,
|
|
2266
|
+
`if [ "$VERIFY_RESULT" = 'success' ]; then`,
|
|
2267
|
+
` active=false; outcome=inactive; conclusion=success`,
|
|
2268
|
+
` reason="Merge-target Verify passed on $branch at $sha ($run_url)."`,
|
|
2269
|
+
`else`,
|
|
2270
|
+
` active=true; outcome=active; conclusion=failure`,
|
|
2271
|
+
` reason="Merge-target Verify failed on $branch at $sha; fix forward or revert on $branch and let the next green Verify push on $branch clear this freeze ($run_url)."`,
|
|
2272
|
+
`fi`,
|
|
2273
|
+
`state=$(jq -cn --argjson active "$active" --argjson id "$id" --arg headSha "$sha" --arg outcome "$outcome" --arg reason "$reason" \\`,
|
|
2274
|
+
` '{active: $active, generationId: $id, headSha: $headSha, outcome: $outcome, reason: $reason, recordedAt: (now | todate), schemaVersion: 1}')`,
|
|
2275
|
+
`gh api --method PATCH "repos/$repo/check-runs/$id" \\`,
|
|
2276
|
+
` -f "status=completed" \\`,
|
|
2277
|
+
` -f "conclusion=$conclusion" \\`,
|
|
2278
|
+
` -f "details_url=$run_url" \\`,
|
|
2279
|
+
` -f "output[title]=merge freeze $outcome" \\`,
|
|
2280
|
+
` -f "output[summary]=$reason" \\`,
|
|
2281
|
+
` -f "output[text]=$state" >/dev/null`,
|
|
2282
|
+
`echo "merge-freeze generation $id completed $outcome on $branch at $sha"`
|
|
2283
|
+
].join("\n");
|
|
2284
|
+
/**
|
|
2285
|
+
* Build the merge-freeze writer job.
|
|
2286
|
+
*
|
|
2287
|
+
* An empty `needs` list is refused: a fold over no verification leaf is
|
|
2288
|
+
* always `success`, so such a job would write a green generation for a merge
|
|
2289
|
+
* target nothing verified — the exact failure the freeze exists to prevent.
|
|
2290
|
+
*/
|
|
2291
|
+
const factoryMergeFreezeJob = (options) => {
|
|
2292
|
+
assertPinnedAction("createGithubAppToken", options.createGithubAppToken, "actions/create-github-app-token");
|
|
2293
|
+
if (options.needs.length === 0) throw new Error("factoryMergeFreezeJob needs at least one verification job: a fold over no needed job always reports success, so the freeze would report a merge target nothing verified as green.");
|
|
2294
|
+
const mintStep = Object.freeze({
|
|
2295
|
+
id: FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID,
|
|
2296
|
+
name: "Mint the factory App token",
|
|
2297
|
+
uses: options.createGithubAppToken.uses,
|
|
2298
|
+
with: Object.freeze({
|
|
2299
|
+
"client-id": FACTORY_PROOF_GATE_APP_ID,
|
|
2300
|
+
"private-key": `\${{ secrets.FACTORY_GITHUB_APP_PRIVATE_KEY }}`
|
|
2301
|
+
})
|
|
2302
|
+
});
|
|
2303
|
+
const reportStep = Object.freeze({
|
|
2304
|
+
env: Object.freeze({
|
|
2305
|
+
GH_TOKEN: `\${{ steps.${FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID}.outputs.token }}`,
|
|
2306
|
+
VERIFY_RESULT: options.verifyResultExpression ?? FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION
|
|
2307
|
+
}),
|
|
2308
|
+
name: "Complete the merge-freeze generation",
|
|
2309
|
+
run: factoryMergeFreezeScript()
|
|
2310
|
+
});
|
|
2311
|
+
return Object.freeze({
|
|
2312
|
+
if: FACTORY_MERGE_FREEZE_IF,
|
|
2313
|
+
jobId: FACTORY_MERGE_FREEZE_JOB_ID,
|
|
2314
|
+
jobName: FACTORY_MERGE_FREEZE_JOB_NAME,
|
|
2315
|
+
needs: Object.freeze([...options.needs]),
|
|
2316
|
+
permissions: FACTORY_MERGE_FREEZE_PERMISSIONS,
|
|
2317
|
+
steps: Object.freeze([mintStep, reportStep])
|
|
2318
|
+
});
|
|
2319
|
+
};
|
|
2320
|
+
//#endregion
|
|
2109
2321
|
//#region src/pr-status-hud-workflow.ts
|
|
2110
2322
|
const FACTORY_PR_STATUS_HUD_JOB_ID = "status-hud";
|
|
2111
2323
|
const FACTORY_PR_STATUS_HUD_JOB_NAME = "Present PR status HUD";
|
|
@@ -2118,15 +2330,11 @@ const FACTORY_PR_STATUS_HUD_PLAN_PATH = ".factory-memory/preview-plan.json";
|
|
|
2118
2330
|
*/
|
|
2119
2331
|
const FACTORY_PR_STATUS_HUD_IF = "always() && github.event_name == 'pull_request' && github.event.pull_request.draft != true";
|
|
2120
2332
|
/**
|
|
2121
|
-
* Least privilege for the job that runs the present step.
|
|
2122
|
-
*
|
|
2123
|
-
*
|
|
2333
|
+
* Least privilege for the job that runs the present step. The present step
|
|
2334
|
+
* is an inventory `list`, so the job needs exactly the inventory's read
|
|
2335
|
+
* permissions (#825, #859).
|
|
2124
2336
|
*/
|
|
2125
|
-
const FACTORY_PR_STATUS_HUD_PERMISSIONS =
|
|
2126
|
-
checks: "read",
|
|
2127
|
-
contents: "read",
|
|
2128
|
-
"pull-requests": "read"
|
|
2129
|
-
});
|
|
2337
|
+
const FACTORY_PR_STATUS_HUD_PERMISSIONS = PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS;
|
|
2130
2338
|
const FACTORY_PR_STATUS_HUD_CONCURRENCY = Object.freeze({
|
|
2131
2339
|
cancelInProgress: false,
|
|
2132
2340
|
group: `status-hud-\${{ github.repository }}-\${{ github.event.pull_request.number }}`
|
|
@@ -2221,6 +2429,11 @@ const factoryPreviewCleanupTopology = (options) => {
|
|
|
2221
2429
|
//#endregion
|
|
2222
2430
|
//#region src/push-identity-workflow.ts
|
|
2223
2431
|
const FACTORY_PUSH_IDENTITY_SCHEMA_VERSION = 1;
|
|
2432
|
+
/**
|
|
2433
|
+
* The artifact prefix and the five step IDs are module-private. Alpha.13
|
|
2434
|
+
* removed them from the public surface (#719). A caller reads a step ID from
|
|
2435
|
+
* the builder's returned `steps` (`step.id`) or from `artifactName`.
|
|
2436
|
+
*/
|
|
2224
2437
|
const FACTORY_PUSH_IDENTITY_ARTIFACT_PREFIX = "factory-push-identity";
|
|
2225
2438
|
const FACTORY_PUSH_IDENTITY_RECORD_STEP_ID = "factory_push_identity_record";
|
|
2226
2439
|
const FACTORY_PUSH_IDENTITY_LOOKUP_STEP_ID = "factory_push_identity_lookup";
|
|
@@ -3286,4 +3499,4 @@ const assertWorkflowShellParses = (yaml, options) => {
|
|
|
3286
3499
|
throw new Error(`${options.source} emits shell bash cannot parse; the runner would treat it as a no-op:\n${detail}`);
|
|
3287
3500
|
};
|
|
3288
3501
|
//#endregion
|
|
3289
|
-
export { EXECUTE_ALCHEMY_ENTRY_MAX_BUFFER, FACTORY_CANDIDATE_IMPACT_BASIS_OUTPUT, FACTORY_CANDIDATE_IMPACT_DECISION_OUTPUT, FACTORY_CANDIDATE_IMPACT_INERT_OUTPUT, FACTORY_CANDIDATE_IMPACT_STEP_ID, FACTORY_CANDIDATE_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_CANDIDATE_PULL_REQUEST_TYPES, FACTORY_LIFECYCLE_CONTRACT_JOB_ID, FACTORY_LIFECYCLE_CONTRACT_JOB_NAME, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_ID, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_NAME, FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT, FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT, FACTORY_PRODUCTION_IMPACT_STEP_ID, FACTORY_PRODUCTION_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_PROOF_GATE_APP_ID, FACTORY_PROOF_GATE_CHECK_NAME, FACTORY_PROOF_GATE_GUARD, FACTORY_PROOF_GATE_IF, FACTORY_PROOF_GATE_MODE_OUTPUT, FACTORY_PROOF_GATE_OUTPUT, FACTORY_PROOF_GATE_REASONS, FACTORY_PROOF_GATE_REASON_OUTPUT, FACTORY_PROOF_GATE_SHELL, FACTORY_PROOF_GATE_SOURCE_URL_OUTPUT, FACTORY_PROOF_GATE_STEP_ID, FACTORY_PROOF_GATE_STEP_NAME, FACTORY_PR_STATUS_HUD_APP_TOKEN_STEP_ID, FACTORY_PR_STATUS_HUD_CONCURRENCY, FACTORY_PR_STATUS_HUD_IF, FACTORY_PR_STATUS_HUD_JOB_ID, FACTORY_PR_STATUS_HUD_JOB_NAME, FACTORY_PR_STATUS_HUD_PERMISSIONS, FACTORY_PR_STATUS_HUD_PLAN_PATH, FACTORY_PUSH_IDENTITY_SCHEMA_VERSION, GitHubApiError, NODE_PNPM_ACTION_FAMILY_NODE24, PREVIEW_PROOF_INVENTORY_CHECK_NAME, PreviewProofTransportError, VITEST_PROFILE_SCHEMA_VERSION, VITEST_PROFILE_TOOL, VitestProfileError, assertProofReuseCoverage, assertWorkflowShellParses, bundleAlchemyEntry, captureVitestProfileEnvironment, executeAlchemyEntry, factoryCandidateImpactWorkflow, factoryCandidateOrPushCondition, factoryLifecycleContractLane, factoryPrStatusHudWorkflow, factoryPreviewCleanupTopology, factoryProductionImpactWorkflow, factoryProofGateScript, factoryProofGateStep, factoryProofReuseSummaryStep, factoryProofTimingStartStep, factoryPushIdentityConsumer, factoryPushIdentityProducer, factoryWorkflow, githubAppJwt, isLocalPreviewStage, localPreviewStage, mintInstallationToken, normalizeVitestProfileSample, parseLocalPreviewStage, previewCleanupDestroyJobId, previewProofInventory, previewProofLifecycle, productionImpactTargetOutput, proofReuseCoverage, proofReuseRequiredCommands, readVitestProfileDocument, resolveProofReuseCommands, runVitestProfile, workflowRunBlocks, workflowShellParseFailures, writeVitestProfile };
|
|
3502
|
+
export { EXECUTE_ALCHEMY_ENTRY_MAX_BUFFER, FACTORY_CANDIDATE_IMPACT_BASIS_OUTPUT, FACTORY_CANDIDATE_IMPACT_DECISION_OUTPUT, FACTORY_CANDIDATE_IMPACT_INERT_OUTPUT, FACTORY_CANDIDATE_IMPACT_STEP_ID, FACTORY_CANDIDATE_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_CANDIDATE_PULL_REQUEST_TYPES, FACTORY_LIFECYCLE_CONTRACT_JOB_ID, FACTORY_LIFECYCLE_CONTRACT_JOB_NAME, FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID, FACTORY_MERGE_FREEZE_CHECK_NAME, FACTORY_MERGE_FREEZE_IF, FACTORY_MERGE_FREEZE_JOB_ID, FACTORY_MERGE_FREEZE_JOB_NAME, FACTORY_MERGE_FREEZE_PERMISSIONS, FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION, FACTORY_MERGE_TARGET_REF_CONDITION, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_ID, FACTORY_PREVIEW_CLEANUP_AUDIT_JOB_NAME, FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT, FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT, FACTORY_PRODUCTION_IMPACT_STEP_ID, FACTORY_PRODUCTION_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_PROOF_GATE_APP_ID, FACTORY_PROOF_GATE_CHECK_NAME, FACTORY_PROOF_GATE_GUARD, FACTORY_PROOF_GATE_IF, FACTORY_PROOF_GATE_MODE_OUTPUT, FACTORY_PROOF_GATE_OUTPUT, FACTORY_PROOF_GATE_PULL_REQUEST_IF, FACTORY_PROOF_GATE_REASONS, FACTORY_PROOF_GATE_REASON_OUTPUT, FACTORY_PROOF_GATE_SHELL, FACTORY_PROOF_GATE_SOURCE_URL_OUTPUT, FACTORY_PROOF_GATE_STEP_ID, FACTORY_PROOF_GATE_STEP_NAME, FACTORY_PR_STATUS_HUD_APP_TOKEN_STEP_ID, FACTORY_PR_STATUS_HUD_CONCURRENCY, FACTORY_PR_STATUS_HUD_IF, FACTORY_PR_STATUS_HUD_JOB_ID, FACTORY_PR_STATUS_HUD_JOB_NAME, FACTORY_PR_STATUS_HUD_PERMISSIONS, FACTORY_PR_STATUS_HUD_PLAN_PATH, FACTORY_PUSH_IDENTITY_SCHEMA_VERSION, GitHubApiError, NODE_PNPM_ACTION_FAMILY_NODE24, PREVIEW_PROOF_INVENTORY_CHECK_NAME, PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS, PreviewProofTransportError, VITEST_PROFILE_SCHEMA_VERSION, VITEST_PROFILE_TOOL, VitestProfileError, assertProofReuseCoverage, assertWorkflowShellParses, bundleAlchemyEntry, captureVitestProfileEnvironment, executeAlchemyEntry, factoryCandidateImpactWorkflow, factoryCandidateOrPushCondition, factoryLifecycleContractLane, factoryMergeFreezeJob, factoryMergeFreezeScript, factoryPrStatusHudWorkflow, factoryPreviewCleanupTopology, factoryProductionImpactWorkflow, factoryProofGateScript, factoryProofGateStep, factoryProofReuseSummaryStep, factoryProofTimingStartStep, factoryPushIdentityConsumer, factoryPushIdentityProducer, factoryWorkflow, githubAppJwt, isLocalPreviewStage, localPreviewStage, mintInstallationToken, normalizeVitestProfileSample, parseLocalPreviewStage, previewCleanupDestroyJobId, previewProofInventory, previewProofLifecycle, productionImpactTargetOutput, proofReuseCoverage, proofReuseRequiredCommands, readVitestProfileDocument, resolveProofReuseCommands, runVitestProfile, workflowRunBlocks, workflowShellParseFailures, writeVitestProfile };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patronage/factory-ci",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.22",
|
|
4
4
|
"description": "Deep CI and deploy building blocks for Patronage factory projects: workflow source artifacts, hosted diff classification, Alchemy entry execution, and disposable-stage semantics",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"alchemy",
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ export {
|
|
|
43
43
|
} from "./preview-proof-lifecycle.ts";
|
|
44
44
|
export {
|
|
45
45
|
PREVIEW_PROOF_INVENTORY_CHECK_NAME,
|
|
46
|
+
PREVIEW_PROOF_INVENTORY_LIST_PERMISSIONS,
|
|
46
47
|
previewProofInventory,
|
|
47
48
|
type PreviewProofInventoryAccess,
|
|
48
49
|
type PreviewProofInventoryCleanupInput,
|
|
@@ -83,6 +84,7 @@ export {
|
|
|
83
84
|
FACTORY_PROOF_GATE_IF,
|
|
84
85
|
FACTORY_PROOF_GATE_MODE_OUTPUT,
|
|
85
86
|
FACTORY_PROOF_GATE_OUTPUT,
|
|
87
|
+
FACTORY_PROOF_GATE_PULL_REQUEST_IF,
|
|
86
88
|
FACTORY_PROOF_GATE_REASON_OUTPUT,
|
|
87
89
|
FACTORY_PROOF_GATE_REASONS,
|
|
88
90
|
FACTORY_PROOF_GATE_SHELL,
|
|
@@ -91,6 +93,7 @@ export {
|
|
|
91
93
|
FACTORY_PROOF_GATE_STEP_NAME,
|
|
92
94
|
type FactoryProofGateOptions,
|
|
93
95
|
type FactoryProofGateReason,
|
|
96
|
+
type FactoryProofGateReuse,
|
|
94
97
|
factoryProofGateScript,
|
|
95
98
|
type FactoryProofGateStep,
|
|
96
99
|
factoryProofGateStep,
|
|
@@ -125,6 +128,20 @@ export {
|
|
|
125
128
|
type FactoryLifecycleContractLaneOptions,
|
|
126
129
|
factoryLifecycleContractLane,
|
|
127
130
|
} from "./lifecycle-contract-lane.ts";
|
|
131
|
+
export {
|
|
132
|
+
FACTORY_MERGE_FREEZE_APP_TOKEN_STEP_ID,
|
|
133
|
+
FACTORY_MERGE_FREEZE_CHECK_NAME,
|
|
134
|
+
FACTORY_MERGE_FREEZE_IF,
|
|
135
|
+
FACTORY_MERGE_FREEZE_JOB_ID,
|
|
136
|
+
FACTORY_MERGE_FREEZE_JOB_NAME,
|
|
137
|
+
FACTORY_MERGE_FREEZE_PERMISSIONS,
|
|
138
|
+
FACTORY_MERGE_FREEZE_VERIFY_RESULT_EXPRESSION,
|
|
139
|
+
FACTORY_MERGE_TARGET_REF_CONDITION,
|
|
140
|
+
type FactoryMergeFreezeJob,
|
|
141
|
+
factoryMergeFreezeJob,
|
|
142
|
+
type FactoryMergeFreezeJobOptions,
|
|
143
|
+
factoryMergeFreezeScript,
|
|
144
|
+
} from "./merge-freeze-job.ts";
|
|
128
145
|
export {
|
|
129
146
|
FACTORY_PR_STATUS_HUD_APP_TOKEN_STEP_ID,
|
|
130
147
|
FACTORY_PR_STATUS_HUD_CONCURRENCY,
|