@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,476 @@
|
|
|
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 _gitOps() {
|
|
11
|
+
const data = require("./git-ops");
|
|
12
|
+
_gitOps = function () {
|
|
13
|
+
return data;
|
|
14
|
+
};
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
18
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
19
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
20
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
21
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
22
|
+
/** A read-only stand-in for `git config --get`. There is no setter: nothing may write the config. */
|
|
23
|
+
function fakeConfig(initial = {}) {
|
|
24
|
+
return {
|
|
25
|
+
get: async key => initial[key]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const DEV = {
|
|
29
|
+
'user.email': 'dev@example.com',
|
|
30
|
+
'user.name': 'A Developer'
|
|
31
|
+
};
|
|
32
|
+
const BOT_ENV = {
|
|
33
|
+
GIT_USER_NAME: 'Release Bot',
|
|
34
|
+
GIT_USER_EMAIL: 'release@acme.example'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Precedence: a configured identity outranks the environment (the var means "when there is nobody
|
|
39
|
+
* else"), and an empty value counts as missing on both sides, because git treats it that way too.
|
|
40
|
+
* `env` is always passed explicitly — otherwise a developer who exports GIT_USER_NAME fails the suite.
|
|
41
|
+
*/
|
|
42
|
+
const IDENTITY = [{
|
|
43
|
+
name: 'a fresh CI checkout has neither half',
|
|
44
|
+
userName: _gitOps().DEFAULT_GIT_USER_NAME,
|
|
45
|
+
email: _gitOps().DEFAULT_GIT_USER_EMAIL
|
|
46
|
+
}, {
|
|
47
|
+
name: 'only the email is configured',
|
|
48
|
+
config: {
|
|
49
|
+
'user.email': 'dev@example.com'
|
|
50
|
+
},
|
|
51
|
+
userName: _gitOps().DEFAULT_GIT_USER_NAME,
|
|
52
|
+
email: 'dev@example.com'
|
|
53
|
+
}, {
|
|
54
|
+
name: 'only the name is configured',
|
|
55
|
+
config: {
|
|
56
|
+
'user.name': 'A Developer'
|
|
57
|
+
},
|
|
58
|
+
userName: 'A Developer',
|
|
59
|
+
email: _gitOps().DEFAULT_GIT_USER_EMAIL
|
|
60
|
+
}, {
|
|
61
|
+
name: 'both are already configured',
|
|
62
|
+
config: DEV,
|
|
63
|
+
userName: 'A Developer',
|
|
64
|
+
email: 'dev@example.com'
|
|
65
|
+
}, {
|
|
66
|
+
name: 'the configured values are empty, which git reads as missing',
|
|
67
|
+
config: {
|
|
68
|
+
'user.email': '',
|
|
69
|
+
'user.name': ''
|
|
70
|
+
},
|
|
71
|
+
userName: _gitOps().DEFAULT_GIT_USER_NAME,
|
|
72
|
+
email: _gitOps().DEFAULT_GIT_USER_EMAIL
|
|
73
|
+
}, {
|
|
74
|
+
name: 'the env vars supply an identity git has none of',
|
|
75
|
+
env: BOT_ENV,
|
|
76
|
+
userName: 'Release Bot',
|
|
77
|
+
email: 'release@acme.example'
|
|
78
|
+
}, {
|
|
79
|
+
name: 'the env vars are IGNORED because git already has an identity — a local run is never rewritten',
|
|
80
|
+
config: DEV,
|
|
81
|
+
env: BOT_ENV,
|
|
82
|
+
userName: 'A Developer',
|
|
83
|
+
email: 'dev@example.com'
|
|
84
|
+
}, {
|
|
85
|
+
name: 'only one env var is set, so each key resolves independently',
|
|
86
|
+
env: {
|
|
87
|
+
GIT_USER_NAME: 'Release Bot'
|
|
88
|
+
},
|
|
89
|
+
userName: 'Release Bot',
|
|
90
|
+
email: _gitOps().DEFAULT_GIT_USER_EMAIL
|
|
91
|
+
}, {
|
|
92
|
+
name: 'an env var is set to empty, which falls back as if unset',
|
|
93
|
+
env: {
|
|
94
|
+
GIT_USER_NAME: '',
|
|
95
|
+
GIT_USER_EMAIL: ''
|
|
96
|
+
},
|
|
97
|
+
userName: _gitOps().DEFAULT_GIT_USER_NAME,
|
|
98
|
+
email: _gitOps().DEFAULT_GIT_USER_EMAIL
|
|
99
|
+
}];
|
|
100
|
+
describe('resolveGitIdentity', () => {
|
|
101
|
+
IDENTITY.forEach(({
|
|
102
|
+
name,
|
|
103
|
+
config,
|
|
104
|
+
env,
|
|
105
|
+
userName,
|
|
106
|
+
email
|
|
107
|
+
}) => {
|
|
108
|
+
it(`resolves the identity when ${name}`, async () => {
|
|
109
|
+
(0, _chai().expect)(await (0, _gitOps().resolveGitIdentity)(fakeConfig(config), env ?? {})).to.deep.equal({
|
|
110
|
+
name: userName,
|
|
111
|
+
email
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Pinning the literals (not the constants) makes cross-repo drift show up as a failing test.
|
|
117
|
+
it('defaults to the identity the scaffolded workflows and the action both document', () => {
|
|
118
|
+
(0, _chai().expect)(_gitOps().DEFAULT_GIT_USER_NAME).to.equal('bit-sync[bot]');
|
|
119
|
+
(0, _chai().expect)(_gitOps().DEFAULT_GIT_USER_EMAIL).to.equal('bit-sync[bot]@users.noreply.github.com');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// The identity rides on the invocation. A `git config user.*` write would outlive the run and
|
|
124
|
+
// re-author the developer's own later commits in that repository as the bot.
|
|
125
|
+
describe('commitWithIdentity', () => {
|
|
126
|
+
function recorder(config, env = {}) {
|
|
127
|
+
const argv = [];
|
|
128
|
+
const run = async args => {
|
|
129
|
+
argv.push(args);
|
|
130
|
+
return '';
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
argv,
|
|
134
|
+
deps: {
|
|
135
|
+
run,
|
|
136
|
+
io: fakeConfig(config),
|
|
137
|
+
env
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
it('carries the identity as -c on the commit itself, and writes no config at all', async () => {
|
|
142
|
+
const {
|
|
143
|
+
argv,
|
|
144
|
+
deps
|
|
145
|
+
} = recorder();
|
|
146
|
+
await (0, _gitOps().commitWithIdentity)('chore(bit-sync): sync git to latest main scope versions', deps);
|
|
147
|
+
(0, _chai().expect)(argv).to.deep.equal([['-c', `user.name=${_gitOps().DEFAULT_GIT_USER_NAME}`, '-c', `user.email=${_gitOps().DEFAULT_GIT_USER_EMAIL}`, 'commit', '-m', 'chore(bit-sync): sync git to latest main scope versions']]);
|
|
148
|
+
// no `config`/`addConfig` call of any shape — and GitConfigIO has no setter to make one with
|
|
149
|
+
(0, _chai().expect)(argv.some(args => args.includes('config') || args.includes('addConfig'))).to.equal(false);
|
|
150
|
+
});
|
|
151
|
+
it('honours a configured identity and appends the extra flags after the message', async () => {
|
|
152
|
+
const {
|
|
153
|
+
argv,
|
|
154
|
+
deps
|
|
155
|
+
} = recorder(DEV, BOT_ENV);
|
|
156
|
+
await (0, _gitOps().commitWithIdentity)('chore(bit-sync): sync lane', _objectSpread(_objectSpread({}, deps), {}, {
|
|
157
|
+
extraArgs: ['--allow-empty']
|
|
158
|
+
}));
|
|
159
|
+
(0, _chai().expect)(argv).to.deep.equal([['-c', 'user.name=A Developer', '-c', 'user.email=dev@example.com', 'commit', '-m', 'chore(bit-sync): sync lane', '--allow-empty']]);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe('fetchRemoteHeads', () => {
|
|
163
|
+
it('passes the all-heads refspec explicitly, overriding whatever the checkout configured', async () => {
|
|
164
|
+
const calls = [];
|
|
165
|
+
await (0, _gitOps().fetchRemoteHeads)(async args => {
|
|
166
|
+
calls.push(args);
|
|
167
|
+
});
|
|
168
|
+
(0, _chai().expect)(calls).to.deep.equal([['fetch', 'origin', _gitOps().ALL_HEADS_REFSPEC]]);
|
|
169
|
+
(0, _chai().expect)(_gitOps().ALL_HEADS_REFSPEC).to.equal('+refs/heads/*:refs/remotes/origin/*');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// A branch deletion is the one irreversible thing the reconciler does, and every input to it was read
|
|
174
|
+
// from refs fetched once per run — so the tip is re-read here, and leased on in the push itself.
|
|
175
|
+
describe('refetchBranchTip', () => {
|
|
176
|
+
it('re-fetches exactly that one head, then reads the remote-tracking ref it just wrote', async () => {
|
|
177
|
+
const argv = [];
|
|
178
|
+
const tip = await (0, _gitOps().refetchBranchTip)('my-lane', async args => {
|
|
179
|
+
argv.push(args);
|
|
180
|
+
return args[0] === 'rev-parse' ? 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1\n' : '';
|
|
181
|
+
});
|
|
182
|
+
(0, _chai().expect)(argv).to.deep.equal([['fetch', 'origin', '+refs/heads/my-lane:refs/remotes/origin/my-lane'], ['rev-parse', 'refs/remotes/origin/my-lane']]);
|
|
183
|
+
(0, _chai().expect)(tip).to.equal('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1');
|
|
184
|
+
(0, _chai().expect)((0, _gitOps().singleHeadRefspec)('my-lane')).to.equal('+refs/heads/my-lane:refs/remotes/origin/my-lane');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// A stale sha here would license deleting a commit that is no longer the tip; undefined keeps the branch.
|
|
188
|
+
it('is undefined — never a stale sha — when the fetch fails, the ref is gone, or the output is empty', async () => {
|
|
189
|
+
(0, _chai().expect)(await (0, _gitOps().refetchBranchTip)('my-lane', async args => {
|
|
190
|
+
if (args[0] === 'fetch') throw new Error("couldn't find remote ref refs/heads/my-lane");
|
|
191
|
+
return 'staaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaale';
|
|
192
|
+
})).to.equal(undefined);
|
|
193
|
+
(0, _chai().expect)(await (0, _gitOps().refetchBranchTip)('my-lane', async () => '')).to.equal(undefined);
|
|
194
|
+
(0, _chai().expect)(await (0, _gitOps().refetchBranchTip)('my-lane', async () => undefined)).to.equal(undefined);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('deleteBranchArgs', () => {
|
|
198
|
+
const SHA = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1';
|
|
199
|
+
it('leases the delete on the expected sha, keeping the full delete refspec', () => {
|
|
200
|
+
(0, _chai().expect)((0, _gitOps().deleteBranchArgs)('my-lane', SHA)).to.deep.equal(['origin', `--force-with-lease=refs/heads/my-lane:${SHA}`, ':refs/heads/my-lane']);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// A bare `--delete <branch>` or a short lease ref would let a branch name be read as an option.
|
|
204
|
+
it('never passes the branch name as a bare argument', () => {
|
|
205
|
+
(0, _chai().expect)((0, _gitOps().deleteBranchArgs)('-hostile', SHA)).to.deep.equal(['origin', `--force-with-lease=refs/heads/-hostile:${SHA}`, ':refs/heads/-hostile']);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Every refusal wording must read as a race. The third one is why this row exists: the local/file
|
|
209
|
+
// transport says "incorrect old value provided", and matching only the first two reported a refused
|
|
210
|
+
// delete as a failed one — caught by CI, whose e2e remotes use that transport.
|
|
211
|
+
it('recognizes every lease-refusal wording, and not an unrelated push failure', () => {
|
|
212
|
+
(0, _chai().expect)((0, _gitOps().isStaleLeaseRejection)('! [rejected] (delete) -> my-lane (stale info)')).to.equal(true);
|
|
213
|
+
(0, _chai().expect)((0, _gitOps().isStaleLeaseRejection)(`remote: error: cannot lock ref 'refs/heads/my-lane': is at ${SHA} but expected abc123`)).to.equal(true);
|
|
214
|
+
(0, _chai().expect)((0, _gitOps().isStaleLeaseRejection)('!\t:refs/heads/race-lane\t[remote rejected] (incorrect old value provided)')).to.equal(true);
|
|
215
|
+
(0, _chai().expect)((0, _gitOps().isStaleLeaseRejection)('remote: GH006: Protected branch update failed')).to.equal(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
describe('isNonContentPath', () => {
|
|
219
|
+
it('covers the two paths the executors never treat as workspace content, and nothing adjacent', () => {
|
|
220
|
+
['.bit', '.bit/objects/ab/cdef', 'node_modules/lodash/index.js'].forEach(p => (0, _chai().expect)((0, _gitOps().isNonContentPath)(p), p).to.equal(true));
|
|
221
|
+
['.bitmap', 'node_modules_backup/x'].forEach(p => (0, _chai().expect)((0, _gitOps().isNonContentPath)(p), p).to.equal(false));
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Both paths must end with the same index: everything staged except `.bit` / `node_modules`. The
|
|
226
|
+
// fallback exists because `git add` refuses a pathspec naming an ignored path.
|
|
227
|
+
describe('addAllExceptScopeAndModules', () => {
|
|
228
|
+
/** Records argv; `failFastPath` models git refusing the `:(exclude)` form (both trees gitignored). */
|
|
229
|
+
function recorder(failFastPath = false) {
|
|
230
|
+
const argv = [];
|
|
231
|
+
const run = async args => {
|
|
232
|
+
argv.push(args);
|
|
233
|
+
if (failFastPath && args.some(arg => arg.startsWith(':(exclude)'))) {
|
|
234
|
+
throw new Error('The following paths are ignored by one of your .gitignore files:\n.bit\nnode_modules');
|
|
235
|
+
}
|
|
236
|
+
return undefined;
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
argv,
|
|
240
|
+
run
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
it('excludes both trees by pathspec in one command, so git never traverses them', async () => {
|
|
244
|
+
const {
|
|
245
|
+
argv,
|
|
246
|
+
run
|
|
247
|
+
} = recorder();
|
|
248
|
+
await (0, _gitOps().addAllExceptScopeAndModules)(run);
|
|
249
|
+
(0, _chai().expect)(argv).to.deep.equal([['add', '-A', '--', '.', ':(exclude).bit', ':(exclude)node_modules']]);
|
|
250
|
+
});
|
|
251
|
+
it('falls back to add-then-reset when git refuses the pathspecs', async () => {
|
|
252
|
+
const {
|
|
253
|
+
argv,
|
|
254
|
+
run
|
|
255
|
+
} = recorder(true);
|
|
256
|
+
await (0, _gitOps().addAllExceptScopeAndModules)(run);
|
|
257
|
+
(0, _chai().expect)(argv).to.deep.equal([['add', '-A', '--', '.', ':(exclude).bit', ':(exclude)node_modules'], ['add', '-A', '--', '.'], ['reset', '-q', '--', '.bit', 'node_modules']]);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Either shape must cover every excluded path, or a new entry silently stops being excluded.
|
|
261
|
+
it('names every excluded path on both paths', async () => {
|
|
262
|
+
const fast = recorder();
|
|
263
|
+
await (0, _gitOps().addAllExceptScopeAndModules)(fast.run);
|
|
264
|
+
const fallback = recorder(true);
|
|
265
|
+
await (0, _gitOps().addAllExceptScopeAndModules)(fallback.run);
|
|
266
|
+
const reset = fallback.argv.find(args => args[0] === 'reset');
|
|
267
|
+
_gitOps().SYNC_EXCLUDED_PATHS.forEach(path => {
|
|
268
|
+
(0, _chai().expect)(fast.argv[0], path).to.include(`:(exclude)${path}`);
|
|
269
|
+
(0, _chai().expect)(reset, path).to.include(path);
|
|
270
|
+
});
|
|
271
|
+
(0, _chai().expect)(reset.slice(3)).to.deep.equal(_gitOps().SYNC_EXCLUDED_PATHS);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// A wrong default-branch name does not fail loudly — it protects the wrong branch. A slashed name is a
|
|
276
|
+
// NAME, not a path to shorten, and an unrecognised shape must yield undefined so the caller can fall back.
|
|
277
|
+
const ORIGIN_HEAD = [['refs/remotes/origin/main\n', 'main'], ['refs/remotes/origin/master', 'master'], ['refs/remotes/origin/release/main', 'release/main'], ['refs/remotes/origin/team/x/main\n', 'team/x/main'], ['refs/remotes/origin/feature/a/b/c', 'feature/a/b/c'], ['refs/heads/main', undefined], ['fatal: ref refs/remotes/origin/HEAD is not a symbolic ref', undefined], ['', undefined], [' ', undefined], ['refs/remotes/origin/', undefined]];
|
|
278
|
+
describe('parseOriginHeadRef', () => {
|
|
279
|
+
it('reads the branch name whole, or nothing at all', () => {
|
|
280
|
+
ORIGIN_HEAD.forEach(([input, expected]) => (0, _chai().expect)((0, _gitOps().parseOriginHeadRef)(input), JSON.stringify(input)).to.equal(expected));
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// These rows assert the ORDER of the three steps: a clean after the reload would leave the workspace
|
|
285
|
+
// holding a `.bitmap` view of a tree that no longer exists.
|
|
286
|
+
describe('checkoutPristine', () => {
|
|
287
|
+
const CLEAN = 'clean -fd -e .bit -e node_modules';
|
|
288
|
+
|
|
289
|
+
/** Records git argv and the reload interleaved, so ordering between the two is assertable. */
|
|
290
|
+
function recorder(revParse = 'abc123\n', containedIn = ' origin/some-branch\n') {
|
|
291
|
+
const steps = [];
|
|
292
|
+
const run = async args => {
|
|
293
|
+
steps.push(args.join(' '));
|
|
294
|
+
// model the REAL runner: a missing ref resolves with empty output rather than rejecting
|
|
295
|
+
if (args[0] === 'rev-parse') return revParse;
|
|
296
|
+
if (args[0] === 'branch') return containedIn;
|
|
297
|
+
return undefined;
|
|
298
|
+
};
|
|
299
|
+
const reload = async () => {
|
|
300
|
+
steps.push('<reload>');
|
|
301
|
+
};
|
|
302
|
+
return {
|
|
303
|
+
steps,
|
|
304
|
+
run,
|
|
305
|
+
reload
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
it('with a startPoint: proves the reset is safe, creates-or-resets the branch, cleans, reloads', async () => {
|
|
309
|
+
const {
|
|
310
|
+
steps,
|
|
311
|
+
run,
|
|
312
|
+
reload
|
|
313
|
+
} = recorder();
|
|
314
|
+
await (0, _gitOps().checkoutPristine)('bit-sync/main', 'origin/main', reload, run);
|
|
315
|
+
(0, _chai().expect)(steps).to.deep.equal(['rev-parse --verify --quiet refs/heads/bit-sync/main', 'branch -r --contains refs/heads/bit-sync/main', 'checkout -f -B bit-sync/main origin/main', CLEAN, '<reload>']);
|
|
316
|
+
});
|
|
317
|
+
it('without a startPoint: plain forced switch to an existing branch, then cleans, then reloads', async () => {
|
|
318
|
+
const {
|
|
319
|
+
steps,
|
|
320
|
+
run,
|
|
321
|
+
reload
|
|
322
|
+
} = recorder();
|
|
323
|
+
await (0, _gitOps().checkoutPristine)('main', undefined, reload, run);
|
|
324
|
+
(0, _chai().expect)(steps).to.deep.equal(['checkout -f main', CLEAN, '<reload>']);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// `checkout -B` moves an existing local ref; unpushed commits would be orphaned to the reflog.
|
|
328
|
+
it('REFUSES to reset a local branch whose commits no remote contains, before touching the tree', async () => {
|
|
329
|
+
const {
|
|
330
|
+
steps,
|
|
331
|
+
run,
|
|
332
|
+
reload
|
|
333
|
+
} = recorder('abc123\n', '\n');
|
|
334
|
+
let message = '';
|
|
335
|
+
await (0, _gitOps().checkoutPristine)('my-lane', 'origin/my-lane', reload, run).catch(e => {
|
|
336
|
+
message = e.message;
|
|
337
|
+
});
|
|
338
|
+
(0, _chai().expect)(message).to.contain('local branch "my-lane" has commits that no remote branch contains');
|
|
339
|
+
(0, _chai().expect)(steps.some(step => step.startsWith('checkout'))).to.equal(false);
|
|
340
|
+
(0, _chai().expect)(steps.some(step => step.startsWith('clean'))).to.equal(false);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Remote containment admits both legitimate reset shapes; start-point ancestry wrongly refuses one.
|
|
344
|
+
it('allows the reset when any remote branch contains the local tip — even a different one than the start point', async () => {
|
|
345
|
+
const {
|
|
346
|
+
steps,
|
|
347
|
+
run,
|
|
348
|
+
reload
|
|
349
|
+
} = recorder('abc123\n', ' origin/policy-lane\n');
|
|
350
|
+
await (0, _gitOps().checkoutPristine)('policy-lane', 'origin/main', reload, run);
|
|
351
|
+
(0, _chai().expect)(steps.some(step => step === 'checkout -f -B policy-lane origin/main')).to.equal(true);
|
|
352
|
+
});
|
|
353
|
+
it('skips the safety proof when the branch does not exist locally (every CI clone)', async () => {
|
|
354
|
+
const {
|
|
355
|
+
steps,
|
|
356
|
+
run,
|
|
357
|
+
reload
|
|
358
|
+
} = recorder('');
|
|
359
|
+
await (0, _gitOps().checkoutPristine)('my-lane', 'origin/my-lane', reload, run);
|
|
360
|
+
(0, _chai().expect)(steps.filter(step => step.startsWith('branch -r'))).to.deep.equal([]);
|
|
361
|
+
(0, _chai().expect)(steps.some(step => step === 'checkout -f -B my-lane origin/my-lane')).to.equal(true);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
describe('parseLsRemoteSymref / remoteHeadBranch', () => {
|
|
365
|
+
const SYMREF_OUT = 'ref: refs/heads/main\tHEAD\n1234abcd\tHEAD\n';
|
|
366
|
+
it('reads the branch out of the symref line, keeping a slashed name whole', () => {
|
|
367
|
+
(0, _chai().expect)((0, _gitOps().parseLsRemoteSymref)(SYMREF_OUT)).to.equal('main');
|
|
368
|
+
// same slash discipline as parseOriginHeadRef: `release/main` is a NAME, not a path to shorten
|
|
369
|
+
(0, _chai().expect)((0, _gitOps().parseLsRemoteSymref)('ref: refs/heads/release/main\tHEAD\nabc\tHEAD\n')).to.equal('release/main');
|
|
370
|
+
});
|
|
371
|
+
it('returns undefined when the server omits the symref line (protocol v0), rather than guessing', () => {
|
|
372
|
+
(0, _chai().expect)((0, _gitOps().parseLsRemoteSymref)('1234abcd\tHEAD\n')).to.equal(undefined);
|
|
373
|
+
(0, _chai().expect)((0, _gitOps().parseLsRemoteSymref)('')).to.equal(undefined);
|
|
374
|
+
});
|
|
375
|
+
it('asks the remote, not the local refs — and swallows an offline failure into undefined', async () => {
|
|
376
|
+
const argv = [];
|
|
377
|
+
(0, _chai().expect)(await (0, _gitOps().remoteHeadBranch)(async args => {
|
|
378
|
+
argv.push(args);
|
|
379
|
+
return SYMREF_OUT;
|
|
380
|
+
})).to.equal('main');
|
|
381
|
+
(0, _chai().expect)(argv).to.deep.equal([['ls-remote', '--symref', 'origin', 'HEAD']]);
|
|
382
|
+
(0, _chai().expect)(await (0, _gitOps().remoteHeadBranch)(async () => {
|
|
383
|
+
throw new Error('could not read from remote repository');
|
|
384
|
+
})).to.equal(undefined);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* The default branch is derived from the remote, not from validated config, so the primitive that
|
|
390
|
+
* builds the checkout argv is the last line of defence against an option-like name.
|
|
391
|
+
*/
|
|
392
|
+
describe('assertCheckoutableBranch', () => {
|
|
393
|
+
it('refuses a name git would read as an option, or reject as a ref, before any argv is built', () => {
|
|
394
|
+
['-x', '--force', '', 'has space', 'a..b', 'refs/heads/main', 'ends.lock'].forEach(name => {
|
|
395
|
+
(0, _chai().expect)(() => (0, _gitOps().assertCheckoutableBranch)(name), name).to.throw(/cannot check out/);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
it('accepts ordinary names, slashed names included', () => {
|
|
399
|
+
['main', 'master', 'release/main', 'feature/a-b_c', 'trunk'].forEach(name => {
|
|
400
|
+
(0, _chai().expect)(() => (0, _gitOps().assertCheckoutableBranch)(name), name).to.not.throw();
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
it('is enforced by checkoutPristine itself, so every caller inherits it', async () => {
|
|
404
|
+
const steps = [];
|
|
405
|
+
let threw = false;
|
|
406
|
+
await (0, _gitOps().checkoutPristine)('-x', undefined, async () => {}, async args => {
|
|
407
|
+
steps.push(args.join(' '));
|
|
408
|
+
}).catch(() => {
|
|
409
|
+
threw = true;
|
|
410
|
+
});
|
|
411
|
+
(0, _chai().expect)(threw).to.equal(true);
|
|
412
|
+
(0, _chai().expect)(steps).to.deep.equal([]);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
describe('localBranchExists', () => {
|
|
416
|
+
// simple-git's `raw` can resolve with empty output on non-zero exits, so all three missing-branch
|
|
417
|
+
// runner behaviors (empty resolve, undefined resolve, rejection) must be false.
|
|
418
|
+
it('asks for the LOCAL ref quietly, and is true only when that ref prints a sha', async () => {
|
|
419
|
+
const argv = [];
|
|
420
|
+
(0, _chai().expect)(await (0, _gitOps().localBranchExists)('main', async args => {
|
|
421
|
+
argv.push(args);
|
|
422
|
+
return 'abc123\n';
|
|
423
|
+
})).to.equal(true);
|
|
424
|
+
(0, _chai().expect)(argv).to.deep.equal([['rev-parse', '--verify', '--quiet', 'refs/heads/main']]);
|
|
425
|
+
(0, _chai().expect)(await (0, _gitOps().localBranchExists)('main', async () => '')).to.equal(false);
|
|
426
|
+
(0, _chai().expect)(await (0, _gitOps().localBranchExists)('main', async () => undefined)).to.equal(false);
|
|
427
|
+
(0, _chai().expect)(await (0, _gitOps().localBranchExists)('main', async () => {
|
|
428
|
+
throw new Error('exit 1');
|
|
429
|
+
})).to.equal(false);
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// Each shape is wrong in the other environment: `-B origin/<branch>` in a developer's repo resets
|
|
434
|
+
// their default branch; a plain switch in a detached-HEAD CI checkout fails outright.
|
|
435
|
+
describe('checkoutPristineRestore', () => {
|
|
436
|
+
function restoreRecorder(localBranchIsPresent) {
|
|
437
|
+
const steps = [];
|
|
438
|
+
const run = async args => {
|
|
439
|
+
steps.push(args.join(' '));
|
|
440
|
+
// model the REAL runner: a missing ref resolves with empty output rather than rejecting
|
|
441
|
+
if (args[0] === 'rev-parse') return localBranchIsPresent ? 'abc123\n' : '';
|
|
442
|
+
if (args[0] === 'branch') return ' origin/some-branch\n';
|
|
443
|
+
return undefined;
|
|
444
|
+
};
|
|
445
|
+
const reload = async () => {
|
|
446
|
+
steps.push('<reload>');
|
|
447
|
+
};
|
|
448
|
+
return {
|
|
449
|
+
steps,
|
|
450
|
+
run,
|
|
451
|
+
reload
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
it('local branch present: plain forced switch — never a -B that would reset it to origin', async () => {
|
|
455
|
+
const {
|
|
456
|
+
steps,
|
|
457
|
+
run,
|
|
458
|
+
reload
|
|
459
|
+
} = restoreRecorder(true);
|
|
460
|
+
await (0, _gitOps().checkoutPristineRestore)('main', reload, run);
|
|
461
|
+
(0, _chai().expect)(steps).to.deep.equal(['rev-parse --verify --quiet refs/heads/main', 'checkout -f main', 'clean -fd -e .bit -e node_modules', '<reload>']);
|
|
462
|
+
});
|
|
463
|
+
it('local branch absent (detached-HEAD CI): forks it from origin/<branch> instead of failing', async () => {
|
|
464
|
+
const {
|
|
465
|
+
steps,
|
|
466
|
+
run,
|
|
467
|
+
reload
|
|
468
|
+
} = restoreRecorder(false);
|
|
469
|
+
await (0, _gitOps().checkoutPristineRestore)('main', reload, run);
|
|
470
|
+
// the second rev-parse is checkoutPristine's own reset guard re-proving what restore just learned —
|
|
471
|
+
// redundant but harmless, and cheaper than giving the guard a bypass parameter
|
|
472
|
+
(0, _chai().expect)(steps).to.deep.equal(['rev-parse --verify --quiet refs/heads/main', 'rev-parse --verify --quiet refs/heads/main', 'checkout -f -B main origin/main', 'clean -fd -e .bit -e node_modules', '<reload>']);
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
//# sourceMappingURL=git-ops.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_chai","data","require","_gitOps","ownKeys","e","r","t","Object","keys","getOwnPropertySymbols","o","filter","getOwnPropertyDescriptor","enumerable","push","apply","_objectSpread","arguments","length","forEach","_defineProperty","getOwnPropertyDescriptors","defineProperties","defineProperty","_toPropertyKey","value","configurable","writable","i","_toPrimitive","Symbol","toPrimitive","call","TypeError","String","Number","fakeConfig","initial","get","key","DEV","BOT_ENV","GIT_USER_NAME","GIT_USER_EMAIL","IDENTITY","name","userName","DEFAULT_GIT_USER_NAME","email","DEFAULT_GIT_USER_EMAIL","config","env","describe","it","expect","resolveGitIdentity","to","deep","equal","recorder","argv","run","args","deps","io","commitWithIdentity","some","includes","extraArgs","calls","fetchRemoteHeads","ALL_HEADS_REFSPEC","tip","refetchBranchTip","singleHeadRefspec","Error","undefined","SHA","deleteBranchArgs","isStaleLeaseRejection","p","isNonContentPath","failFastPath","arg","startsWith","addAllExceptScopeAndModules","fast","fallback","reset","find","SYNC_EXCLUDED_PATHS","path","include","slice","ORIGIN_HEAD","input","expected","parseOriginHeadRef","JSON","stringify","CLEAN","revParse","containedIn","steps","join","reload","checkoutPristine","message","catch","contain","step","SYMREF_OUT","parseLsRemoteSymref","remoteHeadBranch","assertCheckoutableBranch","throw","not","threw","localBranchExists","restoreRecorder","localBranchIsPresent","checkoutPristineRestore"],"sources":["git-ops.spec.ts"],"sourcesContent":["import { expect } from 'chai';\nimport type { GitConfigIO } from './git-ops';\nimport {\n ALL_HEADS_REFSPEC,\n SYNC_EXCLUDED_PATHS,\n addAllExceptScopeAndModules,\n assertCheckoutableBranch,\n checkoutPristine,\n checkoutPristineRestore,\n commitWithIdentity,\n deleteBranchArgs,\n localBranchExists,\n DEFAULT_GIT_USER_EMAIL,\n DEFAULT_GIT_USER_NAME,\n fetchRemoteHeads,\n isNonContentPath,\n isStaleLeaseRejection,\n parseLsRemoteSymref,\n parseOriginHeadRef,\n refetchBranchTip,\n remoteHeadBranch,\n resolveGitIdentity,\n singleHeadRefspec,\n} from './git-ops';\n\n/** A read-only stand-in for `git config --get`. There is no setter: nothing may write the config. */\nfunction fakeConfig(initial: Record<string, string> = {}): GitConfigIO {\n return { get: async (key) => initial[key] };\n}\n\nconst DEV = { 'user.email': 'dev@example.com', 'user.name': 'A Developer' };\nconst BOT_ENV = { GIT_USER_NAME: 'Release Bot', GIT_USER_EMAIL: 'release@acme.example' };\n\n/**\n * Precedence: a configured identity outranks the environment (the var means \"when there is nobody\n * else\"), and an empty value counts as missing on both sides, because git treats it that way too.\n * `env` is always passed explicitly — otherwise a developer who exports GIT_USER_NAME fails the suite.\n */\nconst IDENTITY: Array<{\n name: string;\n config?: Record<string, string>;\n env?: Record<string, string>;\n userName: string;\n email: string;\n}> = [\n { name: 'a fresh CI checkout has neither half', userName: DEFAULT_GIT_USER_NAME, email: DEFAULT_GIT_USER_EMAIL },\n {\n name: 'only the email is configured',\n config: { 'user.email': 'dev@example.com' },\n userName: DEFAULT_GIT_USER_NAME,\n email: 'dev@example.com',\n },\n {\n name: 'only the name is configured',\n config: { 'user.name': 'A Developer' },\n userName: 'A Developer',\n email: DEFAULT_GIT_USER_EMAIL,\n },\n { name: 'both are already configured', config: DEV, userName: 'A Developer', email: 'dev@example.com' },\n {\n name: 'the configured values are empty, which git reads as missing',\n config: { 'user.email': '', 'user.name': '' },\n userName: DEFAULT_GIT_USER_NAME,\n email: DEFAULT_GIT_USER_EMAIL,\n },\n {\n name: 'the env vars supply an identity git has none of',\n env: BOT_ENV,\n userName: 'Release Bot',\n email: 'release@acme.example',\n },\n {\n name: 'the env vars are IGNORED because git already has an identity — a local run is never rewritten',\n config: DEV,\n env: BOT_ENV,\n userName: 'A Developer',\n email: 'dev@example.com',\n },\n {\n name: 'only one env var is set, so each key resolves independently',\n env: { GIT_USER_NAME: 'Release Bot' },\n userName: 'Release Bot',\n email: DEFAULT_GIT_USER_EMAIL,\n },\n {\n name: 'an env var is set to empty, which falls back as if unset',\n env: { GIT_USER_NAME: '', GIT_USER_EMAIL: '' },\n userName: DEFAULT_GIT_USER_NAME,\n email: DEFAULT_GIT_USER_EMAIL,\n },\n];\n\ndescribe('resolveGitIdentity', () => {\n IDENTITY.forEach(({ name, config, env, userName, email }) => {\n it(`resolves the identity when ${name}`, async () => {\n expect(await resolveGitIdentity(fakeConfig(config), env ?? {})).to.deep.equal({ name: userName, email });\n });\n });\n\n // Pinning the literals (not the constants) makes cross-repo drift show up as a failing test.\n it('defaults to the identity the scaffolded workflows and the action both document', () => {\n expect(DEFAULT_GIT_USER_NAME).to.equal('bit-sync[bot]');\n expect(DEFAULT_GIT_USER_EMAIL).to.equal('bit-sync[bot]@users.noreply.github.com');\n });\n});\n\n// The identity rides on the invocation. A `git config user.*` write would outlive the run and\n// re-author the developer's own later commits in that repository as the bot.\ndescribe('commitWithIdentity', () => {\n function recorder(config?: Record<string, string>, env: Record<string, string> = {}) {\n const argv: string[][] = [];\n const run = async (args: string[]) => {\n argv.push(args);\n return '';\n };\n return { argv, deps: { run, io: fakeConfig(config), env } };\n }\n\n it('carries the identity as -c on the commit itself, and writes no config at all', async () => {\n const { argv, deps } = recorder();\n await commitWithIdentity('chore(bit-sync): sync git to latest main scope versions', deps);\n expect(argv).to.deep.equal([\n [\n '-c',\n `user.name=${DEFAULT_GIT_USER_NAME}`,\n '-c',\n `user.email=${DEFAULT_GIT_USER_EMAIL}`,\n 'commit',\n '-m',\n 'chore(bit-sync): sync git to latest main scope versions',\n ],\n ]);\n // no `config`/`addConfig` call of any shape — and GitConfigIO has no setter to make one with\n expect(argv.some((args) => args.includes('config') || args.includes('addConfig'))).to.equal(false);\n });\n\n it('honours a configured identity and appends the extra flags after the message', async () => {\n const { argv, deps } = recorder(DEV, BOT_ENV);\n await commitWithIdentity('chore(bit-sync): sync lane', { ...deps, extraArgs: ['--allow-empty'] });\n expect(argv).to.deep.equal([\n [\n '-c',\n 'user.name=A Developer',\n '-c',\n 'user.email=dev@example.com',\n 'commit',\n '-m',\n 'chore(bit-sync): sync lane',\n '--allow-empty',\n ],\n ]);\n });\n});\n\ndescribe('fetchRemoteHeads', () => {\n it('passes the all-heads refspec explicitly, overriding whatever the checkout configured', async () => {\n const calls: string[][] = [];\n await fetchRemoteHeads(async (args) => {\n calls.push(args);\n });\n expect(calls).to.deep.equal([['fetch', 'origin', ALL_HEADS_REFSPEC]]);\n expect(ALL_HEADS_REFSPEC).to.equal('+refs/heads/*:refs/remotes/origin/*');\n });\n});\n\n// A branch deletion is the one irreversible thing the reconciler does, and every input to it was read\n// from refs fetched once per run — so the tip is re-read here, and leased on in the push itself.\ndescribe('refetchBranchTip', () => {\n it('re-fetches exactly that one head, then reads the remote-tracking ref it just wrote', async () => {\n const argv: string[][] = [];\n const tip = await refetchBranchTip('my-lane', async (args) => {\n argv.push(args);\n return args[0] === 'rev-parse' ? 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1\\n' : '';\n });\n expect(argv).to.deep.equal([\n ['fetch', 'origin', '+refs/heads/my-lane:refs/remotes/origin/my-lane'],\n ['rev-parse', 'refs/remotes/origin/my-lane'],\n ]);\n expect(tip).to.equal('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1');\n expect(singleHeadRefspec('my-lane')).to.equal('+refs/heads/my-lane:refs/remotes/origin/my-lane');\n });\n\n // A stale sha here would license deleting a commit that is no longer the tip; undefined keeps the branch.\n it('is undefined — never a stale sha — when the fetch fails, the ref is gone, or the output is empty', async () => {\n expect(\n await refetchBranchTip('my-lane', async (args) => {\n if (args[0] === 'fetch') throw new Error(\"couldn't find remote ref refs/heads/my-lane\");\n return 'staaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaale';\n })\n ).to.equal(undefined);\n expect(await refetchBranchTip('my-lane', async () => '')).to.equal(undefined);\n expect(await refetchBranchTip('my-lane', async () => undefined)).to.equal(undefined);\n });\n});\n\ndescribe('deleteBranchArgs', () => {\n const SHA = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1';\n\n it('leases the delete on the expected sha, keeping the full delete refspec', () => {\n expect(deleteBranchArgs('my-lane', SHA)).to.deep.equal([\n 'origin',\n `--force-with-lease=refs/heads/my-lane:${SHA}`,\n ':refs/heads/my-lane',\n ]);\n });\n\n // A bare `--delete <branch>` or a short lease ref would let a branch name be read as an option.\n it('never passes the branch name as a bare argument', () => {\n expect(deleteBranchArgs('-hostile', SHA)).to.deep.equal([\n 'origin',\n `--force-with-lease=refs/heads/-hostile:${SHA}`,\n ':refs/heads/-hostile',\n ]);\n });\n\n // Every refusal wording must read as a race. The third one is why this row exists: the local/file\n // transport says \"incorrect old value provided\", and matching only the first two reported a refused\n // delete as a failed one — caught by CI, whose e2e remotes use that transport.\n it('recognizes every lease-refusal wording, and not an unrelated push failure', () => {\n expect(isStaleLeaseRejection('! [rejected] (delete) -> my-lane (stale info)')).to.equal(true);\n expect(\n isStaleLeaseRejection(`remote: error: cannot lock ref 'refs/heads/my-lane': is at ${SHA} but expected abc123`)\n ).to.equal(true);\n expect(\n isStaleLeaseRejection('!\\t:refs/heads/race-lane\\t[remote rejected] (incorrect old value provided)')\n ).to.equal(true);\n expect(isStaleLeaseRejection('remote: GH006: Protected branch update failed')).to.equal(false);\n });\n});\n\ndescribe('isNonContentPath', () => {\n it('covers the two paths the executors never treat as workspace content, and nothing adjacent', () => {\n ['.bit', '.bit/objects/ab/cdef', 'node_modules/lodash/index.js'].forEach((p) =>\n expect(isNonContentPath(p), p).to.equal(true)\n );\n ['.bitmap', 'node_modules_backup/x'].forEach((p) => expect(isNonContentPath(p), p).to.equal(false));\n });\n});\n\n// Both paths must end with the same index: everything staged except `.bit` / `node_modules`. The\n// fallback exists because `git add` refuses a pathspec naming an ignored path.\ndescribe('addAllExceptScopeAndModules', () => {\n /** Records argv; `failFastPath` models git refusing the `:(exclude)` form (both trees gitignored). */\n function recorder(failFastPath = false) {\n const argv: string[][] = [];\n const run = async (args: string[]) => {\n argv.push(args);\n if (failFastPath && args.some((arg) => arg.startsWith(':(exclude)'))) {\n throw new Error('The following paths are ignored by one of your .gitignore files:\\n.bit\\nnode_modules');\n }\n return undefined;\n };\n return { argv, run };\n }\n\n it('excludes both trees by pathspec in one command, so git never traverses them', async () => {\n const { argv, run } = recorder();\n await addAllExceptScopeAndModules(run);\n expect(argv).to.deep.equal([['add', '-A', '--', '.', ':(exclude).bit', ':(exclude)node_modules']]);\n });\n\n it('falls back to add-then-reset when git refuses the pathspecs', async () => {\n const { argv, run } = recorder(true);\n await addAllExceptScopeAndModules(run);\n expect(argv).to.deep.equal([\n ['add', '-A', '--', '.', ':(exclude).bit', ':(exclude)node_modules'],\n ['add', '-A', '--', '.'],\n ['reset', '-q', '--', '.bit', 'node_modules'],\n ]);\n });\n\n // Either shape must cover every excluded path, or a new entry silently stops being excluded.\n it('names every excluded path on both paths', async () => {\n const fast = recorder();\n await addAllExceptScopeAndModules(fast.run);\n const fallback = recorder(true);\n await addAllExceptScopeAndModules(fallback.run);\n const reset = fallback.argv.find((args) => args[0] === 'reset') as string[];\n SYNC_EXCLUDED_PATHS.forEach((path) => {\n expect(fast.argv[0], path).to.include(`:(exclude)${path}`);\n expect(reset, path).to.include(path);\n });\n expect(reset.slice(3)).to.deep.equal(SYNC_EXCLUDED_PATHS);\n });\n});\n\n// A wrong default-branch name does not fail loudly — it protects the wrong branch. A slashed name is a\n// NAME, not a path to shorten, and an unrecognised shape must yield undefined so the caller can fall back.\nconst ORIGIN_HEAD: Array<[string, string | undefined]> = [\n ['refs/remotes/origin/main\\n', 'main'],\n ['refs/remotes/origin/master', 'master'],\n ['refs/remotes/origin/release/main', 'release/main'],\n ['refs/remotes/origin/team/x/main\\n', 'team/x/main'],\n ['refs/remotes/origin/feature/a/b/c', 'feature/a/b/c'],\n ['refs/heads/main', undefined],\n ['fatal: ref refs/remotes/origin/HEAD is not a symbolic ref', undefined],\n ['', undefined],\n [' ', undefined],\n ['refs/remotes/origin/', undefined],\n];\n\ndescribe('parseOriginHeadRef', () => {\n it('reads the branch name whole, or nothing at all', () => {\n ORIGIN_HEAD.forEach(([input, expected]) =>\n expect(parseOriginHeadRef(input), JSON.stringify(input)).to.equal(expected)\n );\n });\n});\n\n// These rows assert the ORDER of the three steps: a clean after the reload would leave the workspace\n// holding a `.bitmap` view of a tree that no longer exists.\ndescribe('checkoutPristine', () => {\n const CLEAN = 'clean -fd -e .bit -e node_modules';\n\n /** Records git argv and the reload interleaved, so ordering between the two is assertable. */\n function recorder(revParse = 'abc123\\n', containedIn = ' origin/some-branch\\n') {\n const steps: string[] = [];\n const run = async (args: string[]) => {\n steps.push(args.join(' '));\n // model the REAL runner: a missing ref resolves with empty output rather than rejecting\n if (args[0] === 'rev-parse') return revParse;\n if (args[0] === 'branch') return containedIn;\n return undefined;\n };\n const reload = async () => {\n steps.push('<reload>');\n };\n return { steps, run, reload };\n }\n\n it('with a startPoint: proves the reset is safe, creates-or-resets the branch, cleans, reloads', async () => {\n const { steps, run, reload } = recorder();\n await checkoutPristine('bit-sync/main', 'origin/main', reload, run);\n expect(steps).to.deep.equal([\n 'rev-parse --verify --quiet refs/heads/bit-sync/main',\n 'branch -r --contains refs/heads/bit-sync/main',\n 'checkout -f -B bit-sync/main origin/main',\n CLEAN,\n '<reload>',\n ]);\n });\n\n it('without a startPoint: plain forced switch to an existing branch, then cleans, then reloads', async () => {\n const { steps, run, reload } = recorder();\n await checkoutPristine('main', undefined, reload, run);\n expect(steps).to.deep.equal(['checkout -f main', CLEAN, '<reload>']);\n });\n\n // `checkout -B` moves an existing local ref; unpushed commits would be orphaned to the reflog.\n it('REFUSES to reset a local branch whose commits no remote contains, before touching the tree', async () => {\n const { steps, run, reload } = recorder('abc123\\n', '\\n');\n let message = '';\n await checkoutPristine('my-lane', 'origin/my-lane', reload, run).catch((e) => {\n message = e.message;\n });\n expect(message).to.contain('local branch \"my-lane\" has commits that no remote branch contains');\n expect(steps.some((step) => step.startsWith('checkout'))).to.equal(false);\n expect(steps.some((step) => step.startsWith('clean'))).to.equal(false);\n });\n\n // Remote containment admits both legitimate reset shapes; start-point ancestry wrongly refuses one.\n it('allows the reset when any remote branch contains the local tip — even a different one than the start point', async () => {\n const { steps, run, reload } = recorder('abc123\\n', ' origin/policy-lane\\n');\n await checkoutPristine('policy-lane', 'origin/main', reload, run);\n expect(steps.some((step) => step === 'checkout -f -B policy-lane origin/main')).to.equal(true);\n });\n\n it('skips the safety proof when the branch does not exist locally (every CI clone)', async () => {\n const { steps, run, reload } = recorder('');\n await checkoutPristine('my-lane', 'origin/my-lane', reload, run);\n expect(steps.filter((step) => step.startsWith('branch -r'))).to.deep.equal([]);\n expect(steps.some((step) => step === 'checkout -f -B my-lane origin/my-lane')).to.equal(true);\n });\n});\n\ndescribe('parseLsRemoteSymref / remoteHeadBranch', () => {\n const SYMREF_OUT = 'ref: refs/heads/main\\tHEAD\\n1234abcd\\tHEAD\\n';\n\n it('reads the branch out of the symref line, keeping a slashed name whole', () => {\n expect(parseLsRemoteSymref(SYMREF_OUT)).to.equal('main');\n // same slash discipline as parseOriginHeadRef: `release/main` is a NAME, not a path to shorten\n expect(parseLsRemoteSymref('ref: refs/heads/release/main\\tHEAD\\nabc\\tHEAD\\n')).to.equal('release/main');\n });\n\n it('returns undefined when the server omits the symref line (protocol v0), rather than guessing', () => {\n expect(parseLsRemoteSymref('1234abcd\\tHEAD\\n')).to.equal(undefined);\n expect(parseLsRemoteSymref('')).to.equal(undefined);\n });\n\n it('asks the remote, not the local refs — and swallows an offline failure into undefined', async () => {\n const argv: string[][] = [];\n expect(\n await remoteHeadBranch(async (args) => {\n argv.push(args);\n return SYMREF_OUT;\n })\n ).to.equal('main');\n expect(argv).to.deep.equal([['ls-remote', '--symref', 'origin', 'HEAD']]);\n expect(\n await remoteHeadBranch(async () => {\n throw new Error('could not read from remote repository');\n })\n ).to.equal(undefined);\n });\n});\n\n/**\n * The default branch is derived from the remote, not from validated config, so the primitive that\n * builds the checkout argv is the last line of defence against an option-like name.\n */\ndescribe('assertCheckoutableBranch', () => {\n it('refuses a name git would read as an option, or reject as a ref, before any argv is built', () => {\n ['-x', '--force', '', 'has space', 'a..b', 'refs/heads/main', 'ends.lock'].forEach((name) => {\n expect(() => assertCheckoutableBranch(name), name).to.throw(/cannot check out/);\n });\n });\n\n it('accepts ordinary names, slashed names included', () => {\n ['main', 'master', 'release/main', 'feature/a-b_c', 'trunk'].forEach((name) => {\n expect(() => assertCheckoutableBranch(name), name).to.not.throw();\n });\n });\n\n it('is enforced by checkoutPristine itself, so every caller inherits it', async () => {\n const steps: string[] = [];\n let threw = false;\n await checkoutPristine(\n '-x',\n undefined,\n async () => {},\n async (args) => {\n steps.push(args.join(' '));\n }\n ).catch(() => {\n threw = true;\n });\n expect(threw).to.equal(true);\n expect(steps).to.deep.equal([]);\n });\n});\n\ndescribe('localBranchExists', () => {\n // simple-git's `raw` can resolve with empty output on non-zero exits, so all three missing-branch\n // runner behaviors (empty resolve, undefined resolve, rejection) must be false.\n it('asks for the LOCAL ref quietly, and is true only when that ref prints a sha', async () => {\n const argv: string[][] = [];\n expect(\n await localBranchExists('main', async (args) => {\n argv.push(args);\n return 'abc123\\n';\n })\n ).to.equal(true);\n expect(argv).to.deep.equal([['rev-parse', '--verify', '--quiet', 'refs/heads/main']]);\n expect(await localBranchExists('main', async () => '')).to.equal(false);\n expect(await localBranchExists('main', async () => undefined)).to.equal(false);\n expect(\n await localBranchExists('main', async () => {\n throw new Error('exit 1');\n })\n ).to.equal(false);\n });\n});\n\n// Each shape is wrong in the other environment: `-B origin/<branch>` in a developer's repo resets\n// their default branch; a plain switch in a detached-HEAD CI checkout fails outright.\ndescribe('checkoutPristineRestore', () => {\n function restoreRecorder(localBranchIsPresent: boolean) {\n const steps: string[] = [];\n const run = async (args: string[]) => {\n steps.push(args.join(' '));\n // model the REAL runner: a missing ref resolves with empty output rather than rejecting\n if (args[0] === 'rev-parse') return localBranchIsPresent ? 'abc123\\n' : '';\n if (args[0] === 'branch') return ' origin/some-branch\\n';\n return undefined;\n };\n const reload = async () => {\n steps.push('<reload>');\n };\n return { steps, run, reload };\n }\n\n it('local branch present: plain forced switch — never a -B that would reset it to origin', async () => {\n const { steps, run, reload } = restoreRecorder(true);\n await checkoutPristineRestore('main', reload, run);\n expect(steps).to.deep.equal([\n 'rev-parse --verify --quiet refs/heads/main',\n 'checkout -f main',\n 'clean -fd -e .bit -e node_modules',\n '<reload>',\n ]);\n });\n\n it('local branch absent (detached-HEAD CI): forks it from origin/<branch> instead of failing', async () => {\n const { steps, run, reload } = restoreRecorder(false);\n await checkoutPristineRestore('main', reload, run);\n // the second rev-parse is checkoutPristine's own reset guard re-proving what restore just learned —\n // redundant but harmless, and cheaper than giving the guard a bypass parameter\n expect(steps).to.deep.equal([\n 'rev-parse --verify --quiet refs/heads/main',\n 'rev-parse --verify --quiet refs/heads/main',\n 'checkout -f -B main origin/main',\n 'clean -fd -e .bit -e node_modules',\n '<reload>',\n ]);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { GitHostProvider, PrInfo } from './git-host-provider';
|
|
2
|
+
/**
|
|
3
|
+
* The `owner/repo` out of a github.com remote URL, anchored to the URL authority. `:digits` is a port
|
|
4
|
+
* only under a scheme; in the scp-like form (`git@github.com:12345/repo`) the colon starts the PATH
|
|
5
|
+
* and an all-digits owner is a legal GitHub username.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseGitHubRepo(remoteUrl: string): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Does this remote point at GitHub? A host test only — an unparseable github.com remote is still
|
|
10
|
+
* GitHub's to claim. Host equality, not substring: `github.com` can be a path segment on another host.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isGitHubRemote(remoteUrl: string): boolean;
|
|
13
|
+
/** Warning sink; a plain callback rather than a `Logger` so this module needs no logger aspect. */
|
|
14
|
+
export type WarnFn = (message: string) => void;
|
|
15
|
+
export declare class GitHubClient implements GitHostProvider {
|
|
16
|
+
readonly name = "github";
|
|
17
|
+
private token;
|
|
18
|
+
/** `owner/repo`. Readable so callers (and tests) can see *which* repository a resolved client speaks to. */
|
|
19
|
+
readonly repo: string;
|
|
20
|
+
private fetchImpl;
|
|
21
|
+
constructor(opts: {
|
|
22
|
+
token: string;
|
|
23
|
+
repo: string;
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* The client this environment describes, or undefined when it describes none. `origin` outranks
|
|
28
|
+
* `GITHUB_REPOSITORY`: every PR operation names a branch that only exists on `origin`, so if the two
|
|
29
|
+
* disagree the origin-parsed repository wins and the mismatch is warned about once.
|
|
30
|
+
*/
|
|
31
|
+
static fromEnv(gitRemoteUrl?: string, warn?: WarnFn): GitHubClient | undefined;
|
|
32
|
+
matchesRemote(remoteUrl: string): boolean;
|
|
33
|
+
/** A constructed client always has both halves — it cannot be built without them. */
|
|
34
|
+
isConfigured(): boolean;
|
|
35
|
+
private request;
|
|
36
|
+
private toPrInfo;
|
|
37
|
+
findPrByBranch(branch: string): Promise<PrInfo | undefined>;
|
|
38
|
+
createPr(opts: {
|
|
39
|
+
head: string;
|
|
40
|
+
base: string;
|
|
41
|
+
title: string;
|
|
42
|
+
body: string;
|
|
43
|
+
}): Promise<PrInfo>;
|
|
44
|
+
closePr(prNumber: number, comment?: string): Promise<void>;
|
|
45
|
+
comment(prNumber: number, body: string): Promise<void>;
|
|
46
|
+
addLabel(prNumber: number, label: string): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The registrable GitHub provider: resolves its `GitHubClient` from the environment lazily, since
|
|
50
|
+
* registration happens at aspect load, before credentials are known. A resolved client is memoized;
|
|
51
|
+
* an unresolved one is retried so a later call that does carry the remote URL can still succeed.
|
|
52
|
+
*/
|
|
53
|
+
export declare class GitHubHostProvider implements GitHostProvider {
|
|
54
|
+
private onWarning;
|
|
55
|
+
readonly name = "github";
|
|
56
|
+
private client;
|
|
57
|
+
/** The last `origin` URL seen, so a PR method can still recover `owner/repo` from it. */
|
|
58
|
+
private remoteHint;
|
|
59
|
+
constructor(onWarning?: WarnFn);
|
|
60
|
+
matchesRemote(remoteUrl: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* A known non-GitHub remote can never count as configured, however complete the environment looks:
|
|
63
|
+
* on Actions the env is always set, so answering "configured" for a remote this provider declined to
|
|
64
|
+
* claim would make it the sole-configured fallback and aim PRs at the wrong host's repository.
|
|
65
|
+
*/
|
|
66
|
+
isConfigured(remoteUrl?: string): boolean;
|
|
67
|
+
findPrByBranch(branch: string): Promise<PrInfo | undefined>;
|
|
68
|
+
createPr(opts: {
|
|
69
|
+
head: string;
|
|
70
|
+
base: string;
|
|
71
|
+
title: string;
|
|
72
|
+
body: string;
|
|
73
|
+
}): Promise<PrInfo>;
|
|
74
|
+
closePr(prNumber: number, comment?: string): Promise<void>;
|
|
75
|
+
comment(prNumber: number, body: string): Promise<void>;
|
|
76
|
+
addLabel(prNumber: number, label: string): Promise<void>;
|
|
77
|
+
private resolveClient;
|
|
78
|
+
private requireClient;
|
|
79
|
+
}
|