@teambit/ci 1.0.485 → 1.0.487
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/ci.docs.mdx +344 -83
- package/commands/sync.cmd.ts +77 -0
- package/dist/ci.docs.mdx +344 -83
- package/dist/ci.main.runtime.d.ts +67 -6
- package/dist/ci.main.runtime.js +152 -25
- package/dist/ci.main.runtime.js.map +1 -1
- package/dist/commands/sync.cmd.d.ts +34 -0
- package/dist/commands/sync.cmd.js +57 -0
- package/dist/commands/sync.cmd.js.map +1 -0
- package/dist/git.d.ts +11 -1
- package/dist/git.js +11 -1
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +26 -1
- package/dist/index.js.map +1 -1
- package/dist/{preview-1785858957533.js → preview-1785883495880.js} +1 -1
- package/dist/sync/git-host-provider.d.ts +47 -0
- package/dist/sync/git-host-provider.js +54 -0
- package/dist/sync/git-host-provider.js.map +1 -0
- package/dist/sync/git-host-provider.spec.d.ts +1 -0
- package/dist/sync/git-host-provider.spec.js +171 -0
- package/dist/sync/git-host-provider.spec.js.map +1 -0
- package/dist/sync/git-ops.d.ts +171 -0
- package/dist/sync/git-ops.js +354 -0
- package/dist/sync/git-ops.js.map +1 -0
- package/dist/sync/git-ops.spec.d.ts +1 -0
- package/dist/sync/git-ops.spec.js +476 -0
- package/dist/sync/git-ops.spec.js.map +1 -0
- package/dist/sync/github-client.d.ts +79 -0
- package/dist/sync/github-client.js +198 -0
- package/dist/sync/github-client.js.map +1 -0
- package/dist/sync/github-client.spec.d.ts +1 -0
- package/dist/sync/github-client.spec.js +205 -0
- package/dist/sync/github-client.spec.js.map +1 -0
- package/dist/sync/init-scaffold.d.ts +40 -0
- package/dist/sync/init-scaffold.js +326 -0
- package/dist/sync/init-scaffold.js.map +1 -0
- package/dist/sync/init-scaffold.spec.d.ts +1 -0
- package/dist/sync/init-scaffold.spec.js +169 -0
- package/dist/sync/init-scaffold.spec.js.map +1 -0
- package/dist/sync/lane-sync-executor.d.ts +256 -0
- package/dist/sync/lane-sync-executor.js +1249 -0
- package/dist/sync/lane-sync-executor.js.map +1 -0
- package/dist/sync/lane-sync-executor.spec.d.ts +1 -0
- package/dist/sync/lane-sync-executor.spec.js +416 -0
- package/dist/sync/lane-sync-executor.spec.js.map +1 -0
- package/dist/sync/main-sync-executor.d.ts +62 -0
- package/dist/sync/main-sync-executor.js +305 -0
- package/dist/sync/main-sync-executor.js.map +1 -0
- package/dist/sync/sync-config.d.ts +83 -0
- package/dist/sync/sync-config.js +187 -0
- package/dist/sync/sync-config.js.map +1 -0
- package/dist/sync/sync-config.spec.d.ts +1 -0
- package/dist/sync/sync-config.spec.js +254 -0
- package/dist/sync/sync-config.spec.js.map +1 -0
- package/dist/sync/sync-orchestrator.d.ts +76 -0
- package/dist/sync/sync-orchestrator.js +396 -0
- package/dist/sync/sync-orchestrator.js.map +1 -0
- package/dist/sync/sync-orchestrator.spec.d.ts +1 -0
- package/dist/sync/sync-orchestrator.spec.js +155 -0
- package/dist/sync/sync-orchestrator.spec.js.map +1 -0
- package/dist/sync/sync-planner.d.ts +60 -0
- package/dist/sync/sync-planner.js +114 -0
- package/dist/sync/sync-planner.js.map +1 -0
- package/dist/sync/sync-planner.spec.d.ts +1 -0
- package/dist/sync/sync-planner.spec.js +210 -0
- package/dist/sync/sync-planner.spec.js.map +1 -0
- package/dist/sync/sync-state.d.ts +94 -0
- package/dist/sync/sync-state.js +178 -0
- package/dist/sync/sync-state.js.map +1 -0
- package/dist/sync/sync-state.spec.d.ts +1 -0
- package/dist/sync/sync-state.spec.js +271 -0
- package/dist/sync/sync-state.spec.js.map +1 -0
- package/dist/sync/workspace-lane.d.ts +13 -0
- package/dist/sync/workspace-lane.js +27 -0
- package/dist/sync/workspace-lane.js.map +1 -0
- package/package.json +20 -15
- package/sync/git-host-provider.spec.ts +197 -0
- package/sync/git-host-provider.ts +89 -0
- package/sync/git-ops.spec.ts +506 -0
- package/sync/git-ops.ts +334 -0
- package/sync/github-client.spec.ts +174 -0
- package/sync/github-client.ts +209 -0
- package/sync/init-scaffold.spec.ts +142 -0
- package/sync/init-scaffold.ts +340 -0
- package/sync/lane-sync-executor.spec.ts +389 -0
- package/sync/lane-sync-executor.ts +1363 -0
- package/sync/main-sync-executor.ts +321 -0
- package/sync/sync-config.spec.ts +283 -0
- package/sync/sync-config.ts +214 -0
- package/sync/sync-orchestrator.spec.ts +120 -0
- package/sync/sync-orchestrator.ts +386 -0
- package/sync/sync-planner.spec.ts +126 -0
- package/sync/sync-planner.ts +99 -0
- package/sync/sync-state.spec.ts +263 -0
- package/sync/sync-state.ts +176 -0
- package/sync/workspace-lane.ts +20 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.WORKFLOW_RELATIVE_PATHS = void 0;
|
|
7
|
+
exports.deriveOwnerRepo = deriveOwnerRepo;
|
|
8
|
+
exports.renderBitReleaseWorkflow = renderBitReleaseWorkflow;
|
|
9
|
+
exports.renderBitSyncWorkflow = renderBitSyncWorkflow;
|
|
10
|
+
exports.renderInitChecklist = renderInitChecklist;
|
|
11
|
+
exports.scaffoldWorkflowFiles = scaffoldWorkflowFiles;
|
|
12
|
+
function _fs() {
|
|
13
|
+
const data = _interopRequireDefault(require("fs"));
|
|
14
|
+
_fs = function () {
|
|
15
|
+
return data;
|
|
16
|
+
};
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
function _path() {
|
|
20
|
+
const data = _interopRequireDefault(require("path"));
|
|
21
|
+
_path = function () {
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
function _githubClient() {
|
|
27
|
+
const data = require("./github-client");
|
|
28
|
+
_githubClient = function () {
|
|
29
|
+
return data;
|
|
30
|
+
};
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
34
|
+
/**
|
|
35
|
+
* `bit ci sync --init` — pure scaffolding logic, unit-testable without a live workspace/git checkout.
|
|
36
|
+
*
|
|
37
|
+
* TEMPLATE SOURCE OF TRUTH: `BIT_SYNC_WORKFLOW_RAW` / `BIT_RELEASE_WORKFLOW_RAW` are byte-for-byte
|
|
38
|
+
* copies of the canonical templates in the `bit-git-sync` action workspace
|
|
39
|
+
* (git-sync/git-sync/workflows/templates/) and MUST be kept in sync with them. The ONLY departure is
|
|
40
|
+
* the two `DEFAULT_BRANCH_MARKER` substitution points; everything else — CHANGE-ME comments, the
|
|
41
|
+
* placeholder action ref, `bit-sync/main` config defaults — stays untouched.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/** Where `scaffoldWorkflowFiles` writes each file, relative to the workspace root. */
|
|
45
|
+
const WORKFLOW_RELATIVE_PATHS = exports.WORKFLOW_RELATIVE_PATHS = {
|
|
46
|
+
sync: _path().default.join('.github', 'workflows', 'bit-sync.yml'),
|
|
47
|
+
release: _path().default.join('.github', 'workflows', 'bit-release.yml')
|
|
48
|
+
};
|
|
49
|
+
const BIT_SYNC_WORKFLOW_RAW = `# Copy this file into your repository at \`.github/workflows/bit-sync.yml\`,
|
|
50
|
+
# then review every line marked \`CHANGE-ME\` below -- the defaults assume a
|
|
51
|
+
# repository whose default branch is \`main\` and a workspace at the repo root.
|
|
52
|
+
#
|
|
53
|
+
# See the \`workflows.docs.mdx\` customer setup guide (component
|
|
54
|
+
# bitdev.git-sync/git-sync/workflows) for the full walk-through: the
|
|
55
|
+
# bit.cloud webhook wiring that drives \`repository_dispatch\`, the required
|
|
56
|
+
# secrets, and the \`teambit.git/ci\`.\`sync\` workspace config this workflow
|
|
57
|
+
# acts on.
|
|
58
|
+
name: bit-sync
|
|
59
|
+
on:
|
|
60
|
+
repository_dispatch:
|
|
61
|
+
# 'bit-export' is the real, empirically-verified bit.cloud webhook
|
|
62
|
+
# contract: a single dispatch type discriminated by
|
|
63
|
+
# client_payload.laneId (non-empty 'scope/lane' -> lane export, empty ->
|
|
64
|
+
# main export). The other three types are forward-compat aliases the
|
|
65
|
+
# event router also accepts, in case a webhook is configured to send the
|
|
66
|
+
# originally-assumed, more granular dispatch types instead.
|
|
67
|
+
types: [bit-export, bit-lane-export, bit-main-export, bit-lane-removed]
|
|
68
|
+
schedule:
|
|
69
|
+
# Scheduled reconcile. This is also the ONLY mechanism that picks up
|
|
70
|
+
# lane deletions -- bit.cloud has no "lane removed" webhook event.
|
|
71
|
+
- cron: '*/30 * * * *'
|
|
72
|
+
push:
|
|
73
|
+
# CHANGE-ME: \`main\` must be your repository's DEFAULT branch, and
|
|
74
|
+
# \`bit-sync/**\` must cover your configured \`sync.mainSyncBranch\`. Pushes
|
|
75
|
+
# to either are bit-sync's own output and must not re-trigger a sync.
|
|
76
|
+
branches-ignore: [main, 'bit-sync/**']
|
|
77
|
+
workflow_dispatch:
|
|
78
|
+
inputs:
|
|
79
|
+
lane: { description: Lane to sync (empty = all), required: false }
|
|
80
|
+
|
|
81
|
+
# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations
|
|
82
|
+
# created after Feb 2023, and anywhere the org/repo default is set to
|
|
83
|
+
# "Read repository contents permission". Sync pushes branches and
|
|
84
|
+
# opens/updates PRs, so both scopes must be declared explicitly -- without
|
|
85
|
+
# this block the sync fails with a 403 on its first write.
|
|
86
|
+
permissions:
|
|
87
|
+
contents: write
|
|
88
|
+
pull-requests: write
|
|
89
|
+
|
|
90
|
+
concurrency:
|
|
91
|
+
# Serializes one lane's syncs so two runs never race on the same branch/PR.
|
|
92
|
+
#
|
|
93
|
+
# Each trigger needs a different expression, because the lane identity
|
|
94
|
+
# arrives in a different place every time:
|
|
95
|
+
# - repository_dispatch -> client_payload.laneId ('scope/lane'), or
|
|
96
|
+
# client_payload.lane for the alias types
|
|
97
|
+
# - push -> github.ref_name (the branch)
|
|
98
|
+
# - workflow_dispatch -> the \`lane\` input
|
|
99
|
+
# - schedule -> nothing lane-shaped; it reconciles everything
|
|
100
|
+
#
|
|
101
|
+
# github.ref_name is populated on schedule runs too (it is the default
|
|
102
|
+
# branch there), so it may only be consulted AFTER the event name has been
|
|
103
|
+
# checked. Reading it first -- as a bare \`... || github.ref_name || 'all'\`
|
|
104
|
+
# chain does -- makes the final fallback unreachable and lumps every cron
|
|
105
|
+
# run into the same group as pushes to the default branch.
|
|
106
|
+
#
|
|
107
|
+
# RESIDUAL RACE (accepted, by design): a webhook run groups as
|
|
108
|
+
# 'scope/my-lane' while a push to that lane's branch groups as 'my-lane', so
|
|
109
|
+
# two runs for the same lane can still overlap across those two triggers.
|
|
110
|
+
# GitHub expressions have no substring/split function, and bit.cloud's
|
|
111
|
+
# webhook payload template exposes only the full laneId, so the two keys
|
|
112
|
+
# cannot be normalized into one here. This is safe rather than merely
|
|
113
|
+
# tolerated: the reconciler is idempotent and the sync executor diffs
|
|
114
|
+
# against converged state, so a second overlapping run finds nothing left to
|
|
115
|
+
# do and no-ops. The concurrency group is an efficiency measure; it is not
|
|
116
|
+
# what makes the sync correct.
|
|
117
|
+
group: >-
|
|
118
|
+
bit-sync-\${{
|
|
119
|
+
github.event_name == 'repository_dispatch' && (github.event.client_payload.laneId || github.event.client_payload.lane || 'main-export')
|
|
120
|
+
|| github.event_name == 'push' && github.ref_name
|
|
121
|
+
|| github.event_name == 'workflow_dispatch' && (github.event.inputs.lane || 'reconcile-all')
|
|
122
|
+
|| 'reconcile-all' }}
|
|
123
|
+
# Never cancel a sync mid-flight: it may already have pushed a branch and be
|
|
124
|
+
# partway through opening or updating its PR.
|
|
125
|
+
cancel-in-progress: false
|
|
126
|
+
|
|
127
|
+
jobs:
|
|
128
|
+
sync:
|
|
129
|
+
runs-on: ubuntu-latest
|
|
130
|
+
steps:
|
|
131
|
+
- uses: actions/checkout@v4
|
|
132
|
+
with:
|
|
133
|
+
fetch-depth: 0
|
|
134
|
+
# A PAT/App token so that pushes this workflow makes (e.g. a sync
|
|
135
|
+
# PR branch) can themselves trigger CI. Pushes authenticated with
|
|
136
|
+
# the default GITHUB_TOKEN do NOT trigger other workflow runs.
|
|
137
|
+
token: \${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
|
138
|
+
- uses: bit-tasks/init@v2
|
|
139
|
+
# Pinned to a commit SHA on purpose: this job holds write permission, and
|
|
140
|
+
# a tag can move. Bump the SHA to take a new version of the action.
|
|
141
|
+
- uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41
|
|
142
|
+
with:
|
|
143
|
+
# CHANGE-ME: keep this in sync with
|
|
144
|
+
# \`teambit.git/ci\`.\`sync.mainSyncBranch\` in your workspace.jsonc
|
|
145
|
+
# (the action's own default is shown here).
|
|
146
|
+
main-sync-branch: bit-sync/main
|
|
147
|
+
# Uncomment if your workspace.jsonc is NOT at the repository root --
|
|
148
|
+
# the action chdirs here before running anything.
|
|
149
|
+
# ws-dir: packages/my-workspace
|
|
150
|
+
env:
|
|
151
|
+
# Service account token. This account must be EXEMPT from the
|
|
152
|
+
# org's change-request/merge block, or bit.cloud rejects the
|
|
153
|
+
# sync-driven writes.
|
|
154
|
+
BIT_CONFIG_ACCESS_TOKEN: \${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}
|
|
155
|
+
GITHUB_TOKEN: \${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
|
156
|
+
# Optional: git identity for the commits the sync creates. Defaults
|
|
157
|
+
# to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.
|
|
158
|
+
# GIT_USER_NAME: bit-sync[bot]
|
|
159
|
+
# GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com
|
|
160
|
+
`;
|
|
161
|
+
const BIT_RELEASE_WORKFLOW_RAW = `# Copy this file into your repository at \`.github/workflows/bit-release.yml\`,
|
|
162
|
+
# then review every line marked \`CHANGE-ME\` below.
|
|
163
|
+
#
|
|
164
|
+
# See the \`workflows.docs.mdx\` customer setup guide (component
|
|
165
|
+
# bitdev.git-sync/git-sync/workflows) for the full walk-through.
|
|
166
|
+
name: bit-release
|
|
167
|
+
on:
|
|
168
|
+
pull_request:
|
|
169
|
+
types: [closed]
|
|
170
|
+
# CHANGE-ME: your repository's DEFAULT branch. \`bit ci merge\` releases
|
|
171
|
+
# from the default branch, so only PRs merged INTO it should trigger a
|
|
172
|
+
# release -- a stacked PR landing on another feature branch has not
|
|
173
|
+
# reached the default branch yet. The action's event router enforces the
|
|
174
|
+
# same rule from \`pull_request.base.ref\`; this filter means a run is never
|
|
175
|
+
# even queued for a non-default base.
|
|
176
|
+
branches: [main]
|
|
177
|
+
|
|
178
|
+
# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations
|
|
179
|
+
# created after Feb 2023, and anywhere the org/repo default is set to
|
|
180
|
+
# "Read repository contents permission". The release run pushes commits/tags
|
|
181
|
+
# and updates PRs, so both scopes must be declared explicitly.
|
|
182
|
+
permissions:
|
|
183
|
+
contents: write
|
|
184
|
+
pull-requests: write
|
|
185
|
+
|
|
186
|
+
concurrency:
|
|
187
|
+
group: bit-release-\${{ github.event.pull_request.number }}
|
|
188
|
+
# Never cancel a release mid-flight.
|
|
189
|
+
cancel-in-progress: false
|
|
190
|
+
|
|
191
|
+
jobs:
|
|
192
|
+
release:
|
|
193
|
+
runs-on: ubuntu-latest
|
|
194
|
+
# Merged main-sync PRs must NOT trigger a release: the scope is already
|
|
195
|
+
# ahead of this merge, there is nothing new to release. The event
|
|
196
|
+
# router also skips this case (routePullRequest checks
|
|
197
|
+
# prHeadRef === mainSyncBranch) -- this \`if:\` is defense in depth so the
|
|
198
|
+
# job doesn't even start.
|
|
199
|
+
# CHANGE-ME: the branch literal below MUST match your workspace's
|
|
200
|
+
# configured \`teambit.git/ci\`.\`sync.mainSyncBranch\`.
|
|
201
|
+
if: github.event.pull_request.merged == true && github.event.pull_request.head.ref != 'bit-sync/main'
|
|
202
|
+
steps:
|
|
203
|
+
- uses: actions/checkout@v4
|
|
204
|
+
with:
|
|
205
|
+
fetch-depth: 0
|
|
206
|
+
token: \${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
|
207
|
+
- uses: bit-tasks/init@v2
|
|
208
|
+
# Pinned to a commit SHA on purpose: this job holds write permission, and
|
|
209
|
+
# a tag can move. Bump the SHA to take a new version of the action.
|
|
210
|
+
- uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41
|
|
211
|
+
with:
|
|
212
|
+
# CHANGE-ME: keep this in sync with
|
|
213
|
+
# \`teambit.git/ci\`.\`sync.mainSyncBranch\` AND with the \`if:\`
|
|
214
|
+
# condition above.
|
|
215
|
+
main-sync-branch: bit-sync/main
|
|
216
|
+
# Uncomment if your workspace.jsonc is NOT at the repository root --
|
|
217
|
+
# the action chdirs here before running anything.
|
|
218
|
+
# ws-dir: packages/my-workspace
|
|
219
|
+
env:
|
|
220
|
+
BIT_CONFIG_ACCESS_TOKEN: \${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}
|
|
221
|
+
GITHUB_TOKEN: \${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
|
222
|
+
# Optional: git identity for the commits the release creates.
|
|
223
|
+
# Defaults to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.
|
|
224
|
+
# GIT_USER_NAME: bit-sync[bot]
|
|
225
|
+
# GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com
|
|
226
|
+
`;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The exact substring in each template that encodes the repository's default branch. Exact substring,
|
|
230
|
+
* never a blanket `main` regex — that would also corrupt `bit-main-export` and `bit-sync/main`.
|
|
231
|
+
*/
|
|
232
|
+
const DEFAULT_BRANCH_MARKER = {
|
|
233
|
+
sync: "branches-ignore: [main, 'bit-sync/**']",
|
|
234
|
+
release: 'branches: [main]'
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* A YAML single-quoted scalar of `value` (the only escape inside single quotes is a doubled `''`).
|
|
239
|
+
* Both substitution points land inside a YAML flow sequence, where `,` and `]` are structural — and a
|
|
240
|
+
* branch name may legally contain both, so quoting is unconditional.
|
|
241
|
+
*/
|
|
242
|
+
function yamlSingleQuoted(value) {
|
|
243
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Replace the default-branch literal inside `marker` with `defaultBranch`, inside `raw`. Throws when
|
|
248
|
+
* the marker has drifted out of the template — a reworded canonical workflow must fail loudly, not
|
|
249
|
+
* ship a scaffold that still says `main`.
|
|
250
|
+
*/
|
|
251
|
+
function substituteDefaultBranch(raw, marker, defaultBranch) {
|
|
252
|
+
if (!raw.includes(marker)) {
|
|
253
|
+
throw new Error(`init-scaffold: expected marker "${marker}" not found in its workflow template — the canonical ` + `template (bitdev.git-sync/git-sync/workflows) has drifted from BIT_SYNC_WORKFLOW_RAW / ` + `BIT_RELEASE_WORKFLOW_RAW; update the copy and this substitution point together`);
|
|
254
|
+
}
|
|
255
|
+
// Function replacements, not strings: `$&`/`$'`/`$$` are special in string replacements and `$` is
|
|
256
|
+
// git-legal in a branch name. A function replacement is inserted verbatim.
|
|
257
|
+
const substitutedMarker = marker.replace('main', () => yamlSingleQuoted(defaultBranch));
|
|
258
|
+
return raw.replace(marker, () => substitutedMarker);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Render `bit-sync.yml` with the repository's actual default branch substituted. */
|
|
262
|
+
function renderBitSyncWorkflow(defaultBranch) {
|
|
263
|
+
return substituteDefaultBranch(BIT_SYNC_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.sync, defaultBranch);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Render `bit-release.yml` with the repository's actual default branch substituted. */
|
|
267
|
+
function renderBitReleaseWorkflow(defaultBranch) {
|
|
268
|
+
return substituteDefaultBranch(BIT_RELEASE_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.release, defaultBranch);
|
|
269
|
+
}
|
|
270
|
+
// Never overwrites an existing file — `--init` must be safe to re-run after hand-edits.
|
|
271
|
+
function writeIfAbsent(workspaceDir, relativePath, content) {
|
|
272
|
+
const absPath = _path().default.join(workspaceDir, relativePath);
|
|
273
|
+
if (_fs().default.existsSync(absPath)) {
|
|
274
|
+
return {
|
|
275
|
+
relativePath,
|
|
276
|
+
status: 'skipped'
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
_fs().default.mkdirSync(_path().default.dirname(absPath), {
|
|
280
|
+
recursive: true
|
|
281
|
+
});
|
|
282
|
+
_fs().default.writeFileSync(absPath, content);
|
|
283
|
+
return {
|
|
284
|
+
relativePath,
|
|
285
|
+
status: 'written'
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Scaffold both workflow files under `workspaceDir`. Never overwrites; creates `.github/workflows/` as needed. */
|
|
290
|
+
function scaffoldWorkflowFiles(workspaceDir, defaultBranch) {
|
|
291
|
+
return [writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.sync, renderBitSyncWorkflow(defaultBranch)), writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.release, renderBitReleaseWorkflow(defaultBranch))];
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Derive `{owner, repo}` from the `origin` remote when it is a parseable GitHub remote, else
|
|
295
|
+
* undefined (the checklist falls back to a placeholder). Reuses `github-client.ts`'s parsing so the
|
|
296
|
+
* two never disagree.
|
|
297
|
+
*/
|
|
298
|
+
function deriveOwnerRepo(remoteUrl) {
|
|
299
|
+
if (!remoteUrl || !(0, _githubClient().isGitHubRemote)(remoteUrl)) return undefined;
|
|
300
|
+
const ownerSlashRepo = (0, _githubClient().parseGitHubRepo)(remoteUrl);
|
|
301
|
+
if (!ownerSlashRepo) return undefined;
|
|
302
|
+
const [owner, repo] = ownerSlashRepo.split('/');
|
|
303
|
+
if (!owner || !repo) return undefined;
|
|
304
|
+
return {
|
|
305
|
+
owner,
|
|
306
|
+
repo
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* The bit.cloud custom webhook payload template, verified byte-perfect against a live delivery.
|
|
312
|
+
* Lane vs. main export is discriminated by whether `laneId` is empty.
|
|
313
|
+
*/
|
|
314
|
+
const WEBHOOK_PAYLOAD_TEMPLATE = '{"event_type":"bit-export","client_payload":{"laneId":"{{laneId}}","componentIds":"{{componentIds}}","owner":"{{owner}}","actor":"{{username}}"}}';
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The manual-steps checklist `bit ci sync --init` prints: everything the command itself cannot do
|
|
318
|
+
* (secrets live in GitHub settings; the webhook lives on bit.cloud).
|
|
319
|
+
*/
|
|
320
|
+
function renderInitChecklist(ownerRepo) {
|
|
321
|
+
const repoSegment = ownerRepo ? `${ownerRepo.owner}/${ownerRepo.repo}` : '<owner>/<repo>';
|
|
322
|
+
const dispatchUrl = `https://api.github.com/repos/${repoSegment}/dispatches`;
|
|
323
|
+
return ['', 'Remaining manual steps (bit ci sync --init only scaffolds files -- these need a human):', '', '1. Secrets -- add in the repository (or org) Settings > Secrets and variables > Actions:', ' - BIT_CONFIG_ACCESS_TOKEN (required): a bit.cloud service-account token with export rights on', " this scope. This account must also be exempt from the org's", ' change-request/merge block -- it is the only identity that merges lanes.', ' - BIT_SYNC_GH_TOKEN (optional): a GitHub PAT or App token. Without it, sync pushes/PRs use the', ' default GITHUB_TOKEN, which does NOT trigger downstream workflow runs (loop-safe, but the', ' sync PR then gets no CI checks).', '', '2. Permissions -- both scaffolded workflows already declare `contents: write` and', ' `pull-requests: write`. Nothing to change unless an org-wide policy overrides workflow-level', ' permissions (e.g. a default of "Read repository contents permission" for the GITHUB_TOKEN).', '', '3. bit.cloud webhook -- create ONE webhook (org Settings > Webhooks) on the "Components > Export', ' succeeded" event:', ` URL: ${dispatchUrl}`, ' Headers: Authorization: Bearer <PAT>', ' Accept: application/vnd.github+json', ' Custom payload template:', ` ${WEBHOOK_PAYLOAD_TEMPLATE}`, ' WARNING: create this webhook fresh -- editing an existing webhook drops its custom headers', ' (known bit.cloud platform bug, verified 2026-07-29). If the URL, headers or template need to', ' change later, delete the webhook and create a new one rather than editing it.', '', '4. Checkout depth -- both scaffolded workflows already check out with `fetch-depth: 0`. This is', ' required: the reconciler reads full git history, and a shallow (--depth=1) clone fails sync', ' safely but uselessly (HALTED, no writes). Keep `fetch-depth: 0` if you customize the workflow.', ' Narrowing the REFSPEC is fine, though -- a single-branch clone is supported, because every', ' fetch sync performs names +refs/heads/*:refs/remotes/origin/* explicitly.', ''].join('\n');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
//# sourceMappingURL=init-scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_fs","data","_interopRequireDefault","require","_path","_githubClient","e","__esModule","default","WORKFLOW_RELATIVE_PATHS","exports","sync","path","join","release","BIT_SYNC_WORKFLOW_RAW","BIT_RELEASE_WORKFLOW_RAW","DEFAULT_BRANCH_MARKER","yamlSingleQuoted","value","replace","substituteDefaultBranch","raw","marker","defaultBranch","includes","Error","substitutedMarker","renderBitSyncWorkflow","renderBitReleaseWorkflow","writeIfAbsent","workspaceDir","relativePath","content","absPath","fs","existsSync","status","mkdirSync","dirname","recursive","writeFileSync","scaffoldWorkflowFiles","deriveOwnerRepo","remoteUrl","isGitHubRemote","undefined","ownerSlashRepo","parseGitHubRepo","owner","repo","split","WEBHOOK_PAYLOAD_TEMPLATE","renderInitChecklist","ownerRepo","repoSegment","dispatchUrl"],"sources":["init-scaffold.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport { parseGitHubRepo, isGitHubRemote } from './github-client';\n\n/**\n * `bit ci sync --init` — pure scaffolding logic, unit-testable without a live workspace/git checkout.\n *\n * TEMPLATE SOURCE OF TRUTH: `BIT_SYNC_WORKFLOW_RAW` / `BIT_RELEASE_WORKFLOW_RAW` are byte-for-byte\n * copies of the canonical templates in the `bit-git-sync` action workspace\n * (git-sync/git-sync/workflows/templates/) and MUST be kept in sync with them. The ONLY departure is\n * the two `DEFAULT_BRANCH_MARKER` substitution points; everything else — CHANGE-ME comments, the\n * placeholder action ref, `bit-sync/main` config defaults — stays untouched.\n */\n\n/** Where `scaffoldWorkflowFiles` writes each file, relative to the workspace root. */\nexport const WORKFLOW_RELATIVE_PATHS = {\n sync: path.join('.github', 'workflows', 'bit-sync.yml'),\n release: path.join('.github', 'workflows', 'bit-release.yml'),\n} as const;\n\nconst BIT_SYNC_WORKFLOW_RAW = `# Copy this file into your repository at \\`.github/workflows/bit-sync.yml\\`,\n# then review every line marked \\`CHANGE-ME\\` below -- the defaults assume a\n# repository whose default branch is \\`main\\` and a workspace at the repo root.\n#\n# See the \\`workflows.docs.mdx\\` customer setup guide (component\n# bitdev.git-sync/git-sync/workflows) for the full walk-through: the\n# bit.cloud webhook wiring that drives \\`repository_dispatch\\`, the required\n# secrets, and the \\`teambit.git/ci\\`.\\`sync\\` workspace config this workflow\n# acts on.\nname: bit-sync\non:\n repository_dispatch:\n # 'bit-export' is the real, empirically-verified bit.cloud webhook\n # contract: a single dispatch type discriminated by\n # client_payload.laneId (non-empty 'scope/lane' -> lane export, empty ->\n # main export). The other three types are forward-compat aliases the\n # event router also accepts, in case a webhook is configured to send the\n # originally-assumed, more granular dispatch types instead.\n types: [bit-export, bit-lane-export, bit-main-export, bit-lane-removed]\n schedule:\n # Scheduled reconcile. This is also the ONLY mechanism that picks up\n # lane deletions -- bit.cloud has no \"lane removed\" webhook event.\n - cron: '*/30 * * * *'\n push:\n # CHANGE-ME: \\`main\\` must be your repository's DEFAULT branch, and\n # \\`bit-sync/**\\` must cover your configured \\`sync.mainSyncBranch\\`. Pushes\n # to either are bit-sync's own output and must not re-trigger a sync.\n branches-ignore: [main, 'bit-sync/**']\n workflow_dispatch:\n inputs:\n lane: { description: Lane to sync (empty = all), required: false }\n\n# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations\n# created after Feb 2023, and anywhere the org/repo default is set to\n# \"Read repository contents permission\". Sync pushes branches and\n# opens/updates PRs, so both scopes must be declared explicitly -- without\n# this block the sync fails with a 403 on its first write.\npermissions:\n contents: write\n pull-requests: write\n\nconcurrency:\n # Serializes one lane's syncs so two runs never race on the same branch/PR.\n #\n # Each trigger needs a different expression, because the lane identity\n # arrives in a different place every time:\n # - repository_dispatch -> client_payload.laneId ('scope/lane'), or\n # client_payload.lane for the alias types\n # - push -> github.ref_name (the branch)\n # - workflow_dispatch -> the \\`lane\\` input\n # - schedule -> nothing lane-shaped; it reconciles everything\n #\n # github.ref_name is populated on schedule runs too (it is the default\n # branch there), so it may only be consulted AFTER the event name has been\n # checked. Reading it first -- as a bare \\`... || github.ref_name || 'all'\\`\n # chain does -- makes the final fallback unreachable and lumps every cron\n # run into the same group as pushes to the default branch.\n #\n # RESIDUAL RACE (accepted, by design): a webhook run groups as\n # 'scope/my-lane' while a push to that lane's branch groups as 'my-lane', so\n # two runs for the same lane can still overlap across those two triggers.\n # GitHub expressions have no substring/split function, and bit.cloud's\n # webhook payload template exposes only the full laneId, so the two keys\n # cannot be normalized into one here. This is safe rather than merely\n # tolerated: the reconciler is idempotent and the sync executor diffs\n # against converged state, so a second overlapping run finds nothing left to\n # do and no-ops. The concurrency group is an efficiency measure; it is not\n # what makes the sync correct.\n group: >-\n bit-sync-\\${{\n github.event_name == 'repository_dispatch' && (github.event.client_payload.laneId || github.event.client_payload.lane || 'main-export')\n || github.event_name == 'push' && github.ref_name\n || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane || 'reconcile-all')\n || 'reconcile-all' }}\n # Never cancel a sync mid-flight: it may already have pushed a branch and be\n # partway through opening or updating its PR.\n cancel-in-progress: false\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n # A PAT/App token so that pushes this workflow makes (e.g. a sync\n # PR branch) can themselves trigger CI. Pushes authenticated with\n # the default GITHUB_TOKEN do NOT trigger other workflow runs.\n token: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n - uses: bit-tasks/init@v2\n # Pinned to a commit SHA on purpose: this job holds write permission, and\n # a tag can move. Bump the SHA to take a new version of the action.\n - uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41\n with:\n # CHANGE-ME: keep this in sync with\n # \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\` in your workspace.jsonc\n # (the action's own default is shown here).\n main-sync-branch: bit-sync/main\n # Uncomment if your workspace.jsonc is NOT at the repository root --\n # the action chdirs here before running anything.\n # ws-dir: packages/my-workspace\n env:\n # Service account token. This account must be EXEMPT from the\n # org's change-request/merge block, or bit.cloud rejects the\n # sync-driven writes.\n BIT_CONFIG_ACCESS_TOKEN: \\${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}\n GITHUB_TOKEN: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n # Optional: git identity for the commits the sync creates. Defaults\n # to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.\n # GIT_USER_NAME: bit-sync[bot]\n # GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com\n`;\n\nconst BIT_RELEASE_WORKFLOW_RAW = `# Copy this file into your repository at \\`.github/workflows/bit-release.yml\\`,\n# then review every line marked \\`CHANGE-ME\\` below.\n#\n# See the \\`workflows.docs.mdx\\` customer setup guide (component\n# bitdev.git-sync/git-sync/workflows) for the full walk-through.\nname: bit-release\non:\n pull_request:\n types: [closed]\n # CHANGE-ME: your repository's DEFAULT branch. \\`bit ci merge\\` releases\n # from the default branch, so only PRs merged INTO it should trigger a\n # release -- a stacked PR landing on another feature branch has not\n # reached the default branch yet. The action's event router enforces the\n # same rule from \\`pull_request.base.ref\\`; this filter means a run is never\n # even queued for a non-default base.\n branches: [main]\n\n# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations\n# created after Feb 2023, and anywhere the org/repo default is set to\n# \"Read repository contents permission\". The release run pushes commits/tags\n# and updates PRs, so both scopes must be declared explicitly.\npermissions:\n contents: write\n pull-requests: write\n\nconcurrency:\n group: bit-release-\\${{ github.event.pull_request.number }}\n # Never cancel a release mid-flight.\n cancel-in-progress: false\n\njobs:\n release:\n runs-on: ubuntu-latest\n # Merged main-sync PRs must NOT trigger a release: the scope is already\n # ahead of this merge, there is nothing new to release. The event\n # router also skips this case (routePullRequest checks\n # prHeadRef === mainSyncBranch) -- this \\`if:\\` is defense in depth so the\n # job doesn't even start.\n # CHANGE-ME: the branch literal below MUST match your workspace's\n # configured \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\`.\n if: github.event.pull_request.merged == true && github.event.pull_request.head.ref != 'bit-sync/main'\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n token: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n - uses: bit-tasks/init@v2\n # Pinned to a commit SHA on purpose: this job holds write permission, and\n # a tag can move. Bump the SHA to take a new version of the action.\n - uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41\n with:\n # CHANGE-ME: keep this in sync with\n # \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\` AND with the \\`if:\\`\n # condition above.\n main-sync-branch: bit-sync/main\n # Uncomment if your workspace.jsonc is NOT at the repository root --\n # the action chdirs here before running anything.\n # ws-dir: packages/my-workspace\n env:\n BIT_CONFIG_ACCESS_TOKEN: \\${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}\n GITHUB_TOKEN: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n # Optional: git identity for the commits the release creates.\n # Defaults to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.\n # GIT_USER_NAME: bit-sync[bot]\n # GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com\n`;\n\n/**\n * The exact substring in each template that encodes the repository's default branch. Exact substring,\n * never a blanket `main` regex — that would also corrupt `bit-main-export` and `bit-sync/main`.\n */\nconst DEFAULT_BRANCH_MARKER = {\n sync: \"branches-ignore: [main, 'bit-sync/**']\",\n release: 'branches: [main]',\n} as const;\n\n/**\n * A YAML single-quoted scalar of `value` (the only escape inside single quotes is a doubled `''`).\n * Both substitution points land inside a YAML flow sequence, where `,` and `]` are structural — and a\n * branch name may legally contain both, so quoting is unconditional.\n */\nfunction yamlSingleQuoted(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Replace the default-branch literal inside `marker` with `defaultBranch`, inside `raw`. Throws when\n * the marker has drifted out of the template — a reworded canonical workflow must fail loudly, not\n * ship a scaffold that still says `main`.\n */\nfunction substituteDefaultBranch(raw: string, marker: string, defaultBranch: string): string {\n if (!raw.includes(marker)) {\n throw new Error(\n `init-scaffold: expected marker \"${marker}\" not found in its workflow template — the canonical ` +\n `template (bitdev.git-sync/git-sync/workflows) has drifted from BIT_SYNC_WORKFLOW_RAW / ` +\n `BIT_RELEASE_WORKFLOW_RAW; update the copy and this substitution point together`\n );\n }\n // Function replacements, not strings: `$&`/`$'`/`$$` are special in string replacements and `$` is\n // git-legal in a branch name. A function replacement is inserted verbatim.\n const substitutedMarker = marker.replace('main', () => yamlSingleQuoted(defaultBranch));\n return raw.replace(marker, () => substitutedMarker);\n}\n\n/** Render `bit-sync.yml` with the repository's actual default branch substituted. */\nexport function renderBitSyncWorkflow(defaultBranch: string): string {\n return substituteDefaultBranch(BIT_SYNC_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.sync, defaultBranch);\n}\n\n/** Render `bit-release.yml` with the repository's actual default branch substituted. */\nexport function renderBitReleaseWorkflow(defaultBranch: string): string {\n return substituteDefaultBranch(BIT_RELEASE_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.release, defaultBranch);\n}\n\nexport interface ScaffoldFileOutcome {\n /** relative to the workspace root, e.g. `.github/workflows/bit-sync.yml` */\n relativePath: string;\n status: 'written' | 'skipped';\n}\n\n// Never overwrites an existing file — `--init` must be safe to re-run after hand-edits.\nfunction writeIfAbsent(workspaceDir: string, relativePath: string, content: string): ScaffoldFileOutcome {\n const absPath = path.join(workspaceDir, relativePath);\n if (fs.existsSync(absPath)) {\n return { relativePath, status: 'skipped' };\n }\n fs.mkdirSync(path.dirname(absPath), { recursive: true });\n fs.writeFileSync(absPath, content);\n return { relativePath, status: 'written' };\n}\n\n/** Scaffold both workflow files under `workspaceDir`. Never overwrites; creates `.github/workflows/` as needed. */\nexport function scaffoldWorkflowFiles(workspaceDir: string, defaultBranch: string): ScaffoldFileOutcome[] {\n return [\n writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.sync, renderBitSyncWorkflow(defaultBranch)),\n writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.release, renderBitReleaseWorkflow(defaultBranch)),\n ];\n}\n\nexport interface OwnerRepo {\n owner: string;\n repo: string;\n}\n\n/**\n * Derive `{owner, repo}` from the `origin` remote when it is a parseable GitHub remote, else\n * undefined (the checklist falls back to a placeholder). Reuses `github-client.ts`'s parsing so the\n * two never disagree.\n */\nexport function deriveOwnerRepo(remoteUrl: string | undefined): OwnerRepo | undefined {\n if (!remoteUrl || !isGitHubRemote(remoteUrl)) return undefined;\n const ownerSlashRepo = parseGitHubRepo(remoteUrl);\n if (!ownerSlashRepo) return undefined;\n const [owner, repo] = ownerSlashRepo.split('/');\n if (!owner || !repo) return undefined;\n return { owner, repo };\n}\n\n/**\n * The bit.cloud custom webhook payload template, verified byte-perfect against a live delivery.\n * Lane vs. main export is discriminated by whether `laneId` is empty.\n */\nconst WEBHOOK_PAYLOAD_TEMPLATE =\n '{\"event_type\":\"bit-export\",\"client_payload\":{\"laneId\":\"{{laneId}}\",\"componentIds\":\"{{componentIds}}\",\"owner\":\"{{owner}}\",\"actor\":\"{{username}}\"}}';\n\n/**\n * The manual-steps checklist `bit ci sync --init` prints: everything the command itself cannot do\n * (secrets live in GitHub settings; the webhook lives on bit.cloud).\n */\nexport function renderInitChecklist(ownerRepo: OwnerRepo | undefined): string {\n const repoSegment = ownerRepo ? `${ownerRepo.owner}/${ownerRepo.repo}` : '<owner>/<repo>';\n const dispatchUrl = `https://api.github.com/repos/${repoSegment}/dispatches`;\n return [\n '',\n 'Remaining manual steps (bit ci sync --init only scaffolds files -- these need a human):',\n '',\n '1. Secrets -- add in the repository (or org) Settings > Secrets and variables > Actions:',\n ' - BIT_CONFIG_ACCESS_TOKEN (required): a bit.cloud service-account token with export rights on',\n \" this scope. This account must also be exempt from the org's\",\n ' change-request/merge block -- it is the only identity that merges lanes.',\n ' - BIT_SYNC_GH_TOKEN (optional): a GitHub PAT or App token. Without it, sync pushes/PRs use the',\n ' default GITHUB_TOKEN, which does NOT trigger downstream workflow runs (loop-safe, but the',\n ' sync PR then gets no CI checks).',\n '',\n '2. Permissions -- both scaffolded workflows already declare `contents: write` and',\n ' `pull-requests: write`. Nothing to change unless an org-wide policy overrides workflow-level',\n ' permissions (e.g. a default of \"Read repository contents permission\" for the GITHUB_TOKEN).',\n '',\n '3. bit.cloud webhook -- create ONE webhook (org Settings > Webhooks) on the \"Components > Export',\n ' succeeded\" event:',\n ` URL: ${dispatchUrl}`,\n ' Headers: Authorization: Bearer <PAT>',\n ' Accept: application/vnd.github+json',\n ' Custom payload template:',\n ` ${WEBHOOK_PAYLOAD_TEMPLATE}`,\n ' WARNING: create this webhook fresh -- editing an existing webhook drops its custom headers',\n ' (known bit.cloud platform bug, verified 2026-07-29). If the URL, headers or template need to',\n ' change later, delete the webhook and create a new one rather than editing it.',\n '',\n '4. Checkout depth -- both scaffolded workflows already check out with `fetch-depth: 0`. This is',\n ' required: the reconciler reads full git history, and a shallow (--depth=1) clone fails sync',\n ' safely but uselessly (HALTED, no writes). Keep `fetch-depth: 0` if you customize the workflow.',\n ' Narrowing the REFSPEC is fine, though -- a single-branch clone is supported, because every',\n ' fetch sync performs names +refs/heads/*:refs/remotes/origin/* explicitly.',\n '',\n ].join('\\n');\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAAA,IAAA;EAAA,MAAAC,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAH,GAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAG,MAAA;EAAA,MAAAH,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAC,KAAA,YAAAA,CAAA;IAAA,OAAAH,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAI,cAAA;EAAA,MAAAJ,IAAA,GAAAE,OAAA;EAAAE,aAAA,YAAAA,CAAA;IAAA,OAAAJ,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAAkE,SAAAC,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAElE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACO,MAAMG,uBAAuB,GAAAC,OAAA,CAAAD,uBAAA,GAAG;EACrCE,IAAI,EAAEC,eAAI,CAACC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,CAAC;EACvDC,OAAO,EAAEF,eAAI,CAACC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB;AAC9D,CAAU;AAEV,MAAME,qBAAqB,GAAG;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAED,MAAMC,wBAAwB,GAAG;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA;AACA;AACA,MAAMC,qBAAqB,GAAG;EAC5BN,IAAI,EAAE,wCAAwC;EAC9CG,OAAO,EAAE;AACX,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA,SAASI,gBAAgBA,CAACC,KAAa,EAAU;EAC/C,OAAO,IAAIA,KAAK,CAACC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;AACzC;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASC,uBAAuBA,CAACC,GAAW,EAAEC,MAAc,EAAEC,aAAqB,EAAU;EAC3F,IAAI,CAACF,GAAG,CAACG,QAAQ,CAACF,MAAM,CAAC,EAAE;IACzB,MAAM,IAAIG,KAAK,CACb,mCAAmCH,MAAM,uDAAuD,GAC9F,yFAAyF,GACzF,gFACJ,CAAC;EACH;EACA;EACA;EACA,MAAMI,iBAAiB,GAAGJ,MAAM,CAACH,OAAO,CAAC,MAAM,EAAE,MAAMF,gBAAgB,CAACM,aAAa,CAAC,CAAC;EACvF,OAAOF,GAAG,CAACF,OAAO,CAACG,MAAM,EAAE,MAAMI,iBAAiB,CAAC;AACrD;;AAEA;AACO,SAASC,qBAAqBA,CAACJ,aAAqB,EAAU;EACnE,OAAOH,uBAAuB,CAACN,qBAAqB,EAAEE,qBAAqB,CAACN,IAAI,EAAEa,aAAa,CAAC;AAClG;;AAEA;AACO,SAASK,wBAAwBA,CAACL,aAAqB,EAAU;EACtE,OAAOH,uBAAuB,CAACL,wBAAwB,EAAEC,qBAAqB,CAACH,OAAO,EAAEU,aAAa,CAAC;AACxG;AAQA;AACA,SAASM,aAAaA,CAACC,YAAoB,EAAEC,YAAoB,EAAEC,OAAe,EAAuB;EACvG,MAAMC,OAAO,GAAGtB,eAAI,CAACC,IAAI,CAACkB,YAAY,EAAEC,YAAY,CAAC;EACrD,IAAIG,aAAE,CAACC,UAAU,CAACF,OAAO,CAAC,EAAE;IAC1B,OAAO;MAAEF,YAAY;MAAEK,MAAM,EAAE;IAAU,CAAC;EAC5C;EACAF,aAAE,CAACG,SAAS,CAAC1B,eAAI,CAAC2B,OAAO,CAACL,OAAO,CAAC,EAAE;IAAEM,SAAS,EAAE;EAAK,CAAC,CAAC;EACxDL,aAAE,CAACM,aAAa,CAACP,OAAO,EAAED,OAAO,CAAC;EAClC,OAAO;IAAED,YAAY;IAAEK,MAAM,EAAE;EAAU,CAAC;AAC5C;;AAEA;AACO,SAASK,qBAAqBA,CAACX,YAAoB,EAAEP,aAAqB,EAAyB;EACxG,OAAO,CACLM,aAAa,CAACC,YAAY,EAAEtB,uBAAuB,CAACE,IAAI,EAAEiB,qBAAqB,CAACJ,aAAa,CAAC,CAAC,EAC/FM,aAAa,CAACC,YAAY,EAAEtB,uBAAuB,CAACK,OAAO,EAAEe,wBAAwB,CAACL,aAAa,CAAC,CAAC,CACtG;AACH;AAOA;AACA;AACA;AACA;AACA;AACO,SAASmB,eAAeA,CAACC,SAA6B,EAAyB;EACpF,IAAI,CAACA,SAAS,IAAI,CAAC,IAAAC,8BAAc,EAACD,SAAS,CAAC,EAAE,OAAOE,SAAS;EAC9D,MAAMC,cAAc,GAAG,IAAAC,+BAAe,EAACJ,SAAS,CAAC;EACjD,IAAI,CAACG,cAAc,EAAE,OAAOD,SAAS;EACrC,MAAM,CAACG,KAAK,EAAEC,IAAI,CAAC,GAAGH,cAAc,CAACI,KAAK,CAAC,GAAG,CAAC;EAC/C,IAAI,CAACF,KAAK,IAAI,CAACC,IAAI,EAAE,OAAOJ,SAAS;EACrC,OAAO;IAAEG,KAAK;IAAEC;EAAK,CAAC;AACxB;;AAEA;AACA;AACA;AACA;AACA,MAAME,wBAAwB,GAC5B,mJAAmJ;;AAErJ;AACA;AACA;AACA;AACO,SAASC,mBAAmBA,CAACC,SAAgC,EAAU;EAC5E,MAAMC,WAAW,GAAGD,SAAS,GAAG,GAAGA,SAAS,CAACL,KAAK,IAAIK,SAAS,CAACJ,IAAI,EAAE,GAAG,gBAAgB;EACzF,MAAMM,WAAW,GAAG,gCAAgCD,WAAW,aAAa;EAC5E,OAAO,CACL,EAAE,EACF,yFAAyF,EACzF,EAAE,EACF,0FAA0F,EAC1F,kGAAkG,EAClG,kEAAkE,EAClE,+EAA+E,EAC/E,mGAAmG,EACnG,gGAAgG,EAChG,uCAAuC,EACvC,EAAE,EACF,mFAAmF,EACnF,iGAAiG,EACjG,gGAAgG,EAChG,EAAE,EACF,kGAAkG,EAClG,sBAAsB,EACtB,eAAeC,WAAW,EAAE,EAC5B,yCAAyC,EACzC,iDAAiD,EACjD,6BAA6B,EAC7B,MAAMJ,wBAAwB,EAAE,EAChC,+FAA+F,EAC/F,iGAAiG,EACjG,kFAAkF,EAClF,EAAE,EACF,iGAAiG,EACjG,gGAAgG,EAChG,mGAAmG,EACnG,+FAA+F,EAC/F,8EAA8E,EAC9E,EAAE,CACH,CAACvC,IAAI,CAAC,IAAI,CAAC;AACd","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _chai() {
|
|
4
|
+
const data = require("chai");
|
|
5
|
+
_chai = function () {
|
|
6
|
+
return data;
|
|
7
|
+
};
|
|
8
|
+
return data;
|
|
9
|
+
}
|
|
10
|
+
function _fs() {
|
|
11
|
+
const data = _interopRequireDefault(require("fs"));
|
|
12
|
+
_fs = function () {
|
|
13
|
+
return data;
|
|
14
|
+
};
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
function _os() {
|
|
18
|
+
const data = _interopRequireDefault(require("os"));
|
|
19
|
+
_os = function () {
|
|
20
|
+
return data;
|
|
21
|
+
};
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
function _path() {
|
|
25
|
+
const data = _interopRequireDefault(require("path"));
|
|
26
|
+
_path = function () {
|
|
27
|
+
return data;
|
|
28
|
+
};
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
function _yaml() {
|
|
32
|
+
const data = _interopRequireDefault(require("yaml"));
|
|
33
|
+
_yaml = function () {
|
|
34
|
+
return data;
|
|
35
|
+
};
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
function _initScaffold() {
|
|
39
|
+
const data = require("./init-scaffold");
|
|
40
|
+
_initScaffold = function () {
|
|
41
|
+
return data;
|
|
42
|
+
};
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
46
|
+
const yamlLoad = src => _yaml().default.parse(src);
|
|
47
|
+
|
|
48
|
+
/** YAML 1.1-schema parsers resolve the plain scalar key `on` to `true`, so accept either key. */
|
|
49
|
+
function onSection(rendered) {
|
|
50
|
+
const doc = yamlLoad(rendered);
|
|
51
|
+
return doc.on ?? doc[true];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parsed, not substring-matched: the substitution point sits inside a YAML flow sequence, where `,` and
|
|
56
|
+
* `]` are structural and a substring match is blind to exactly the damage an unquoted value does. `$` is
|
|
57
|
+
* git-legal and `$&`/`$'`/`$$` are special in a string replacement, so those must arrive verbatim too.
|
|
58
|
+
*/
|
|
59
|
+
const BRANCHES = ['main', 'develop', 'release/2026', 'release/main', 'a,b]c', "it's/a-branch", "a$'b", 'a$&b', 'a$$b', 'a$`b']; // prettier-ignore
|
|
60
|
+
|
|
61
|
+
describe('init-scaffold', () => {
|
|
62
|
+
describe('the rendered workflows', () => {
|
|
63
|
+
it('put the default branch in as exactly one element of each template’s branch filter', () => {
|
|
64
|
+
BRANCHES.forEach(branch => {
|
|
65
|
+
(0, _chai().expect)(onSection((0, _initScaffold().renderBitSyncWorkflow)(branch)).push['branches-ignore'], branch).to.deep.equal([branch, 'bit-sync/**']);
|
|
66
|
+
(0, _chai().expect)(onSection((0, _initScaffold().renderBitReleaseWorkflow)(branch)).pull_request.branches, branch).to.deep.equal([branch]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
it('quote the value rather than escape it, and touch nothing else in the document', () => {
|
|
70
|
+
const sync = (0, _initScaffold().renderBitSyncWorkflow)('develop');
|
|
71
|
+
const release = (0, _initScaffold().renderBitReleaseWorkflow)('develop');
|
|
72
|
+
(0, _chai().expect)(sync).to.contain("branches-ignore: ['develop', 'bit-sync/**']");
|
|
73
|
+
(0, _chai().expect)(release).to.contain("branches: ['develop']");
|
|
74
|
+
// a naive replace-all of "main" would rewrite every one of these
|
|
75
|
+
(0, _chai().expect)(sync).to.contain('main-sync-branch: bit-sync/main');
|
|
76
|
+
(0, _chai().expect)(sync).to.contain('bit-main-export');
|
|
77
|
+
(0, _chai().expect)(sync).to.contain('main export)');
|
|
78
|
+
(0, _chai().expect)(sync).to.contain('${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}');
|
|
79
|
+
(0, _chai().expect)(release).to.contain("github.event.pull_request.head.ref != 'bit-sync/main'");
|
|
80
|
+
(0, _chai().expect)(release).to.contain('main-sync-branch: bit-sync/main');
|
|
81
|
+
// YAML single-quote escaping, and the rest of the document intact
|
|
82
|
+
(0, _chai().expect)((0, _initScaffold().renderBitSyncWorkflow)("it's/a-branch")).to.contain("'it''s/a-branch'");
|
|
83
|
+
(0, _chai().expect)(yamlLoad((0, _initScaffold().renderBitSyncWorkflow)('release/main')).jobs).to.be.an('object');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('scaffoldWorkflowFiles', () => {
|
|
87
|
+
let workspaceDir;
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
workspaceDir = _fs().default.mkdtempSync(_path().default.join(_os().default.tmpdir(), 'init-scaffold-'));
|
|
90
|
+
});
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
_fs().default.rmSync(workspaceDir, {
|
|
93
|
+
recursive: true,
|
|
94
|
+
force: true
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it('writes both workflow files with the default branch substituted, creating .github/workflows/', () => {
|
|
98
|
+
(0, _chai().expect)((0, _initScaffold().scaffoldWorkflowFiles)(workspaceDir, 'develop')).to.deep.equal([{
|
|
99
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.sync,
|
|
100
|
+
status: 'written'
|
|
101
|
+
}, {
|
|
102
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.release,
|
|
103
|
+
status: 'written'
|
|
104
|
+
}]);
|
|
105
|
+
const read = rel => _fs().default.readFileSync(_path().default.join(workspaceDir, rel), 'utf8');
|
|
106
|
+
(0, _chai().expect)(read(_initScaffold().WORKFLOW_RELATIVE_PATHS.sync)).to.contain("branches-ignore: ['develop', 'bit-sync/**']");
|
|
107
|
+
(0, _chai().expect)(read(_initScaffold().WORKFLOW_RELATIVE_PATHS.release)).to.contain("branches: ['develop']");
|
|
108
|
+
});
|
|
109
|
+
it('is idempotent: a second run skips both files rather than overwriting them', () => {
|
|
110
|
+
(0, _initScaffold().scaffoldWorkflowFiles)(workspaceDir, 'develop');
|
|
111
|
+
(0, _chai().expect)((0, _initScaffold().scaffoldWorkflowFiles)(workspaceDir, 'develop')).to.deep.equal([{
|
|
112
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.sync,
|
|
113
|
+
status: 'skipped'
|
|
114
|
+
}, {
|
|
115
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.release,
|
|
116
|
+
status: 'skipped'
|
|
117
|
+
}]);
|
|
118
|
+
});
|
|
119
|
+
it('skips only the file that already exists, writing the other one', () => {
|
|
120
|
+
const syncAbsPath = _path().default.join(workspaceDir, _initScaffold().WORKFLOW_RELATIVE_PATHS.sync);
|
|
121
|
+
_fs().default.mkdirSync(_path().default.dirname(syncAbsPath), {
|
|
122
|
+
recursive: true
|
|
123
|
+
});
|
|
124
|
+
_fs().default.writeFileSync(syncAbsPath, 'hand-edited content, do not touch\n');
|
|
125
|
+
(0, _chai().expect)((0, _initScaffold().scaffoldWorkflowFiles)(workspaceDir, 'develop')).to.deep.equal([{
|
|
126
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.sync,
|
|
127
|
+
status: 'skipped'
|
|
128
|
+
}, {
|
|
129
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.release,
|
|
130
|
+
status: 'written'
|
|
131
|
+
}]);
|
|
132
|
+
(0, _chai().expect)(_fs().default.readFileSync(syncAbsPath, 'utf8')).to.equal('hand-edited content, do not touch\n');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe('deriveOwnerRepo', () => {
|
|
136
|
+
const REMOTES = [['git@github.com:acme/shop.git', {
|
|
137
|
+
owner: 'acme',
|
|
138
|
+
repo: 'shop'
|
|
139
|
+
}], ['https://github.com/acme/shop.git', {
|
|
140
|
+
owner: 'acme',
|
|
141
|
+
repo: 'shop'
|
|
142
|
+
}], ['https://github.com/acme/shop', {
|
|
143
|
+
owner: 'acme',
|
|
144
|
+
repo: 'shop'
|
|
145
|
+
}], ['https://gitlab.com/acme/shop.git', undefined],
|
|
146
|
+
// a local/bare remote has no host at all
|
|
147
|
+
['/path/to/bare.git', undefined], ['file:///path/to/bare.git', undefined], [undefined, undefined]];
|
|
148
|
+
it('parses a GitHub remote in either form, and nothing else', () => {
|
|
149
|
+
REMOTES.forEach(([remote, expected]) => (0, _chai().expect)((0, _initScaffold().deriveOwnerRepo)(remote), String(remote)).to.deep.equal(expected));
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('renderInitChecklist', () => {
|
|
153
|
+
it('renders a real dispatch URL when owner/repo were derived, a placeholder when not', () => {
|
|
154
|
+
const derived = (0, _initScaffold().renderInitChecklist)({
|
|
155
|
+
owner: 'acme',
|
|
156
|
+
repo: 'shop'
|
|
157
|
+
});
|
|
158
|
+
(0, _chai().expect)(derived).to.contain('https://api.github.com/repos/acme/shop/dispatches');
|
|
159
|
+
(0, _chai().expect)(derived).to.not.contain('<owner>/<repo>');
|
|
160
|
+
(0, _chai().expect)((0, _initScaffold().renderInitChecklist)(undefined)).to.contain('https://api.github.com/repos/<owner>/<repo>/dispatches');
|
|
161
|
+
});
|
|
162
|
+
it('always includes the secrets, permissions, webhook and fetch-depth checklist items', () => {
|
|
163
|
+
const checklist = (0, _initScaffold().renderInitChecklist)(undefined);
|
|
164
|
+
['BIT_CONFIG_ACCESS_TOKEN', 'BIT_SYNC_GH_TOKEN', 'contents: write', 'Components > Export', 'Authorization: Bearer <PAT>', 'Accept: application/vnd.github+json', '"event_type":"bit-export"', '{{laneId}}', 'drops its custom headers', 'fetch-depth: 0'].forEach(item => (0, _chai().expect)(checklist, item).to.contain(item));
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
//# sourceMappingURL=init-scaffold.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_chai","data","require","_fs","_interopRequireDefault","_os","_path","_yaml","_initScaffold","e","__esModule","default","yamlLoad","src","YAML","parse","onSection","rendered","doc","on","BRANCHES","describe","it","forEach","branch","expect","renderBitSyncWorkflow","push","to","deep","equal","renderBitReleaseWorkflow","pull_request","branches","sync","release","contain","jobs","be","an","workspaceDir","beforeEach","fs","mkdtempSync","path","join","os","tmpdir","afterEach","rmSync","recursive","force","scaffoldWorkflowFiles","relativePath","WORKFLOW_RELATIVE_PATHS","status","read","rel","readFileSync","syncAbsPath","mkdirSync","dirname","writeFileSync","REMOTES","owner","repo","undefined","remote","expected","deriveOwnerRepo","String","derived","renderInitChecklist","not","checklist","item"],"sources":["init-scaffold.spec.ts"],"sourcesContent":["import { expect } from 'chai';\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport YAML from 'yaml';\nimport {\n WORKFLOW_RELATIVE_PATHS,\n renderBitSyncWorkflow,\n renderBitReleaseWorkflow,\n scaffoldWorkflowFiles,\n deriveOwnerRepo,\n renderInitChecklist,\n} from './init-scaffold';\n\nconst yamlLoad = (src: string) => YAML.parse(src);\n\n/** YAML 1.1-schema parsers resolve the plain scalar key `on` to `true`, so accept either key. */\nfunction onSection(rendered: string): any {\n const doc = yamlLoad(rendered) as any;\n return doc.on ?? doc[true as any];\n}\n\n/**\n * Parsed, not substring-matched: the substitution point sits inside a YAML flow sequence, where `,` and\n * `]` are structural and a substring match is blind to exactly the damage an unquoted value does. `$` is\n * git-legal and `$&`/`$'`/`$$` are special in a string replacement, so those must arrive verbatim too.\n */\nconst BRANCHES = ['main', 'develop', 'release/2026', 'release/main', 'a,b]c', \"it's/a-branch\", \"a$'b\", 'a$&b', 'a$$b', 'a$`b']; // prettier-ignore\n\ndescribe('init-scaffold', () => {\n describe('the rendered workflows', () => {\n it('put the default branch in as exactly one element of each template’s branch filter', () => {\n BRANCHES.forEach((branch) => {\n expect(onSection(renderBitSyncWorkflow(branch)).push['branches-ignore'], branch).to.deep.equal([\n branch,\n 'bit-sync/**',\n ]);\n expect(onSection(renderBitReleaseWorkflow(branch)).pull_request.branches, branch).to.deep.equal([branch]);\n });\n });\n\n it('quote the value rather than escape it, and touch nothing else in the document', () => {\n const sync = renderBitSyncWorkflow('develop');\n const release = renderBitReleaseWorkflow('develop');\n expect(sync).to.contain(\"branches-ignore: ['develop', 'bit-sync/**']\");\n expect(release).to.contain(\"branches: ['develop']\");\n // a naive replace-all of \"main\" would rewrite every one of these\n expect(sync).to.contain('main-sync-branch: bit-sync/main');\n expect(sync).to.contain('bit-main-export');\n expect(sync).to.contain('main export)');\n expect(sync).to.contain('${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}');\n expect(release).to.contain(\"github.event.pull_request.head.ref != 'bit-sync/main'\");\n expect(release).to.contain('main-sync-branch: bit-sync/main');\n // YAML single-quote escaping, and the rest of the document intact\n expect(renderBitSyncWorkflow(\"it's/a-branch\")).to.contain(\"'it''s/a-branch'\");\n expect(yamlLoad(renderBitSyncWorkflow('release/main')).jobs).to.be.an('object');\n });\n });\n\n describe('scaffoldWorkflowFiles', () => {\n let workspaceDir: string;\n\n beforeEach(() => {\n workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'init-scaffold-'));\n });\n\n afterEach(() => {\n fs.rmSync(workspaceDir, { recursive: true, force: true });\n });\n\n it('writes both workflow files with the default branch substituted, creating .github/workflows/', () => {\n expect(scaffoldWorkflowFiles(workspaceDir, 'develop')).to.deep.equal([\n { relativePath: WORKFLOW_RELATIVE_PATHS.sync, status: 'written' },\n { relativePath: WORKFLOW_RELATIVE_PATHS.release, status: 'written' },\n ]);\n const read = (rel: string) => fs.readFileSync(path.join(workspaceDir, rel), 'utf8');\n expect(read(WORKFLOW_RELATIVE_PATHS.sync)).to.contain(\"branches-ignore: ['develop', 'bit-sync/**']\");\n expect(read(WORKFLOW_RELATIVE_PATHS.release)).to.contain(\"branches: ['develop']\");\n });\n\n it('is idempotent: a second run skips both files rather than overwriting them', () => {\n scaffoldWorkflowFiles(workspaceDir, 'develop');\n expect(scaffoldWorkflowFiles(workspaceDir, 'develop')).to.deep.equal([\n { relativePath: WORKFLOW_RELATIVE_PATHS.sync, status: 'skipped' },\n { relativePath: WORKFLOW_RELATIVE_PATHS.release, status: 'skipped' },\n ]);\n });\n\n it('skips only the file that already exists, writing the other one', () => {\n const syncAbsPath = path.join(workspaceDir, WORKFLOW_RELATIVE_PATHS.sync);\n fs.mkdirSync(path.dirname(syncAbsPath), { recursive: true });\n fs.writeFileSync(syncAbsPath, 'hand-edited content, do not touch\\n');\n expect(scaffoldWorkflowFiles(workspaceDir, 'develop')).to.deep.equal([\n { relativePath: WORKFLOW_RELATIVE_PATHS.sync, status: 'skipped' },\n { relativePath: WORKFLOW_RELATIVE_PATHS.release, status: 'written' },\n ]);\n expect(fs.readFileSync(syncAbsPath, 'utf8')).to.equal('hand-edited content, do not touch\\n');\n });\n });\n\n describe('deriveOwnerRepo', () => {\n const REMOTES: Array<[string | undefined, { owner: string; repo: string } | undefined]> = [\n ['git@github.com:acme/shop.git', { owner: 'acme', repo: 'shop' }],\n ['https://github.com/acme/shop.git', { owner: 'acme', repo: 'shop' }],\n ['https://github.com/acme/shop', { owner: 'acme', repo: 'shop' }],\n ['https://gitlab.com/acme/shop.git', undefined],\n // a local/bare remote has no host at all\n ['/path/to/bare.git', undefined],\n ['file:///path/to/bare.git', undefined],\n [undefined, undefined],\n ];\n\n it('parses a GitHub remote in either form, and nothing else', () => {\n REMOTES.forEach(([remote, expected]) => expect(deriveOwnerRepo(remote), String(remote)).to.deep.equal(expected));\n });\n });\n\n describe('renderInitChecklist', () => {\n it('renders a real dispatch URL when owner/repo were derived, a placeholder when not', () => {\n const derived = renderInitChecklist({ owner: 'acme', repo: 'shop' });\n expect(derived).to.contain('https://api.github.com/repos/acme/shop/dispatches');\n expect(derived).to.not.contain('<owner>/<repo>');\n expect(renderInitChecklist(undefined)).to.contain('https://api.github.com/repos/<owner>/<repo>/dispatches');\n });\n\n it('always includes the secrets, permissions, webhook and fetch-depth checklist items', () => {\n const checklist = renderInitChecklist(undefined);\n [\n 'BIT_CONFIG_ACCESS_TOKEN',\n 'BIT_SYNC_GH_TOKEN',\n 'contents: write',\n 'Components > Export',\n 'Authorization: Bearer <PAT>',\n 'Accept: application/vnd.github+json',\n '\"event_type\":\"bit-export\"',\n '{{laneId}}',\n 'drops its custom headers',\n 'fetch-depth: 0',\n ].forEach((item) => expect(checklist, item).to.contain(item));\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|