@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 @@
|
|
|
1
|
+
{"version":3,"names":["_bitError","data","require","isValidGitBranchName","name","validateBranchName","undefined","startsWith","test","includes","endsWith","split","some","component","assertValidBranchName","configKey","problem","BitError","assertValidBranchPrefix","prefix","resolveSyncConfig","raw","resolved","branchPrefix","branches","lanes","mainSyncBranch","autoMergeMainSyncPr","mainSync","onConflict","Object","entries","forEach","laneName","branch","laneNameToBranch","cfg","override","derived","branchToLaneName","find","b","slice","length","parseLaneTarget","input","defaultScope","trimmed","trim","hostScope","parts","syncableLaneNameForBranch","isValidLaneName","MAX_LANE_NAME_LENGTH","globToRegExp","glob","escaped","replace","RegExp","shouldSyncLane","pattern"],"sources":["sync-config.ts"],"sourcesContent":["import { BitError } from '@teambit/bit-error';\n\n/**\n * Is this a branch name git will accept? A conservative pure subset of `git check-ref-format --branch`:\n * stricter than git is fine, laxer is not. A leading `-` must be refused here because it would be read\n * as a command-line option once interpolated into a git invocation.\n */\nexport function isValidGitBranchName(name: string): boolean {\n return validateBranchName(name) === undefined;\n}\n\n/** Why `name` is not a usable branch name, or undefined when it is. */\nexport function validateBranchName(name: string): string | undefined {\n if (!name) return 'it is empty';\n if (name.startsWith('-')) return 'it starts with \"-\", which git would read as a command-line option';\n if (/\\s/.test(name)) return 'it contains whitespace';\n // oxlint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(name)) return 'it contains a control character';\n if (/[~^:?*[\\\\]/.test(name)) return 'it contains one of the characters git forbids in a ref: ~ ^ : ? * [ \\\\';\n if (name.includes('..')) return 'it contains \"..\"';\n if (name.includes('@{')) return 'it contains \"@{\"';\n if (name === '@') return 'it is \"@\", which git reserves';\n // Leading `refs/` only: pushes interpolate the name into `refs/heads/<b>`, so it would double up.\n if (name.startsWith('refs/'))\n return 'it starts with \"refs/\" — configure the bare branch name (\"main\", not \"refs/heads/main\")';\n if (name.startsWith('/') || name.endsWith('/')) return 'it starts or ends with \"/\"';\n if (name.includes('//')) return 'it contains an empty path component (\"//\")';\n if (name.endsWith('.')) return 'it ends with \".\"';\n if (name.endsWith('.lock')) return 'it ends with \".lock\"';\n if (name.split('/').some((component) => component.startsWith('.'))) return 'a path component starts with \".\"';\n if (name.split('/').some((component) => component.endsWith('.lock'))) return 'a path component ends with \".lock\"';\n return undefined;\n}\n\n/** Throw a `BitError` naming the offending config key and value unless `name` is a usable branch name. */\nexport function assertValidBranchName(name: string, configKey: string): void {\n const problem = validateBranchName(name);\n if (!problem) return;\n throw new BitError(\n `bit ci sync: ${configKey} is \"${name}\", which is not a valid git branch name — ${problem}. ` +\n `Fix it in the \"teambit.git/ci\" sync config in workspace.jsonc.`\n );\n}\n\n/**\n * A `branchPrefix` may end in `/` or be empty, so it is validated as the *start* of a branch name.\n * The full check still happens on the derived name (see `laneNameToBranch`).\n */\nexport function assertValidBranchPrefix(prefix: string, configKey: string): void {\n if (!prefix) return;\n const problem = validateBranchName(`${prefix}x`);\n if (!problem) return;\n throw new BitError(\n `bit ci sync: ${configKey} is \"${prefix}\", which cannot start a valid git branch name — ${problem}. ` +\n `Fix it in the \"teambit.git/ci\" sync config in workspace.jsonc.`\n );\n}\n\nexport interface CiSyncConfig {\n /** The prefix for a lane-mapped branch. 'lane/' maps lane my-lane to branch lane/my-lane. */\n branchPrefix?: string;\n /** Explicit overrides that map a lane name to a branch name. */\n branches?: Record<string, string>;\n /** The glob patterns of the lane names to sync. An empty list stops lane mirroring. */\n lanes?: string[];\n /** The branch that carries the main-scope drift. */\n mainSyncBranch?: string;\n /**\n * How main-scope drift reaches the default branch. 'pr' (the default): the command commits the drift\n * to `mainSyncBranch` and opens a pull request. 'direct-push': the command commits the drift on the\n * default branch and pushes it, and does not use `mainSyncBranch`.\n */\n mainSync?: 'pr' | 'direct-push';\n /**\n * What the command does with one contested line during a `merge-diverged` action. 'halt' (the\n * default): the command stops, labels the pull request, and writes the recovery steps. 'git-wins':\n * the command keeps the branch version. 'lane-wins': the command takes the lane version.\n * Non-conflicting changes always merge, with every value.\n */\n onConflict?: 'halt' | 'git-wins' | 'lane-wins';\n /** Reserved. The command warns and enables no auto-merge on the main sync pull request. */\n autoMergeMainSyncPr?: boolean;\n}\n\n/**\n * Resolve the configured sync options, validating every branch name before anything runs — a bad name\n * must fail at startup naming the config key, not mid-run with git's own ref error.\n */\nexport function resolveSyncConfig(raw?: CiSyncConfig): Required<CiSyncConfig> {\n const resolved: Required<CiSyncConfig> = {\n branchPrefix: raw?.branchPrefix ?? '',\n branches: raw?.branches ?? {},\n lanes: raw?.lanes ?? ['*'],\n mainSyncBranch: raw?.mainSyncBranch ?? 'bit-sync/main',\n autoMergeMainSyncPr: raw?.autoMergeMainSyncPr ?? false,\n mainSync: raw?.mainSync ?? 'pr',\n onConflict: raw?.onConflict ?? 'halt',\n };\n // Config comes from workspace.jsonc, so the union types are not enforced at runtime; a typo must\n // fail at startup rather than fall through to whichever mode a comparison happens to miss.\n if (resolved.mainSync !== 'pr' && resolved.mainSync !== 'direct-push') {\n throw new BitError(\n `sync.mainSync: \"${resolved.mainSync}\" is not a valid value. Use \"pr\" (propose the main-scope drift ` +\n `as a pull request from sync.mainSyncBranch) or \"direct-push\" (push the drift straight onto the ` +\n `default branch)`\n );\n }\n if (resolved.onConflict !== 'halt' && resolved.onConflict !== 'git-wins' && resolved.onConflict !== 'lane-wins') {\n throw new BitError(\n `sync.onConflict: \"${resolved.onConflict}\" is not a valid value. Use \"halt\" (stop the conflicted ` +\n `lane, label the PR and let a human resolve — the default), \"git-wins\" (conflicting hunks keep ` +\n `the branch's version) or \"lane-wins\" (conflicting hunks take the lane's version)`\n );\n }\n assertValidBranchPrefix(resolved.branchPrefix, 'sync.branchPrefix');\n assertValidBranchName(resolved.mainSyncBranch, 'sync.mainSyncBranch');\n Object.entries(resolved.branches).forEach(([laneName, branch]) =>\n assertValidBranchName(branch, `sync.branches[\"${laneName}\"]`)\n );\n return resolved;\n}\n\n/**\n * The branch a lane name maps to. The derived result is validated too: a prefix that is fine on its\n * own can still combine with a lane name into something git refuses.\n */\nexport function laneNameToBranch(laneName: string, cfg: Required<CiSyncConfig>): string {\n const override = cfg.branches[laneName];\n if (override) return override; // already validated by resolveSyncConfig\n const derived = `${cfg.branchPrefix}${laneName}`;\n assertValidBranchName(derived, `the branch for lane \"${laneName}\" (sync.branchPrefix + lane name)`);\n return derived;\n}\n\nexport function branchToLaneName(branch: string, cfg: Required<CiSyncConfig>): string | undefined {\n const override = Object.entries(cfg.branches).find(([, b]) => b === branch);\n if (override) return override[0];\n if (cfg.branchPrefix) {\n return branch.startsWith(cfg.branchPrefix) ? branch.slice(cfg.branchPrefix.length) : undefined;\n }\n return branch;\n}\n\n/**\n * A lane to sync, split into the scope hosting the lane object and the bare name. The branch mapping\n * uses the NAME only; every bit operation uses the full `hostScope/name`.\n */\nexport type LaneTarget = {\n /** the scope hosting the lane object; the workspace's defaultScope unless the target said otherwise */\n hostScope: string;\n /** the bare lane name — never contains a '/' */\n name: string;\n};\n\n/**\n * Parse the `[lane]` argument (`my-lane` or `some-org.some-scope/my-lane`) into a {@link LaneTarget}.\n * A lane name cannot contain `/`, so the single `/` is always the scope/name boundary. The dot in a\n * scope id is not required (self-hosted scopes can be bare names), so it is not validated here.\n */\nexport function parseLaneTarget(input: string, defaultScope: string): LaneTarget {\n const trimmed = input.trim();\n if (!trimmed) throw new BitError('bit ci sync: the lane argument is empty');\n if (!trimmed.includes('/')) return { hostScope: defaultScope, name: trimmed };\n const parts = trimmed.split('/');\n const [hostScope, name] = parts;\n if (parts.length !== 2 || !hostScope || !name) {\n throw new BitError(\n `bit ci sync: \"${input}\" is not a valid lane target. Use either a lane name (\"my-lane\", resolved ` +\n `against the workspace's defaultScope \"${defaultScope}\") or a scope-qualified lane id ` +\n `(\"some-org.some-scope/my-lane\")`\n );\n }\n return { hostScope, name };\n}\n\n/**\n * The lane name a branch maps to, only if that name could actually be a lane AND maps back to a branch\n * name usable in a git invocation. `branchToLaneName` alone is a string transform (the identity under\n * the default empty prefix), so it would report `feature/foo` as a \"lane\" — a name bit forbids. The\n * round trip matters because the lane grammar permits `-`: a developer branch called `-x` is a valid\n * lane name, so enumeration would adopt it as a target and the branch mapping would then throw, turning\n * one oddly-named branch into a halt that fails the whole `--all` run. Such a branch is not lane-mapped;\n * skip it like any other.\n */\nexport function syncableLaneNameForBranch(branch: string, cfg: Required<CiSyncConfig>): string | undefined {\n const laneName = branchToLaneName(branch, cfg);\n if (!laneName || !isValidLaneName(laneName)) return undefined;\n // `laneNameToBranch` asserts rather than returns, so the round trip is checked here: a configured\n // override is already validated, and a derived name must pass the same branch-name rules.\n if (cfg.branches[laneName]) return laneName;\n return validateBranchName(`${cfg.branchPrefix}${laneName}`) === undefined ? laneName : undefined;\n}\n\n/** bit's own maximum lane-name length (`create-lane.ts`'s `MAX_LANE_NAME_LENGTH`). */\nconst MAX_LANE_NAME_LENGTH = 800;\n\n/**\n * Could this string be a bit lane name at all? Mirrors the module-private `isValidLaneName` in\n * `create-lane.ts`; drift errs toward skipping a branch, and `sync-config.spec.ts` pins the rule.\n */\nexport function isValidLaneName(name: string): boolean {\n if (!name || name.length > MAX_LANE_NAME_LENGTH) return false;\n return /^[$\\-_!a-z0-9]+$/.test(name);\n}\n\n/** minimal glob: '*' wildcard only (matches the lanes list use-case; avoids a new dep) */\nfunction globToRegExp(glob: string): RegExp {\n const escaped = glob.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*');\n return new RegExp(`^${escaped}$`);\n}\n\nexport function shouldSyncLane(laneName: string, cfg: Required<CiSyncConfig>): boolean {\n return cfg.lanes.some((pattern) => globToRegExp(pattern).test(laneName));\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
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 _syncConfig() {
|
|
11
|
+
const data = require("./sync-config");
|
|
12
|
+
_syncConfig = 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
|
+
const DEFAULT_SCOPE = 'acme.shop';
|
|
23
|
+
|
|
24
|
+
/** `refs/` in the middle is git-legal and no refspec here doubles it, so only a LEADING one is refused. */
|
|
25
|
+
const VALID = ['main', 'bit-sync/main', 'lane/my-lane', 'feature/ABC-123_thing', 'release-2.0', 'a', 'feature/refs/foo', 'my-refs/foo', 'lane/refs', 'refs'];
|
|
26
|
+
const INVALID = [
|
|
27
|
+
// an option-like leading dash — git would read it as a command-line option
|
|
28
|
+
'--force', '-delete',
|
|
29
|
+
// characters and sequences git forbids in a ref, plus control characters
|
|
30
|
+
'a b', 'a\tb', 'a..b', 'a~b', 'a^b', 'a:b', 'a?b', 'a*b', 'a[b', 'a\\b', 'a@{b', '@', 'a\u0001b', 'a\u007fb',
|
|
31
|
+
// path-shape rules
|
|
32
|
+
'', '/x', 'x/', 'a//b', 'a.', 'a.lock', '.hidden', 'a/.hidden', 'a/b.lock',
|
|
33
|
+
// stricter than git on purpose: every push interpolates the value into `refs/heads/<b>`
|
|
34
|
+
'refs/heads/foo', 'refs/foo', 'refs/remotes/origin/foo'];
|
|
35
|
+
describe('isValidGitBranchName', () => {
|
|
36
|
+
it('accepts the shapes real configs use', () => {
|
|
37
|
+
VALID.forEach(name => (0, _chai().expect)((0, _syncConfig().isValidGitBranchName)(name), name).to.equal(true));
|
|
38
|
+
});
|
|
39
|
+
it('rejects every shape git would refuse, plus the two this module is stricter about', () => {
|
|
40
|
+
INVALID.forEach(name => (0, _chai().expect)((0, _syncConfig().isValidGitBranchName)(name), JSON.stringify(name)).to.equal(false));
|
|
41
|
+
});
|
|
42
|
+
it('says what is wrong in the terms the user has to act on', () => {
|
|
43
|
+
(0, _chai().expect)((0, _syncConfig().validateBranchName)('-x')).to.contain('command-line option');
|
|
44
|
+
(0, _chai().expect)((0, _syncConfig().validateBranchName)('refs/heads/main')).to.contain('bare branch name');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('assertValidBranchName', () => {
|
|
48
|
+
it('names the offending config key and value, so the message points at the line to fix', () => {
|
|
49
|
+
let message = '';
|
|
50
|
+
try {
|
|
51
|
+
(0, _syncConfig().assertValidBranchName)('--force', 'sync.branches["my-lane"]');
|
|
52
|
+
} catch (err) {
|
|
53
|
+
message = err.message;
|
|
54
|
+
}
|
|
55
|
+
(0, _chai().expect)(message).to.contain('sync.branches["my-lane"]');
|
|
56
|
+
(0, _chai().expect)(message).to.contain('--force');
|
|
57
|
+
(0, _chai().expect)(message).to.contain('workspace.jsonc');
|
|
58
|
+
(0, _chai().expect)(() => (0, _syncConfig().assertValidBranchName)('bit-sync/main', 'sync.mainSyncBranch')).to.not.throw();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('assertValidBranchPrefix', () => {
|
|
62
|
+
it('accepts an empty prefix (the default) and ordinary directory-ish prefixes', () => {
|
|
63
|
+
['', 'lane/', 'bit-'].forEach(prefix => (0, _chai().expect)(() => (0, _syncConfig().assertValidBranchPrefix)(prefix, 'sync.branchPrefix'), prefix).to.not.throw());
|
|
64
|
+
});
|
|
65
|
+
it('rejects a prefix that could never start a valid name', () => {
|
|
66
|
+
// 'lane//' — an empty path component in the prefix must not be collapsed away by the check
|
|
67
|
+
['-x', 'a b/', 'a..b/', 'lane//'].forEach(prefix => (0, _chai().expect)(() => (0, _syncConfig().assertValidBranchPrefix)(prefix, 'sync.branchPrefix'), prefix).to.throw('sync.branchPrefix'));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('resolveSyncConfig validates branch names up front', () => {
|
|
71
|
+
/** A bad name must fail at startup naming its config key, not mid-run with git's own ref error. */
|
|
72
|
+
const rejected = [['sync.mainSyncBranch', {
|
|
73
|
+
mainSyncBranch: '--force'
|
|
74
|
+
}],
|
|
75
|
+
// a fully-qualified ref where a bare branch name belongs
|
|
76
|
+
['sync.mainSyncBranch', {
|
|
77
|
+
mainSyncBranch: 'refs/heads/bit-sync/main'
|
|
78
|
+
}], ['sync.branches["my-lane"]', {
|
|
79
|
+
branches: {
|
|
80
|
+
'my-lane': 'a..b'
|
|
81
|
+
}
|
|
82
|
+
}], ['sync.branchPrefix', {
|
|
83
|
+
branchPrefix: '-x'
|
|
84
|
+
}],
|
|
85
|
+
// a `refs/`-rooted prefix double-prefixes every derived branch, not just one configured name
|
|
86
|
+
['sync.branchPrefix', {
|
|
87
|
+
branchPrefix: 'refs/heads/'
|
|
88
|
+
}]];
|
|
89
|
+
rejected.forEach(([key, raw]) => {
|
|
90
|
+
it(`rejects ${JSON.stringify(raw)} naming ${key}`, () => {
|
|
91
|
+
(0, _chai().expect)(() => (0, _syncConfig().resolveSyncConfig)(raw)).to.throw(key);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
it('accepts the documented defaults unchanged', () => {
|
|
95
|
+
const cfg = (0, _syncConfig().resolveSyncConfig)();
|
|
96
|
+
(0, _chai().expect)(cfg.mainSyncBranch).to.equal('bit-sync/main');
|
|
97
|
+
(0, _chai().expect)(cfg.branchPrefix).to.equal('');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('laneNameToBranch validates the derived name', () => {
|
|
101
|
+
it('catches a prefix + lane name that only together form something git refuses', () => {
|
|
102
|
+
// `lane/` is a fine prefix on its own; appending a lane name ending the branch in "." is not.
|
|
103
|
+
const cfg = (0, _syncConfig().resolveSyncConfig)({
|
|
104
|
+
branchPrefix: 'lane/'
|
|
105
|
+
});
|
|
106
|
+
(0, _chai().expect)(() => (0, _syncConfig().laneNameToBranch)('x.', cfg)).to.throw('not a valid git branch name');
|
|
107
|
+
(0, _chai().expect)((0, _syncConfig().laneNameToBranch)('my-lane', cfg)).to.equal('lane/my-lane');
|
|
108
|
+
});
|
|
109
|
+
it('returns an override as configured (already validated by resolveSyncConfig)', () => {
|
|
110
|
+
(0, _chai().expect)((0, _syncConfig().laneNameToBranch)('my-lane', (0, _syncConfig().resolveSyncConfig)({
|
|
111
|
+
branches: {
|
|
112
|
+
'my-lane': 'custom/branch'
|
|
113
|
+
}
|
|
114
|
+
}))).to.equal('custom/branch');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('sync-config', () => {
|
|
118
|
+
it('applies defaults', () => {
|
|
119
|
+
(0, _chai().expect)((0, _syncConfig().resolveSyncConfig)(undefined)).to.deep.equal({
|
|
120
|
+
branchPrefix: '',
|
|
121
|
+
branches: {},
|
|
122
|
+
lanes: ['*'],
|
|
123
|
+
mainSyncBranch: 'bit-sync/main',
|
|
124
|
+
autoMergeMainSyncPr: false,
|
|
125
|
+
mainSync: 'pr',
|
|
126
|
+
// a silent policy pick rewrites someone's work, so silence is opt-in
|
|
127
|
+
onConflict: 'halt'
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
it('accepts every documented value of the union-typed options', () => {
|
|
131
|
+
['pr', 'direct-push'].forEach(v => (0, _chai().expect)((0, _syncConfig().resolveSyncConfig)({
|
|
132
|
+
mainSync: v
|
|
133
|
+
}).mainSync).to.equal(v));
|
|
134
|
+
['halt', 'git-wins', 'lane-wins'].forEach(v => (0, _chai().expect)((0, _syncConfig().resolveSyncConfig)({
|
|
135
|
+
onConflict: v
|
|
136
|
+
}).onConflict).to.equal(v));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Config comes from workspace.jsonc, so the union types are not enforced at runtime: a typo must fail
|
|
140
|
+
// at startup naming the key, the value and the options, not fall through to whichever mode a
|
|
141
|
+
// comparison happens to miss.
|
|
142
|
+
const REJECTED = [[{
|
|
143
|
+
mainSync: 'direct'
|
|
144
|
+
}, /sync\.mainSync.*"direct".*"pr".*"direct-push"/], [{
|
|
145
|
+
onConflict: 'ours'
|
|
146
|
+
}, /sync\.onConflict.*"ours".*"halt".*"git-wins".*"lane-wins"/]];
|
|
147
|
+
REJECTED.forEach(([raw, expected]) => {
|
|
148
|
+
it(`refuses ${JSON.stringify(raw)}, naming the key, the value and the valid options`, () => {
|
|
149
|
+
(0, _chai().expect)(() => (0, _syncConfig().resolveSyncConfig)(raw)).to.throw(expected);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
it('maps lane <-> branch: an explicit override outranks the prefix in both directions', () => {
|
|
153
|
+
const cfg = (0, _syncConfig().resolveSyncConfig)({
|
|
154
|
+
branchPrefix: 'lane/',
|
|
155
|
+
branches: {
|
|
156
|
+
'my-lane': 'custom'
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
(0, _chai().expect)((0, _syncConfig().laneNameToBranch)('my-lane', cfg)).to.equal('custom');
|
|
160
|
+
(0, _chai().expect)((0, _syncConfig().branchToLaneName)('custom', cfg)).to.equal('my-lane');
|
|
161
|
+
(0, _chai().expect)((0, _syncConfig().branchToLaneName)('lane/other', cfg)).to.equal('other');
|
|
162
|
+
(0, _chai().expect)((0, _syncConfig().branchToLaneName)('unrelated-branch', cfg)).to.equal(undefined);
|
|
163
|
+
// with no prefix and no override, a branch maps to the same-named lane
|
|
164
|
+
(0, _chai().expect)((0, _syncConfig().branchToLaneName)('some-lane', (0, _syncConfig().resolveSyncConfig)({}))).to.equal('some-lane');
|
|
165
|
+
});
|
|
166
|
+
describe('parseLaneTarget', () => {
|
|
167
|
+
// A lane name cannot contain '/', so the single '/' is always the scope/name boundary; a hosting
|
|
168
|
+
// scope needs no dot (self-hosted and test scopes are bare names).
|
|
169
|
+
const PARSED = [['my-lane', {
|
|
170
|
+
hostScope: DEFAULT_SCOPE,
|
|
171
|
+
name: 'my-lane'
|
|
172
|
+
}], ['other-org.other-scope/my-lane', {
|
|
173
|
+
hostScope: 'other-org.other-scope',
|
|
174
|
+
name: 'my-lane'
|
|
175
|
+
}], ['bare-scope/my-lane', {
|
|
176
|
+
hostScope: 'bare-scope',
|
|
177
|
+
name: 'my-lane'
|
|
178
|
+
}], [' my-lane ', {
|
|
179
|
+
hostScope: DEFAULT_SCOPE,
|
|
180
|
+
name: 'my-lane'
|
|
181
|
+
}]];
|
|
182
|
+
it('resolves a bare name against defaultScope and splits a scope-qualified id', () => {
|
|
183
|
+
PARSED.forEach(([input, expected]) => (0, _chai().expect)((0, _syncConfig().parseLaneTarget)(input, DEFAULT_SCOPE), input).to.deep.equal(expected));
|
|
184
|
+
});
|
|
185
|
+
it('refuses malformed targets rather than guessing at them', () => {
|
|
186
|
+
['', ' ', '/', '/my-lane', 'scope/', 'a/b/c', 'scope//lane'].forEach(input => (0, _chai().expect)(() => (0, _syncConfig().parseLaneTarget)(input, DEFAULT_SCOPE), `input: "${input}"`).to.throw());
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
it('filters lanes by glob list', () => {
|
|
190
|
+
(0, _chai().expect)((0, _syncConfig().shouldSyncLane)('anything', (0, _syncConfig().resolveSyncConfig)({}))).to.equal(true);
|
|
191
|
+
const cfg = (0, _syncConfig().resolveSyncConfig)({
|
|
192
|
+
lanes: ['feature-*']
|
|
193
|
+
});
|
|
194
|
+
(0, _chai().expect)((0, _syncConfig().shouldSyncLane)('feature-x', cfg)).to.equal(true);
|
|
195
|
+
(0, _chai().expect)((0, _syncConfig().shouldSyncLane)('hotfix-y', cfg)).to.equal(false);
|
|
196
|
+
(0, _chai().expect)((0, _syncConfig().shouldSyncLane)('anything', (0, _syncConfig().resolveSyncConfig)({
|
|
197
|
+
lanes: []
|
|
198
|
+
}))).to.equal(false);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Pins bit's own rule (create-lane.ts), which this module re-states rather than imports.
|
|
203
|
+
describe('isValidLaneName', () => {
|
|
204
|
+
it('accepts what bit accepts: lowercase alphanumerics plus - _ $ !', () => {
|
|
205
|
+
['my-lane', 'lane_1', 'a', 'x$y', 'w!', 'release-2'].forEach(name => (0, _chai().expect)((0, _syncConfig().isValidLaneName)(name), name).to.equal(true));
|
|
206
|
+
});
|
|
207
|
+
it('rejects uppercase, spaces, dots, the empty string, and the slash a branch name brings in', () => {
|
|
208
|
+
['FEATURE', 'Feature', 'a b', 'a.b', '', 'a+b', 'café', 'feature/foo', 'a/b/c'].forEach(name => (0, _chai().expect)((0, _syncConfig().isValidLaneName)(name), JSON.stringify(name)).to.equal(false));
|
|
209
|
+
});
|
|
210
|
+
it("rejects a name past bit's length limit", () => {
|
|
211
|
+
(0, _chai().expect)((0, _syncConfig().isValidLaneName)('a'.repeat(800))).to.equal(true);
|
|
212
|
+
(0, _chai().expect)((0, _syncConfig().isValidLaneName)('a'.repeat(801))).to.equal(false);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* `--all` maps every remote branch through here; anything it returns becomes a sync target whose branch
|
|
218
|
+
* name is then derived and validated. So the two directions must agree, or an ordinary developer branch
|
|
219
|
+
* halts the run instead of being skipped.
|
|
220
|
+
*/
|
|
221
|
+
describe('syncableLaneNameForBranch round-trips lane and branch validity', () => {
|
|
222
|
+
const cfg = (raw = {}) => (0, _syncConfig().resolveSyncConfig)(_objectSpread({
|
|
223
|
+
lanes: ['*']
|
|
224
|
+
}, raw));
|
|
225
|
+
it('maps an ordinary branch to its lane name', () => {
|
|
226
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('my-lane', cfg())).to.equal('my-lane');
|
|
227
|
+
});
|
|
228
|
+
it('skips a branch whose mapped lane name is valid as a LANE but not usable as a BRANCH', () => {
|
|
229
|
+
// the lane grammar permits `-`, so `-x` is a legal lane name — but git reads it as an option, and
|
|
230
|
+
// `laneNameToBranch` asserts on it. Enumeration must skip such a branch, not adopt it as a target.
|
|
231
|
+
(0, _chai().expect)((0, _syncConfig().isValidLaneName)('-x')).to.equal(true);
|
|
232
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('-x', cfg())).to.equal(undefined);
|
|
233
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('--force', cfg())).to.equal(undefined);
|
|
234
|
+
});
|
|
235
|
+
it('skips a branch whose name bit forbids as a lane', () => {
|
|
236
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('feature/foo', cfg())).to.equal(undefined);
|
|
237
|
+
});
|
|
238
|
+
it('honours a configured override, which resolveSyncConfig already validated', () => {
|
|
239
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('release/x', cfg({
|
|
240
|
+
branches: {
|
|
241
|
+
'lane-x': 'release/x'
|
|
242
|
+
}
|
|
243
|
+
}))).to.equal('lane-x');
|
|
244
|
+
});
|
|
245
|
+
it('applies the round trip through a configured prefix too', () => {
|
|
246
|
+
const prefixed = cfg({
|
|
247
|
+
branchPrefix: 'bit-sync/'
|
|
248
|
+
});
|
|
249
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('bit-sync/my-lane', prefixed)).to.equal('my-lane');
|
|
250
|
+
(0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('my-lane', prefixed)).to.equal(undefined);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
//# sourceMappingURL=sync-config.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_chai","data","require","_syncConfig","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","DEFAULT_SCOPE","VALID","INVALID","describe","it","name","expect","isValidGitBranchName","to","equal","JSON","stringify","validateBranchName","contain","message","assertValidBranchName","err","not","throw","prefix","assertValidBranchPrefix","rejected","mainSyncBranch","branches","branchPrefix","key","raw","resolveSyncConfig","cfg","laneNameToBranch","undefined","deep","lanes","autoMergeMainSyncPr","mainSync","onConflict","v","REJECTED","expected","branchToLaneName","PARSED","hostScope","input","parseLaneTarget","shouldSyncLane","isValidLaneName","repeat","syncableLaneNameForBranch","prefixed"],"sources":["sync-config.spec.ts"],"sourcesContent":["import { expect } from 'chai';\nimport type { CiSyncConfig, LaneTarget } from './sync-config';\nimport {\n syncableLaneNameForBranch,\n assertValidBranchName,\n assertValidBranchPrefix,\n isValidGitBranchName,\n validateBranchName,\n resolveSyncConfig,\n laneNameToBranch,\n branchToLaneName,\n isValidLaneName,\n parseLaneTarget,\n shouldSyncLane,\n} from './sync-config';\n\nconst DEFAULT_SCOPE = 'acme.shop';\n\n/** `refs/` in the middle is git-legal and no refspec here doubles it, so only a LEADING one is refused. */\nconst VALID = [\n 'main',\n 'bit-sync/main',\n 'lane/my-lane',\n 'feature/ABC-123_thing',\n 'release-2.0',\n 'a',\n 'feature/refs/foo',\n 'my-refs/foo',\n 'lane/refs',\n 'refs',\n];\n\nconst INVALID = [\n // an option-like leading dash — git would read it as a command-line option\n '--force',\n '-delete',\n // characters and sequences git forbids in a ref, plus control characters\n 'a b',\n 'a\\tb',\n 'a..b',\n 'a~b',\n 'a^b',\n 'a:b',\n 'a?b',\n 'a*b',\n 'a[b',\n 'a\\\\b',\n 'a@{b',\n '@',\n 'a\\u0001b',\n 'a\\u007fb',\n // path-shape rules\n '',\n '/x',\n 'x/',\n 'a//b',\n 'a.',\n 'a.lock',\n '.hidden',\n 'a/.hidden',\n 'a/b.lock',\n // stricter than git on purpose: every push interpolates the value into `refs/heads/<b>`\n 'refs/heads/foo',\n 'refs/foo',\n 'refs/remotes/origin/foo',\n];\n\ndescribe('isValidGitBranchName', () => {\n it('accepts the shapes real configs use', () => {\n VALID.forEach((name) => expect(isValidGitBranchName(name), name).to.equal(true));\n });\n\n it('rejects every shape git would refuse, plus the two this module is stricter about', () => {\n INVALID.forEach((name) => expect(isValidGitBranchName(name), JSON.stringify(name)).to.equal(false));\n });\n\n it('says what is wrong in the terms the user has to act on', () => {\n expect(validateBranchName('-x')).to.contain('command-line option');\n expect(validateBranchName('refs/heads/main')).to.contain('bare branch name');\n });\n});\n\ndescribe('assertValidBranchName', () => {\n it('names the offending config key and value, so the message points at the line to fix', () => {\n let message = '';\n try {\n assertValidBranchName('--force', 'sync.branches[\"my-lane\"]');\n } catch (err: any) {\n message = err.message;\n }\n expect(message).to.contain('sync.branches[\"my-lane\"]');\n expect(message).to.contain('--force');\n expect(message).to.contain('workspace.jsonc');\n expect(() => assertValidBranchName('bit-sync/main', 'sync.mainSyncBranch')).to.not.throw();\n });\n});\n\ndescribe('assertValidBranchPrefix', () => {\n it('accepts an empty prefix (the default) and ordinary directory-ish prefixes', () => {\n ['', 'lane/', 'bit-'].forEach((prefix) =>\n expect(() => assertValidBranchPrefix(prefix, 'sync.branchPrefix'), prefix).to.not.throw()\n );\n });\n\n it('rejects a prefix that could never start a valid name', () => {\n // 'lane//' — an empty path component in the prefix must not be collapsed away by the check\n ['-x', 'a b/', 'a..b/', 'lane//'].forEach((prefix) =>\n expect(() => assertValidBranchPrefix(prefix, 'sync.branchPrefix'), prefix).to.throw('sync.branchPrefix')\n );\n });\n});\n\ndescribe('resolveSyncConfig validates branch names up front', () => {\n /** A bad name must fail at startup naming its config key, not mid-run with git's own ref error. */\n const rejected: Array<[string, Parameters<typeof resolveSyncConfig>[0]]> = [\n ['sync.mainSyncBranch', { mainSyncBranch: '--force' }],\n // a fully-qualified ref where a bare branch name belongs\n ['sync.mainSyncBranch', { mainSyncBranch: 'refs/heads/bit-sync/main' }],\n ['sync.branches[\"my-lane\"]', { branches: { 'my-lane': 'a..b' } }],\n ['sync.branchPrefix', { branchPrefix: '-x' }],\n // a `refs/`-rooted prefix double-prefixes every derived branch, not just one configured name\n ['sync.branchPrefix', { branchPrefix: 'refs/heads/' }],\n ];\n\n rejected.forEach(([key, raw]) => {\n it(`rejects ${JSON.stringify(raw)} naming ${key}`, () => {\n expect(() => resolveSyncConfig(raw)).to.throw(key);\n });\n });\n\n it('accepts the documented defaults unchanged', () => {\n const cfg = resolveSyncConfig();\n expect(cfg.mainSyncBranch).to.equal('bit-sync/main');\n expect(cfg.branchPrefix).to.equal('');\n });\n});\n\ndescribe('laneNameToBranch validates the derived name', () => {\n it('catches a prefix + lane name that only together form something git refuses', () => {\n // `lane/` is a fine prefix on its own; appending a lane name ending the branch in \".\" is not.\n const cfg = resolveSyncConfig({ branchPrefix: 'lane/' });\n expect(() => laneNameToBranch('x.', cfg)).to.throw('not a valid git branch name');\n expect(laneNameToBranch('my-lane', cfg)).to.equal('lane/my-lane');\n });\n\n it('returns an override as configured (already validated by resolveSyncConfig)', () => {\n expect(laneNameToBranch('my-lane', resolveSyncConfig({ branches: { 'my-lane': 'custom/branch' } }))).to.equal(\n 'custom/branch'\n );\n });\n});\n\ndescribe('sync-config', () => {\n it('applies defaults', () => {\n expect(resolveSyncConfig(undefined)).to.deep.equal({\n branchPrefix: '',\n branches: {},\n lanes: ['*'],\n mainSyncBranch: 'bit-sync/main',\n autoMergeMainSyncPr: false,\n mainSync: 'pr',\n // a silent policy pick rewrites someone's work, so silence is opt-in\n onConflict: 'halt',\n });\n });\n\n it('accepts every documented value of the union-typed options', () => {\n (['pr', 'direct-push'] as const).forEach((v) => expect(resolveSyncConfig({ mainSync: v }).mainSync).to.equal(v));\n (['halt', 'git-wins', 'lane-wins'] as const).forEach((v) =>\n expect(resolveSyncConfig({ onConflict: v }).onConflict).to.equal(v)\n );\n });\n\n // Config comes from workspace.jsonc, so the union types are not enforced at runtime: a typo must fail\n // at startup naming the key, the value and the options, not fall through to whichever mode a\n // comparison happens to miss.\n const REJECTED: Array<[CiSyncConfig, RegExp]> = [\n [{ mainSync: 'direct' as any }, /sync\\.mainSync.*\"direct\".*\"pr\".*\"direct-push\"/],\n [{ onConflict: 'ours' as any }, /sync\\.onConflict.*\"ours\".*\"halt\".*\"git-wins\".*\"lane-wins\"/],\n ];\n\n REJECTED.forEach(([raw, expected]) => {\n it(`refuses ${JSON.stringify(raw)}, naming the key, the value and the valid options`, () => {\n expect(() => resolveSyncConfig(raw)).to.throw(expected);\n });\n });\n\n it('maps lane <-> branch: an explicit override outranks the prefix in both directions', () => {\n const cfg = resolveSyncConfig({ branchPrefix: 'lane/', branches: { 'my-lane': 'custom' } });\n expect(laneNameToBranch('my-lane', cfg)).to.equal('custom');\n expect(branchToLaneName('custom', cfg)).to.equal('my-lane');\n expect(branchToLaneName('lane/other', cfg)).to.equal('other');\n expect(branchToLaneName('unrelated-branch', cfg)).to.equal(undefined);\n // with no prefix and no override, a branch maps to the same-named lane\n expect(branchToLaneName('some-lane', resolveSyncConfig({}))).to.equal('some-lane');\n });\n\n describe('parseLaneTarget', () => {\n // A lane name cannot contain '/', so the single '/' is always the scope/name boundary; a hosting\n // scope needs no dot (self-hosted and test scopes are bare names).\n const PARSED: Array<[string, LaneTarget]> = [\n ['my-lane', { hostScope: DEFAULT_SCOPE, name: 'my-lane' }],\n ['other-org.other-scope/my-lane', { hostScope: 'other-org.other-scope', name: 'my-lane' }],\n ['bare-scope/my-lane', { hostScope: 'bare-scope', name: 'my-lane' }],\n [' my-lane ', { hostScope: DEFAULT_SCOPE, name: 'my-lane' }],\n ];\n\n it('resolves a bare name against defaultScope and splits a scope-qualified id', () => {\n PARSED.forEach(([input, expected]) =>\n expect(parseLaneTarget(input, DEFAULT_SCOPE), input).to.deep.equal(expected)\n );\n });\n\n it('refuses malformed targets rather than guessing at them', () => {\n ['', ' ', '/', '/my-lane', 'scope/', 'a/b/c', 'scope//lane'].forEach((input) =>\n expect(() => parseLaneTarget(input, DEFAULT_SCOPE), `input: \"${input}\"`).to.throw()\n );\n });\n });\n\n it('filters lanes by glob list', () => {\n expect(shouldSyncLane('anything', resolveSyncConfig({}))).to.equal(true);\n const cfg = resolveSyncConfig({ lanes: ['feature-*'] });\n expect(shouldSyncLane('feature-x', cfg)).to.equal(true);\n expect(shouldSyncLane('hotfix-y', cfg)).to.equal(false);\n expect(shouldSyncLane('anything', resolveSyncConfig({ lanes: [] }))).to.equal(false);\n });\n});\n\n// Pins bit's own rule (create-lane.ts), which this module re-states rather than imports.\ndescribe('isValidLaneName', () => {\n it('accepts what bit accepts: lowercase alphanumerics plus - _ $ !', () => {\n ['my-lane', 'lane_1', 'a', 'x$y', 'w!', 'release-2'].forEach((name) =>\n expect(isValidLaneName(name), name).to.equal(true)\n );\n });\n\n it('rejects uppercase, spaces, dots, the empty string, and the slash a branch name brings in', () => {\n ['FEATURE', 'Feature', 'a b', 'a.b', '', 'a+b', 'café', 'feature/foo', 'a/b/c'].forEach((name) =>\n expect(isValidLaneName(name), JSON.stringify(name)).to.equal(false)\n );\n });\n\n it(\"rejects a name past bit's length limit\", () => {\n expect(isValidLaneName('a'.repeat(800))).to.equal(true);\n expect(isValidLaneName('a'.repeat(801))).to.equal(false);\n });\n});\n\n/**\n * `--all` maps every remote branch through here; anything it returns becomes a sync target whose branch\n * name is then derived and validated. So the two directions must agree, or an ordinary developer branch\n * halts the run instead of being skipped.\n */\ndescribe('syncableLaneNameForBranch round-trips lane and branch validity', () => {\n const cfg = (raw: CiSyncConfig = {}) => resolveSyncConfig({ lanes: ['*'], ...raw });\n\n it('maps an ordinary branch to its lane name', () => {\n expect(syncableLaneNameForBranch('my-lane', cfg())).to.equal('my-lane');\n });\n\n it('skips a branch whose mapped lane name is valid as a LANE but not usable as a BRANCH', () => {\n // the lane grammar permits `-`, so `-x` is a legal lane name — but git reads it as an option, and\n // `laneNameToBranch` asserts on it. Enumeration must skip such a branch, not adopt it as a target.\n expect(isValidLaneName('-x')).to.equal(true);\n expect(syncableLaneNameForBranch('-x', cfg())).to.equal(undefined);\n expect(syncableLaneNameForBranch('--force', cfg())).to.equal(undefined);\n });\n\n it('skips a branch whose name bit forbids as a lane', () => {\n expect(syncableLaneNameForBranch('feature/foo', cfg())).to.equal(undefined);\n });\n\n it('honours a configured override, which resolveSyncConfig already validated', () => {\n expect(syncableLaneNameForBranch('release/x', cfg({ branches: { 'lane-x': 'release/x' } }))).to.equal('lane-x');\n });\n\n it('applies the round trip through a configured prefix too', () => {\n const prefixed = cfg({ branchPrefix: 'bit-sync/' });\n expect(syncableLaneNameForBranch('bit-sync/my-lane', prefixed)).to.equal('my-lane');\n expect(syncableLaneNameForBranch('my-lane', prefixed)).to.equal(undefined);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Logger } from '@teambit/logger';
|
|
2
|
+
import type { Workspace } from '@teambit/workspace';
|
|
3
|
+
import type { LanesMain } from '@teambit/lanes';
|
|
4
|
+
import type { CheckoutMain } from '@teambit/checkout';
|
|
5
|
+
import type { CiMain, CiWorkspaceConfig } from '../ci.main.runtime';
|
|
6
|
+
/**
|
|
7
|
+
* Refuse a `--dry-run` whose plan would be computed by force-checking-out branches over uncommitted
|
|
8
|
+
* work: the main-scope drift is a diff, so the tree IS written before the dry-run return. A dry run
|
|
9
|
+
* promises to write nothing, so it may not discard either — with a clean tree the write-then-restore
|
|
10
|
+
* loses nothing.
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertCleanForDryRun(dirtyFiles: string[]): void;
|
|
13
|
+
/** Everything the sync orchestration needs from `CiMain`, passed explicitly. */
|
|
14
|
+
export type SyncOrchestratorDeps = {
|
|
15
|
+
ci: CiMain;
|
|
16
|
+
workspace: Workspace;
|
|
17
|
+
logger: Logger;
|
|
18
|
+
lanes: LanesMain;
|
|
19
|
+
checkout: CheckoutMain;
|
|
20
|
+
config: CiWorkspaceConfig;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Routing and aggregation for `bit ci sync`: decides which targets a run visits and how their
|
|
24
|
+
* summaries combine. What reconciling a target means lives in the two executors, both stateless and
|
|
25
|
+
* idempotent, so the trigger only ever decides when the reconciler runs.
|
|
26
|
+
*/
|
|
27
|
+
export declare class SyncOrchestrator {
|
|
28
|
+
private deps;
|
|
29
|
+
constructor(deps: SyncOrchestratorDeps);
|
|
30
|
+
/**
|
|
31
|
+
* `bit ci sync` — reconcile Bit lanes and the main scope with git branches and pull requests.
|
|
32
|
+
* Returns the per-target summary lines; throws the joined summary after the loop when any target
|
|
33
|
+
* halted or was refused, so CI exits non-zero while every other target was still attempted. A skip
|
|
34
|
+
* is neither — it reports itself and leaves the run green.
|
|
35
|
+
*/
|
|
36
|
+
sync(opts?: {
|
|
37
|
+
lane?: string;
|
|
38
|
+
branch?: string;
|
|
39
|
+
all?: boolean;
|
|
40
|
+
main?: boolean;
|
|
41
|
+
dryRun?: boolean;
|
|
42
|
+
init?: boolean;
|
|
43
|
+
}): Promise<string>;
|
|
44
|
+
/**
|
|
45
|
+
* `bit ci sync --init` — one-command onboarding: scaffolds the two workflow files, adds the sync
|
|
46
|
+
* config block to workspace.jsonc if missing, and prints the manual-steps checklist. Safe to
|
|
47
|
+
* re-run: existing files are skipped, an existing config block is left alone; it never reconciles.
|
|
48
|
+
*/
|
|
49
|
+
private syncInit;
|
|
50
|
+
/**
|
|
51
|
+
* Add `"sync": {}` under `"teambit.git/ci"` in workspace.jsonc if that key isn't there yet; returns
|
|
52
|
+
* whether it wrote. The comment-preserving `WorkspaceConfig` API merges into existing
|
|
53
|
+
* `teambit.git/ci` config rather than clobbering it.
|
|
54
|
+
*/
|
|
55
|
+
private ensureSyncConfigBlock;
|
|
56
|
+
/**
|
|
57
|
+
* Why this lane name must not be handed to the lane executor, or undefined when it's syncable. The
|
|
58
|
+
* mapped branch is checked too — the lane path must never write the two branches the main-scope
|
|
59
|
+
* path owns.
|
|
60
|
+
*/
|
|
61
|
+
private laneNotSyncableReason;
|
|
62
|
+
/**
|
|
63
|
+
* The lane names to reconcile on an `--all` run (sorted), plus one HALTED line per enumeration
|
|
64
|
+
* failure. The list is the union of the remote's lanes AND the lane-mapped branches on `origin` —
|
|
65
|
+
* a lane deleted on bit.cloud is gone from the first source, and only the branch side can make
|
|
66
|
+
* `close-pr` fire for it. An enumeration failure is reported rather than thrown, so the half that
|
|
67
|
+
* did enumerate is still reconciled while the run exits non-zero.
|
|
68
|
+
*/
|
|
69
|
+
private listLanesToSync;
|
|
70
|
+
/**
|
|
71
|
+
* Join the per-target summary lines, and turn any halt into a non-zero exit — the executors never
|
|
72
|
+
* throw, so this is the single place "needs a human" becomes visible to CI. `BitError` rather than
|
|
73
|
+
* `Error`: bit reports a plain `Error` as an internal failure (stack trace, Sentry FATAL).
|
|
74
|
+
*/
|
|
75
|
+
private summarizeSync;
|
|
76
|
+
}
|