@teambit/ci 1.0.485 → 1.0.486

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.
Files changed (97) hide show
  1. package/ci.docs.mdx +344 -83
  2. package/commands/sync.cmd.ts +77 -0
  3. package/dist/ci.docs.mdx +344 -83
  4. package/dist/ci.main.runtime.d.ts +67 -6
  5. package/dist/ci.main.runtime.js +152 -25
  6. package/dist/ci.main.runtime.js.map +1 -1
  7. package/dist/commands/sync.cmd.d.ts +34 -0
  8. package/dist/commands/sync.cmd.js +57 -0
  9. package/dist/commands/sync.cmd.js.map +1 -0
  10. package/dist/git.d.ts +11 -1
  11. package/dist/git.js +11 -1
  12. package/dist/git.js.map +1 -1
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +26 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/{preview-1785858957533.js → preview-1785864931304.js} +1 -1
  17. package/dist/sync/git-host-provider.d.ts +47 -0
  18. package/dist/sync/git-host-provider.js +54 -0
  19. package/dist/sync/git-host-provider.js.map +1 -0
  20. package/dist/sync/git-host-provider.spec.d.ts +1 -0
  21. package/dist/sync/git-host-provider.spec.js +171 -0
  22. package/dist/sync/git-host-provider.spec.js.map +1 -0
  23. package/dist/sync/git-ops.d.ts +171 -0
  24. package/dist/sync/git-ops.js +354 -0
  25. package/dist/sync/git-ops.js.map +1 -0
  26. package/dist/sync/git-ops.spec.d.ts +1 -0
  27. package/dist/sync/git-ops.spec.js +476 -0
  28. package/dist/sync/git-ops.spec.js.map +1 -0
  29. package/dist/sync/github-client.d.ts +79 -0
  30. package/dist/sync/github-client.js +198 -0
  31. package/dist/sync/github-client.js.map +1 -0
  32. package/dist/sync/github-client.spec.d.ts +1 -0
  33. package/dist/sync/github-client.spec.js +205 -0
  34. package/dist/sync/github-client.spec.js.map +1 -0
  35. package/dist/sync/init-scaffold.d.ts +40 -0
  36. package/dist/sync/init-scaffold.js +326 -0
  37. package/dist/sync/init-scaffold.js.map +1 -0
  38. package/dist/sync/init-scaffold.spec.d.ts +1 -0
  39. package/dist/sync/init-scaffold.spec.js +169 -0
  40. package/dist/sync/init-scaffold.spec.js.map +1 -0
  41. package/dist/sync/lane-sync-executor.d.ts +256 -0
  42. package/dist/sync/lane-sync-executor.js +1249 -0
  43. package/dist/sync/lane-sync-executor.js.map +1 -0
  44. package/dist/sync/lane-sync-executor.spec.d.ts +1 -0
  45. package/dist/sync/lane-sync-executor.spec.js +416 -0
  46. package/dist/sync/lane-sync-executor.spec.js.map +1 -0
  47. package/dist/sync/main-sync-executor.d.ts +62 -0
  48. package/dist/sync/main-sync-executor.js +305 -0
  49. package/dist/sync/main-sync-executor.js.map +1 -0
  50. package/dist/sync/sync-config.d.ts +83 -0
  51. package/dist/sync/sync-config.js +187 -0
  52. package/dist/sync/sync-config.js.map +1 -0
  53. package/dist/sync/sync-config.spec.d.ts +1 -0
  54. package/dist/sync/sync-config.spec.js +254 -0
  55. package/dist/sync/sync-config.spec.js.map +1 -0
  56. package/dist/sync/sync-orchestrator.d.ts +76 -0
  57. package/dist/sync/sync-orchestrator.js +396 -0
  58. package/dist/sync/sync-orchestrator.js.map +1 -0
  59. package/dist/sync/sync-orchestrator.spec.d.ts +1 -0
  60. package/dist/sync/sync-orchestrator.spec.js +155 -0
  61. package/dist/sync/sync-orchestrator.spec.js.map +1 -0
  62. package/dist/sync/sync-planner.d.ts +60 -0
  63. package/dist/sync/sync-planner.js +114 -0
  64. package/dist/sync/sync-planner.js.map +1 -0
  65. package/dist/sync/sync-planner.spec.d.ts +1 -0
  66. package/dist/sync/sync-planner.spec.js +210 -0
  67. package/dist/sync/sync-planner.spec.js.map +1 -0
  68. package/dist/sync/sync-state.d.ts +94 -0
  69. package/dist/sync/sync-state.js +178 -0
  70. package/dist/sync/sync-state.js.map +1 -0
  71. package/dist/sync/sync-state.spec.d.ts +1 -0
  72. package/dist/sync/sync-state.spec.js +271 -0
  73. package/dist/sync/sync-state.spec.js.map +1 -0
  74. package/dist/sync/workspace-lane.d.ts +13 -0
  75. package/dist/sync/workspace-lane.js +27 -0
  76. package/dist/sync/workspace-lane.js.map +1 -0
  77. package/package.json +17 -12
  78. package/sync/git-host-provider.spec.ts +197 -0
  79. package/sync/git-host-provider.ts +89 -0
  80. package/sync/git-ops.spec.ts +506 -0
  81. package/sync/git-ops.ts +334 -0
  82. package/sync/github-client.spec.ts +174 -0
  83. package/sync/github-client.ts +209 -0
  84. package/sync/init-scaffold.spec.ts +142 -0
  85. package/sync/init-scaffold.ts +340 -0
  86. package/sync/lane-sync-executor.spec.ts +389 -0
  87. package/sync/lane-sync-executor.ts +1363 -0
  88. package/sync/main-sync-executor.ts +321 -0
  89. package/sync/sync-config.spec.ts +283 -0
  90. package/sync/sync-config.ts +214 -0
  91. package/sync/sync-orchestrator.spec.ts +120 -0
  92. package/sync/sync-orchestrator.ts +386 -0
  93. package/sync/sync-planner.spec.ts +126 -0
  94. package/sync/sync-planner.ts +99 -0
  95. package/sync/sync-state.spec.ts +263 -0
  96. package/sync/sync-state.ts +176 -0
  97. package/sync/workspace-lane.ts +20 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Git primitives shared by the lane and main sync executors; each encodes a decision that must stay
3
+ * identical on both paths.
4
+ */
5
+ /** paths the sync executors never treat as workspace content — see `cleanUntrackedScoped` */
6
+ export declare const SYNC_EXCLUDED_PATHS: string[];
7
+ /**
8
+ * How a raw git invocation is run, injectable so a helper whose whole content is its argv can be unit
9
+ * tested without a repository. Production always uses {@link realGitRaw}.
10
+ */
11
+ export type GitArgsRunner = (args: string[]) => Promise<unknown>;
12
+ export declare const realGitRaw: GitArgsRunner;
13
+ /**
14
+ * Remove untracked files, leaving the local bit scope and installed packages alone. No `-x` (ignored
15
+ * files stay), and `.bit` / `node_modules` are excluded explicitly — without that, the clean deletes
16
+ * the local scope in a workspace whose `.gitignore` lacks Bit's block.
17
+ */
18
+ export declare function cleanUntrackedScoped(run?: GitArgsRunner): Promise<void>;
19
+ /**
20
+ * Refuse a branch name git would read as an option or reject as a ref. Not every branch reaching the
21
+ * checkout comes from validated config: the default branch is derived from the remote
22
+ * (`getDefaultBranchName`), so the primitive that builds the argv is where the guard belongs — every
23
+ * caller inherits it.
24
+ */
25
+ export declare function assertCheckoutableBranch(branch: string): void;
26
+ /**
27
+ * Put the working tree on `branch` holding nothing but that commit's content, then make the workspace
28
+ * re-read the checked-out `.bitmap`. Order matters: `checkout -f` (leftover tracked changes must not
29
+ * abort the target), then the scoped clean (a forced checkout leaves untracked files in place, and
30
+ * every commit path stages with `-A`), then the reload so it reads the pristine tree. With
31
+ * `startPoint`, `-B` creates-or-resets the branch — refused when the local branch holds commits no
32
+ * remote contains, because moving it would orphan unpushed work; the predicate is remote containment,
33
+ * not start-point ancestry (a fully pushed tip loses nothing in either legitimate reset shape).
34
+ */
35
+ export declare function checkoutPristine(branch: string, startPoint: string | undefined, reload: () => Promise<void>, run?: GitArgsRunner): Promise<void>;
36
+ /**
37
+ * Whether the branch exists locally. Judged by output, never by whether the call threw: simple-git's
38
+ * `raw` resolves with empty output on some non-zero exits instead of rejecting.
39
+ */
40
+ export declare function localBranchExists(branch: string, run?: GitArgsRunner): Promise<boolean>;
41
+ /**
42
+ * `checkoutPristine` for the restore paths: a start point only when the branch is absent locally
43
+ * (detached-HEAD/single-branch checkouts) — passing `origin/<branch>` when it exists would `-B`-reset
44
+ * the developer's default branch and discard their unpushed commits.
45
+ */
46
+ export declare function checkoutPristineRestore(branch: string, reload: () => Promise<void>, run?: GitArgsRunner): Promise<void>;
47
+ /**
48
+ * Stage every change except `.bit` / `node_modules` (a bare `add -A` would commit the local scope when
49
+ * `.gitignore` lacks Bit's block). The `:(exclude)` pathspecs make git skip those trees instead of
50
+ * staging them for a reset to undo; `git add` refuses them when a named path IS ignored — the case where
51
+ * the traversal is already cheap — so the add-then-reset pair is the fallback. Both leave the same index,
52
+ * and `git reset -- <paths>` is a no-op for paths absent from it, so neither fallback command can fail.
53
+ */
54
+ export declare function addAllExceptScopeAndModules(run?: GitArgsRunner): Promise<void>;
55
+ /**
56
+ * Whether a `git status` path is one the sync executors never treat as workspace content. Applied
57
+ * everywhere a status is interpreted — otherwise `.bit`/`node_modules` files count as drift when
58
+ * `.gitignore` lacks Bit's block.
59
+ */
60
+ export declare function isNonContentPath(path: string): boolean;
61
+ /**
62
+ * The refspec every sync fetch passes **explicitly**, overriding whatever `remote.origin.fetch` this
63
+ * checkout was configured with.
64
+ */
65
+ export declare const ALL_HEADS_REFSPEC = "+refs/heads/*:refs/remotes/origin/*";
66
+ /**
67
+ * Fetch `origin` so every branch it has is available as `refs/remotes/origin/<branch>`. A bare fetch
68
+ * honours the checkout's configured `remote.origin.fetch`, which in a single-branch clone updates
69
+ * exactly one ref while enumeration (`ls-remote`) sees every branch; an explicit refspec overrides the
70
+ * config. Does not make shallow clones work — depth is a different axis.
71
+ */
72
+ export declare function fetchRemoteHeads(run?: GitArgsRunner): Promise<void>;
73
+ /** Refspec that updates exactly one remote-tracking ref — a mid-run re-read of a single branch. */
74
+ export declare function singleHeadRefspec(branch: string): string;
75
+ /**
76
+ * `origin/<branch>`'s tip as the remote has it NOW: every other ref a run reads comes from one fetch at
77
+ * its start. Undefined when the branch is gone or unreadable — never a stale sha, because the answer
78
+ * decides whether a branch deletion is still safe.
79
+ */
80
+ export declare function refetchBranchTip(branch: string, run?: GitArgsRunner): Promise<string | undefined>;
81
+ /**
82
+ * `git push` argv that deletes a remote branch only while its tip is still `expectedTipSha`. Full
83
+ * `:refs/heads/<b>` refspec so the branch name can never be read as an option; the lease makes the server
84
+ * itself refuse a racing update rather than lose it.
85
+ */
86
+ export declare function deleteBranchArgs(branch: string, expectedTipSha: string): string[];
87
+ /**
88
+ * Whether a failed push was the lease refusing. The wording varies by git version and transport:
89
+ * "stale info" (client-side check), "is at X but expected Y" (receive-pack), and "incorrect old value
90
+ * provided" (the local/file transport, which is what CI's e2e remotes use). Over-matching is the safe
91
+ * direction — the only consequence is reporting a branch as kept rather than as a failed delete.
92
+ */
93
+ export declare function isStaleLeaseRejection(message: string): boolean;
94
+ /** Whether `origin` has the given branch. Assumes a `git fetch` isn't required (uses `ls-remote`). */
95
+ export declare function branchExistsOnRemote(branch: string): Promise<boolean>;
96
+ /**
97
+ * Every branch `origin` has, by short name. `ls-remote` rather than `git branch -r` so the answer
98
+ * cannot be narrowed by the checkout's refspec or staleness.
99
+ */
100
+ export declare function listRemoteBranches(): Promise<string[]>;
101
+ /**
102
+ * Is `maybeAncestor` reachable from `descendant` (true for identical commits)? Not `merge-base
103
+ * --is-ancestor`: it answers via the exit code, and simple-git's `raw` resolves on exit 1, so "not an
104
+ * ancestor" would read as yes. Throws when the commits share no history or a revision cannot resolve.
105
+ */
106
+ export declare function isAncestor(maybeAncestor: string, descendant: string): Promise<boolean>;
107
+ /** The prefix `git symbolic-ref refs/remotes/origin/HEAD` puts in front of the default branch name. */
108
+ export declare const ORIGIN_HEAD_REF_PREFIX = "refs/remotes/origin/";
109
+ /**
110
+ * The default branch name out of `git symbolic-ref refs/remotes/origin/HEAD` output, or undefined for
111
+ * an unrecognised shape. Strips the prefix; never splits on `/` — branch names may contain slashes
112
+ * (`release/main`), and truncating would protect the wrong branch.
113
+ */
114
+ export declare function parseOriginHeadRef(symbolicRefOutput: string): string | undefined;
115
+ /**
116
+ * The default branch name out of `git ls-remote --symref origin HEAD` output, or undefined when no
117
+ * symref line is present (some servers omit it). Same slash discipline as {@link parseOriginHeadRef}.
118
+ */
119
+ export declare function parseLsRemoteSymref(lsRemoteOutput: string): string | undefined;
120
+ /**
121
+ * The remote's own answer for its default branch. Unlike the local `origin/HEAD` symref or enumerating
122
+ * `origin/*`, this cannot be stale or narrowed — only unanswered (undefined hands on to the fallback).
123
+ */
124
+ export declare function remoteHeadBranch(run?: GitArgsRunner): Promise<string | undefined>;
125
+ /**
126
+ * The git repository's root, or undefined outside a repo. A bit workspace is not necessarily the repo
127
+ * root, and GitHub only discovers workflows at `<repo>/.github/workflows`.
128
+ */
129
+ export declare function gitRepoRoot(): Promise<string | undefined>;
130
+ /**
131
+ * Reading `git config`, injectable so the identity decision can be unit tested without a repository.
132
+ * There is deliberately no writer — see {@link resolveGitIdentity}.
133
+ */
134
+ export type GitConfigIO = {
135
+ /** the configured value, or undefined when unset (or when git cannot answer) */
136
+ get(key: string): Promise<string | undefined>;
137
+ };
138
+ export declare const realGitConfigIO: GitConfigIO;
139
+ /**
140
+ * The identity the sync commits with when nothing else says otherwise. A contract: the scaffolded
141
+ * workflow templates (`init-scaffold.ts`) and the GitHub Action's defaults must carry the same pair,
142
+ * or the same repository commits under different authors depending on entry point.
143
+ */
144
+ export declare const DEFAULT_GIT_USER_NAME = "bit-sync[bot]";
145
+ export declare const DEFAULT_GIT_USER_EMAIL = "bit-sync[bot]@users.noreply.github.com";
146
+ export type GitIdentity = {
147
+ name: string;
148
+ email: string;
149
+ };
150
+ /**
151
+ * Who the sync commits as. Read-only on purpose: writing these keys into the repository's config
152
+ * would outlive the run and re-author the developer's own later commits as the bot. Precedence per
153
+ * key: configured git identity > `GIT_USER_NAME`/`GIT_USER_EMAIL` > default.
154
+ */
155
+ export declare function resolveGitIdentity(io?: GitConfigIO, env?: NodeJS.ProcessEnv): Promise<GitIdentity>;
156
+ /** The `git -c` pair that applies an identity to ONE invocation, leaving the repo's config alone. */
157
+ export declare function identityArgs(identity: GitIdentity): string[];
158
+ export type IdentityDeps = {
159
+ run?: GitArgsRunner;
160
+ io?: GitConfigIO;
161
+ env?: NodeJS.ProcessEnv;
162
+ };
163
+ /**
164
+ * Run a git command that may author a commit. Every such command needs the identity — a fresh CI
165
+ * checkout has none, and git refuses to commit ("Please tell me who you are") without one.
166
+ */
167
+ export declare function gitWithIdentity(args: string[], deps?: IdentityDeps): Promise<string>;
168
+ /** `git commit` under the sync identity; `extraArgs` carries flags such as `--allow-empty`. */
169
+ export declare function commitWithIdentity(message: string, opts?: IdentityDeps & {
170
+ extraArgs?: string[];
171
+ }): Promise<void>;
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SYNC_EXCLUDED_PATHS = exports.ORIGIN_HEAD_REF_PREFIX = exports.DEFAULT_GIT_USER_NAME = exports.DEFAULT_GIT_USER_EMAIL = exports.ALL_HEADS_REFSPEC = void 0;
7
+ exports.addAllExceptScopeAndModules = addAllExceptScopeAndModules;
8
+ exports.assertCheckoutableBranch = assertCheckoutableBranch;
9
+ exports.branchExistsOnRemote = branchExistsOnRemote;
10
+ exports.checkoutPristine = checkoutPristine;
11
+ exports.checkoutPristineRestore = checkoutPristineRestore;
12
+ exports.cleanUntrackedScoped = cleanUntrackedScoped;
13
+ exports.commitWithIdentity = commitWithIdentity;
14
+ exports.deleteBranchArgs = deleteBranchArgs;
15
+ exports.fetchRemoteHeads = fetchRemoteHeads;
16
+ exports.gitRepoRoot = gitRepoRoot;
17
+ exports.gitWithIdentity = gitWithIdentity;
18
+ exports.identityArgs = identityArgs;
19
+ exports.isAncestor = isAncestor;
20
+ exports.isNonContentPath = isNonContentPath;
21
+ exports.isStaleLeaseRejection = isStaleLeaseRejection;
22
+ exports.listRemoteBranches = listRemoteBranches;
23
+ exports.localBranchExists = localBranchExists;
24
+ exports.parseLsRemoteSymref = parseLsRemoteSymref;
25
+ exports.parseOriginHeadRef = parseOriginHeadRef;
26
+ exports.realGitRaw = exports.realGitConfigIO = void 0;
27
+ exports.refetchBranchTip = refetchBranchTip;
28
+ exports.remoteHeadBranch = remoteHeadBranch;
29
+ exports.resolveGitIdentity = resolveGitIdentity;
30
+ exports.singleHeadRefspec = singleHeadRefspec;
31
+ function _bitError() {
32
+ const data = require("@teambit/bit-error");
33
+ _bitError = function () {
34
+ return data;
35
+ };
36
+ return data;
37
+ }
38
+ function _git() {
39
+ const data = require("../git");
40
+ _git = function () {
41
+ return data;
42
+ };
43
+ return data;
44
+ }
45
+ function _syncConfig() {
46
+ const data = require("./sync-config");
47
+ _syncConfig = function () {
48
+ return data;
49
+ };
50
+ return data;
51
+ }
52
+ const _excluded = ["extraArgs"];
53
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
54
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
55
+ /**
56
+ * Git primitives shared by the lane and main sync executors; each encodes a decision that must stay
57
+ * identical on both paths.
58
+ */
59
+
60
+ /** paths the sync executors never treat as workspace content — see `cleanUntrackedScoped` */
61
+ const SYNC_EXCLUDED_PATHS = exports.SYNC_EXCLUDED_PATHS = ['.bit', 'node_modules'];
62
+
63
+ /**
64
+ * How a raw git invocation is run, injectable so a helper whose whole content is its argv can be unit
65
+ * tested without a repository. Production always uses {@link realGitRaw}.
66
+ */
67
+
68
+ const realGitRaw = args => _git().git.raw(args);
69
+
70
+ /**
71
+ * Remove untracked files, leaving the local bit scope and installed packages alone. No `-x` (ignored
72
+ * files stay), and `.bit` / `node_modules` are excluded explicitly — without that, the clean deletes
73
+ * the local scope in a workspace whose `.gitignore` lacks Bit's block.
74
+ */
75
+ exports.realGitRaw = realGitRaw;
76
+ async function cleanUntrackedScoped(run = realGitRaw) {
77
+ await run(['clean', '-fd', ...SYNC_EXCLUDED_PATHS.flatMap(path => ['-e', path])]);
78
+ }
79
+
80
+ /**
81
+ * Refuse a branch name git would read as an option or reject as a ref. Not every branch reaching the
82
+ * checkout comes from validated config: the default branch is derived from the remote
83
+ * (`getDefaultBranchName`), so the primitive that builds the argv is where the guard belongs — every
84
+ * caller inherits it.
85
+ */
86
+ function assertCheckoutableBranch(branch) {
87
+ const problem = (0, _syncConfig().validateBranchName)(branch);
88
+ if (problem) {
89
+ throw new (_bitError().BitError)(`bit ci sync cannot check out "${branch}": ${problem}. Rename the branch, or set the intended name ` + `explicitly in the "teambit.git/ci" sync config.`);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Put the working tree on `branch` holding nothing but that commit's content, then make the workspace
95
+ * re-read the checked-out `.bitmap`. Order matters: `checkout -f` (leftover tracked changes must not
96
+ * abort the target), then the scoped clean (a forced checkout leaves untracked files in place, and
97
+ * every commit path stages with `-A`), then the reload so it reads the pristine tree. With
98
+ * `startPoint`, `-B` creates-or-resets the branch — refused when the local branch holds commits no
99
+ * remote contains, because moving it would orphan unpushed work; the predicate is remote containment,
100
+ * not start-point ancestry (a fully pushed tip loses nothing in either legitimate reset shape).
101
+ */
102
+ async function checkoutPristine(branch, startPoint, reload, run = realGitRaw) {
103
+ assertCheckoutableBranch(branch);
104
+ if (startPoint && (await localBranchExists(branch, run))) {
105
+ const containedIn = String((await run(['branch', '-r', '--contains', `refs/heads/${branch}`])) ?? '').trim();
106
+ if (!containedIn) {
107
+ throw new Error(`local branch "${branch}" has commits that no remote branch contains — resetting it would ` + `orphan them (recoverable only from the reflog). Push or back up the local branch, delete ` + `it, or run from a clean checkout`);
108
+ }
109
+ }
110
+ await run(startPoint ? ['checkout', '-f', '-B', branch, startPoint] : ['checkout', '-f', branch]);
111
+ await cleanUntrackedScoped(run);
112
+ await reload();
113
+ }
114
+
115
+ /**
116
+ * Whether the branch exists locally. Judged by output, never by whether the call threw: simple-git's
117
+ * `raw` resolves with empty output on some non-zero exits instead of rejecting.
118
+ */
119
+ async function localBranchExists(branch, run = realGitRaw) {
120
+ const out = await run(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`]).catch(() => '');
121
+ return String(out ?? '').trim().length > 0;
122
+ }
123
+
124
+ /**
125
+ * `checkoutPristine` for the restore paths: a start point only when the branch is absent locally
126
+ * (detached-HEAD/single-branch checkouts) — passing `origin/<branch>` when it exists would `-B`-reset
127
+ * the developer's default branch and discard their unpushed commits.
128
+ */
129
+ async function checkoutPristineRestore(branch, reload, run = realGitRaw) {
130
+ const startPoint = (await localBranchExists(branch, run)) ? undefined : `origin/${branch}`;
131
+ await checkoutPristine(branch, startPoint, reload, run);
132
+ }
133
+
134
+ /**
135
+ * Stage every change except `.bit` / `node_modules` (a bare `add -A` would commit the local scope when
136
+ * `.gitignore` lacks Bit's block). The `:(exclude)` pathspecs make git skip those trees instead of
137
+ * staging them for a reset to undo; `git add` refuses them when a named path IS ignored — the case where
138
+ * the traversal is already cheap — so the add-then-reset pair is the fallback. Both leave the same index,
139
+ * and `git reset -- <paths>` is a no-op for paths absent from it, so neither fallback command can fail.
140
+ */
141
+ async function addAllExceptScopeAndModules(run = realGitRaw) {
142
+ const stageAll = ['add', '-A', '--', '.'];
143
+ try {
144
+ await run([...stageAll, ...SYNC_EXCLUDED_PATHS.map(path => `:(exclude)${path}`)]);
145
+ } catch {
146
+ await run(stageAll);
147
+ await run(['reset', '-q', '--', ...SYNC_EXCLUDED_PATHS]);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Whether a `git status` path is one the sync executors never treat as workspace content. Applied
153
+ * everywhere a status is interpreted — otherwise `.bit`/`node_modules` files count as drift when
154
+ * `.gitignore` lacks Bit's block.
155
+ */
156
+ function isNonContentPath(path) {
157
+ return SYNC_EXCLUDED_PATHS.some(excluded => path === excluded || path.startsWith(`${excluded}/`));
158
+ }
159
+
160
+ /**
161
+ * The refspec every sync fetch passes **explicitly**, overriding whatever `remote.origin.fetch` this
162
+ * checkout was configured with.
163
+ */
164
+ const ALL_HEADS_REFSPEC = exports.ALL_HEADS_REFSPEC = '+refs/heads/*:refs/remotes/origin/*';
165
+
166
+ /**
167
+ * Fetch `origin` so every branch it has is available as `refs/remotes/origin/<branch>`. A bare fetch
168
+ * honours the checkout's configured `remote.origin.fetch`, which in a single-branch clone updates
169
+ * exactly one ref while enumeration (`ls-remote`) sees every branch; an explicit refspec overrides the
170
+ * config. Does not make shallow clones work — depth is a different axis.
171
+ */
172
+ async function fetchRemoteHeads(run = realGitRaw) {
173
+ await run(['fetch', 'origin', ALL_HEADS_REFSPEC]);
174
+ }
175
+
176
+ /** Refspec that updates exactly one remote-tracking ref — a mid-run re-read of a single branch. */
177
+ function singleHeadRefspec(branch) {
178
+ return `+refs/heads/${branch}:refs/remotes/origin/${branch}`;
179
+ }
180
+
181
+ /**
182
+ * `origin/<branch>`'s tip as the remote has it NOW: every other ref a run reads comes from one fetch at
183
+ * its start. Undefined when the branch is gone or unreadable — never a stale sha, because the answer
184
+ * decides whether a branch deletion is still safe.
185
+ */
186
+ async function refetchBranchTip(branch, run = realGitRaw) {
187
+ try {
188
+ await run(['fetch', 'origin', singleHeadRefspec(branch)]);
189
+ const out = await run(['rev-parse', `refs/remotes/origin/${branch}`]);
190
+ return String(out ?? '').trim() || undefined;
191
+ } catch {
192
+ return undefined;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * `git push` argv that deletes a remote branch only while its tip is still `expectedTipSha`. Full
198
+ * `:refs/heads/<b>` refspec so the branch name can never be read as an option; the lease makes the server
199
+ * itself refuse a racing update rather than lose it.
200
+ */
201
+ function deleteBranchArgs(branch, expectedTipSha) {
202
+ return ['origin', `--force-with-lease=refs/heads/${branch}:${expectedTipSha}`, `:refs/heads/${branch}`];
203
+ }
204
+
205
+ /**
206
+ * Whether a failed push was the lease refusing. The wording varies by git version and transport:
207
+ * "stale info" (client-side check), "is at X but expected Y" (receive-pack), and "incorrect old value
208
+ * provided" (the local/file transport, which is what CI's e2e remotes use). Over-matching is the safe
209
+ * direction — the only consequence is reporting a branch as kept rather than as a failed delete.
210
+ */
211
+ function isStaleLeaseRejection(message) {
212
+ return /stale info|force-with-lease|but expected|incorrect old value/i.test(message);
213
+ }
214
+
215
+ /** Whether `origin` has the given branch. Assumes a `git fetch` isn't required (uses `ls-remote`). */
216
+ async function branchExistsOnRemote(branch) {
217
+ const out = await _git().git.raw(['ls-remote', '--heads', 'origin', branch]);
218
+ return out.trim().length > 0;
219
+ }
220
+
221
+ /**
222
+ * Every branch `origin` has, by short name. `ls-remote` rather than `git branch -r` so the answer
223
+ * cannot be narrowed by the checkout's refspec or staleness.
224
+ */
225
+ async function listRemoteBranches() {
226
+ const out = await _git().git.raw(['ls-remote', '--heads', 'origin']);
227
+ const prefix = 'refs/heads/';
228
+ return out.split('\n').map(line => line.split('\t')[1]?.trim()).filter(ref => Boolean(ref && ref.startsWith(prefix))).map(ref => ref.slice(prefix.length));
229
+ }
230
+
231
+ /**
232
+ * Is `maybeAncestor` reachable from `descendant` (true for identical commits)? Not `merge-base
233
+ * --is-ancestor`: it answers via the exit code, and simple-git's `raw` resolves on exit 1, so "not an
234
+ * ancestor" would read as yes. Throws when the commits share no history or a revision cannot resolve.
235
+ */
236
+ async function isAncestor(maybeAncestor, descendant) {
237
+ const ancestorSha = (await _git().git.revparse([maybeAncestor])).trim();
238
+ const mergeBase = (await _git().git.raw(['merge-base', maybeAncestor, descendant])).trim();
239
+ if (!ancestorSha || !mergeBase) throw new Error(`could not compare ${maybeAncestor} against ${descendant}`);
240
+ return ancestorSha === mergeBase;
241
+ }
242
+
243
+ /** The prefix `git symbolic-ref refs/remotes/origin/HEAD` puts in front of the default branch name. */
244
+ const ORIGIN_HEAD_REF_PREFIX = exports.ORIGIN_HEAD_REF_PREFIX = 'refs/remotes/origin/';
245
+
246
+ /**
247
+ * The default branch name out of `git symbolic-ref refs/remotes/origin/HEAD` output, or undefined for
248
+ * an unrecognised shape. Strips the prefix; never splits on `/` — branch names may contain slashes
249
+ * (`release/main`), and truncating would protect the wrong branch.
250
+ */
251
+ function parseOriginHeadRef(symbolicRefOutput) {
252
+ const ref = symbolicRefOutput.trim();
253
+ if (!ref.startsWith(ORIGIN_HEAD_REF_PREFIX)) return undefined;
254
+ return ref.slice(ORIGIN_HEAD_REF_PREFIX.length) || undefined;
255
+ }
256
+
257
+ /** The prefix a symref answer from `git ls-remote --symref origin HEAD` puts before the branch name. */
258
+ const LS_REMOTE_SYMREF_PREFIX = 'ref: refs/heads/';
259
+
260
+ /**
261
+ * The default branch name out of `git ls-remote --symref origin HEAD` output, or undefined when no
262
+ * symref line is present (some servers omit it). Same slash discipline as {@link parseOriginHeadRef}.
263
+ */
264
+ function parseLsRemoteSymref(lsRemoteOutput) {
265
+ const line = lsRemoteOutput.split('\n').find(l => l.startsWith(LS_REMOTE_SYMREF_PREFIX));
266
+ if (!line) return undefined;
267
+ const name = line.slice(LS_REMOTE_SYMREF_PREFIX.length).split('\t')[0].trim();
268
+ return name || undefined;
269
+ }
270
+
271
+ /**
272
+ * The remote's own answer for its default branch. Unlike the local `origin/HEAD` symref or enumerating
273
+ * `origin/*`, this cannot be stale or narrowed — only unanswered (undefined hands on to the fallback).
274
+ */
275
+ async function remoteHeadBranch(run = realGitRaw) {
276
+ try {
277
+ const out = await run(['ls-remote', '--symref', 'origin', 'HEAD']);
278
+ return parseLsRemoteSymref(String(out ?? ''));
279
+ } catch {
280
+ return undefined;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * The git repository's root, or undefined outside a repo. A bit workspace is not necessarily the repo
286
+ * root, and GitHub only discovers workflows at `<repo>/.github/workflows`.
287
+ */
288
+ async function gitRepoRoot() {
289
+ try {
290
+ const root = (await _git().git.raw(['rev-parse', '--show-toplevel'])).trim();
291
+ return root || undefined;
292
+ } catch {
293
+ return undefined;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Reading `git config`, injectable so the identity decision can be unit tested without a repository.
299
+ * There is deliberately no writer — see {@link resolveGitIdentity}.
300
+ */
301
+
302
+ const realGitConfigIO = exports.realGitConfigIO = {
303
+ get: key => _git().git.raw(['config', '--get', key]).then(out => out.trim().length ? out.trim() : undefined)
304
+ // A missing key and "not a git repository" both exit non-zero; both mean "no identity to keep".
305
+ .catch(() => undefined)
306
+ };
307
+
308
+ /**
309
+ * The identity the sync commits with when nothing else says otherwise. A contract: the scaffolded
310
+ * workflow templates (`init-scaffold.ts`) and the GitHub Action's defaults must carry the same pair,
311
+ * or the same repository commits under different authors depending on entry point.
312
+ */
313
+ const DEFAULT_GIT_USER_NAME = exports.DEFAULT_GIT_USER_NAME = 'bit-sync[bot]';
314
+ const DEFAULT_GIT_USER_EMAIL = exports.DEFAULT_GIT_USER_EMAIL = 'bit-sync[bot]@users.noreply.github.com';
315
+ /**
316
+ * Who the sync commits as. Read-only on purpose: writing these keys into the repository's config
317
+ * would outlive the run and re-author the developer's own later commits as the bot. Precedence per
318
+ * key: configured git identity > `GIT_USER_NAME`/`GIT_USER_EMAIL` > default.
319
+ */
320
+ async function resolveGitIdentity(io = realGitConfigIO, env = process.env) {
321
+ return {
322
+ name: (await io.get('user.name')) || env.GIT_USER_NAME || DEFAULT_GIT_USER_NAME,
323
+ email: (await io.get('user.email')) || env.GIT_USER_EMAIL || DEFAULT_GIT_USER_EMAIL
324
+ };
325
+ }
326
+
327
+ /** The `git -c` pair that applies an identity to ONE invocation, leaving the repo's config alone. */
328
+ function identityArgs(identity) {
329
+ return ['-c', `user.name=${identity.name}`, '-c', `user.email=${identity.email}`];
330
+ }
331
+ /**
332
+ * Run a git command that may author a commit. Every such command needs the identity — a fresh CI
333
+ * checkout has none, and git refuses to commit ("Please tell me who you are") without one.
334
+ */
335
+ async function gitWithIdentity(args, deps = {}) {
336
+ const {
337
+ run = realGitRaw,
338
+ io = realGitConfigIO,
339
+ env = process.env
340
+ } = deps;
341
+ const identity = await resolveGitIdentity(io, env);
342
+ return String((await run([...identityArgs(identity), ...args])) ?? '');
343
+ }
344
+
345
+ /** `git commit` under the sync identity; `extraArgs` carries flags such as `--allow-empty`. */
346
+ async function commitWithIdentity(message, opts = {}) {
347
+ const {
348
+ extraArgs = []
349
+ } = opts,
350
+ deps = _objectWithoutProperties(opts, _excluded);
351
+ await gitWithIdentity(['commit', '-m', message, ...extraArgs], deps);
352
+ }
353
+
354
+ //# sourceMappingURL=git-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_bitError","data","require","_git","_syncConfig","_excluded","_objectWithoutProperties","e","t","o","r","i","_objectWithoutPropertiesLoose","Object","getOwnPropertySymbols","n","length","indexOf","propertyIsEnumerable","call","hasOwnProperty","SYNC_EXCLUDED_PATHS","exports","realGitRaw","args","git","raw","cleanUntrackedScoped","run","flatMap","path","assertCheckoutableBranch","branch","problem","validateBranchName","BitError","checkoutPristine","startPoint","reload","localBranchExists","containedIn","String","trim","Error","out","catch","checkoutPristineRestore","undefined","addAllExceptScopeAndModules","stageAll","map","isNonContentPath","some","excluded","startsWith","ALL_HEADS_REFSPEC","fetchRemoteHeads","singleHeadRefspec","refetchBranchTip","deleteBranchArgs","expectedTipSha","isStaleLeaseRejection","message","test","branchExistsOnRemote","listRemoteBranches","prefix","split","line","filter","ref","Boolean","slice","isAncestor","maybeAncestor","descendant","ancestorSha","revparse","mergeBase","ORIGIN_HEAD_REF_PREFIX","parseOriginHeadRef","symbolicRefOutput","LS_REMOTE_SYMREF_PREFIX","parseLsRemoteSymref","lsRemoteOutput","find","l","name","remoteHeadBranch","gitRepoRoot","root","realGitConfigIO","get","key","then","DEFAULT_GIT_USER_NAME","DEFAULT_GIT_USER_EMAIL","resolveGitIdentity","io","env","process","GIT_USER_NAME","email","GIT_USER_EMAIL","identityArgs","identity","gitWithIdentity","deps","commitWithIdentity","opts","extraArgs"],"sources":["git-ops.ts"],"sourcesContent":["import { BitError } from '@teambit/bit-error';\nimport { git } from '../git';\nimport { validateBranchName } from './sync-config';\n\n/**\n * Git primitives shared by the lane and main sync executors; each encodes a decision that must stay\n * identical on both paths.\n */\n\n/** paths the sync executors never treat as workspace content — see `cleanUntrackedScoped` */\nexport const SYNC_EXCLUDED_PATHS = ['.bit', 'node_modules'];\n\n/**\n * How a raw git invocation is run, injectable so a helper whose whole content is its argv can be unit\n * tested without a repository. Production always uses {@link realGitRaw}.\n */\nexport type GitArgsRunner = (args: string[]) => Promise<unknown>;\n\nexport const realGitRaw: GitArgsRunner = (args) => git.raw(args);\n\n/**\n * Remove untracked files, leaving the local bit scope and installed packages alone. No `-x` (ignored\n * files stay), and `.bit` / `node_modules` are excluded explicitly — without that, the clean deletes\n * the local scope in a workspace whose `.gitignore` lacks Bit's block.\n */\nexport async function cleanUntrackedScoped(run: GitArgsRunner = realGitRaw): Promise<void> {\n await run(['clean', '-fd', ...SYNC_EXCLUDED_PATHS.flatMap((path) => ['-e', path])]);\n}\n\n/**\n * Refuse a branch name git would read as an option or reject as a ref. Not every branch reaching the\n * checkout comes from validated config: the default branch is derived from the remote\n * (`getDefaultBranchName`), so the primitive that builds the argv is where the guard belongs — every\n * caller inherits it.\n */\nexport function assertCheckoutableBranch(branch: string): void {\n const problem = validateBranchName(branch);\n if (problem) {\n throw new BitError(\n `bit ci sync cannot check out \"${branch}\": ${problem}. Rename the branch, or set the intended name ` +\n `explicitly in the \"teambit.git/ci\" sync config.`\n );\n }\n}\n\n/**\n * Put the working tree on `branch` holding nothing but that commit's content, then make the workspace\n * re-read the checked-out `.bitmap`. Order matters: `checkout -f` (leftover tracked changes must not\n * abort the target), then the scoped clean (a forced checkout leaves untracked files in place, and\n * every commit path stages with `-A`), then the reload so it reads the pristine tree. With\n * `startPoint`, `-B` creates-or-resets the branch — refused when the local branch holds commits no\n * remote contains, because moving it would orphan unpushed work; the predicate is remote containment,\n * not start-point ancestry (a fully pushed tip loses nothing in either legitimate reset shape).\n */\nexport async function checkoutPristine(\n branch: string,\n startPoint: string | undefined,\n reload: () => Promise<void>,\n run: GitArgsRunner = realGitRaw\n): Promise<void> {\n assertCheckoutableBranch(branch);\n if (startPoint && (await localBranchExists(branch, run))) {\n const containedIn = String((await run(['branch', '-r', '--contains', `refs/heads/${branch}`])) ?? '').trim();\n if (!containedIn) {\n throw new Error(\n `local branch \"${branch}\" has commits that no remote branch contains — resetting it would ` +\n `orphan them (recoverable only from the reflog). Push or back up the local branch, delete ` +\n `it, or run from a clean checkout`\n );\n }\n }\n await run(startPoint ? ['checkout', '-f', '-B', branch, startPoint] : ['checkout', '-f', branch]);\n await cleanUntrackedScoped(run);\n await reload();\n}\n\n/**\n * Whether the branch exists locally. Judged by output, never by whether the call threw: simple-git's\n * `raw` resolves with empty output on some non-zero exits instead of rejecting.\n */\nexport async function localBranchExists(branch: string, run: GitArgsRunner = realGitRaw): Promise<boolean> {\n const out = await run(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`]).catch(() => '');\n return String(out ?? '').trim().length > 0;\n}\n\n/**\n * `checkoutPristine` for the restore paths: a start point only when the branch is absent locally\n * (detached-HEAD/single-branch checkouts) — passing `origin/<branch>` when it exists would `-B`-reset\n * the developer's default branch and discard their unpushed commits.\n */\nexport async function checkoutPristineRestore(\n branch: string,\n reload: () => Promise<void>,\n run: GitArgsRunner = realGitRaw\n): Promise<void> {\n const startPoint = (await localBranchExists(branch, run)) ? undefined : `origin/${branch}`;\n await checkoutPristine(branch, startPoint, reload, run);\n}\n\n/**\n * Stage every change except `.bit` / `node_modules` (a bare `add -A` would commit the local scope when\n * `.gitignore` lacks Bit's block). The `:(exclude)` pathspecs make git skip those trees instead of\n * staging them for a reset to undo; `git add` refuses them when a named path IS ignored — the case where\n * the traversal is already cheap — so the add-then-reset pair is the fallback. Both leave the same index,\n * and `git reset -- <paths>` is a no-op for paths absent from it, so neither fallback command can fail.\n */\nexport async function addAllExceptScopeAndModules(run: GitArgsRunner = realGitRaw): Promise<void> {\n const stageAll = ['add', '-A', '--', '.'];\n try {\n await run([...stageAll, ...SYNC_EXCLUDED_PATHS.map((path) => `:(exclude)${path}`)]);\n } catch {\n await run(stageAll);\n await run(['reset', '-q', '--', ...SYNC_EXCLUDED_PATHS]);\n }\n}\n\n/**\n * Whether a `git status` path is one the sync executors never treat as workspace content. Applied\n * everywhere a status is interpreted — otherwise `.bit`/`node_modules` files count as drift when\n * `.gitignore` lacks Bit's block.\n */\nexport function isNonContentPath(path: string): boolean {\n return SYNC_EXCLUDED_PATHS.some((excluded) => path === excluded || path.startsWith(`${excluded}/`));\n}\n\n/**\n * The refspec every sync fetch passes **explicitly**, overriding whatever `remote.origin.fetch` this\n * checkout was configured with.\n */\nexport const ALL_HEADS_REFSPEC = '+refs/heads/*:refs/remotes/origin/*';\n\n/**\n * Fetch `origin` so every branch it has is available as `refs/remotes/origin/<branch>`. A bare fetch\n * honours the checkout's configured `remote.origin.fetch`, which in a single-branch clone updates\n * exactly one ref while enumeration (`ls-remote`) sees every branch; an explicit refspec overrides the\n * config. Does not make shallow clones work — depth is a different axis.\n */\nexport async function fetchRemoteHeads(run: GitArgsRunner = realGitRaw): Promise<void> {\n await run(['fetch', 'origin', ALL_HEADS_REFSPEC]);\n}\n\n/** Refspec that updates exactly one remote-tracking ref — a mid-run re-read of a single branch. */\nexport function singleHeadRefspec(branch: string): string {\n return `+refs/heads/${branch}:refs/remotes/origin/${branch}`;\n}\n\n/**\n * `origin/<branch>`'s tip as the remote has it NOW: every other ref a run reads comes from one fetch at\n * its start. Undefined when the branch is gone or unreadable — never a stale sha, because the answer\n * decides whether a branch deletion is still safe.\n */\nexport async function refetchBranchTip(branch: string, run: GitArgsRunner = realGitRaw): Promise<string | undefined> {\n try {\n await run(['fetch', 'origin', singleHeadRefspec(branch)]);\n const out = await run(['rev-parse', `refs/remotes/origin/${branch}`]);\n return String(out ?? '').trim() || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * `git push` argv that deletes a remote branch only while its tip is still `expectedTipSha`. Full\n * `:refs/heads/<b>` refspec so the branch name can never be read as an option; the lease makes the server\n * itself refuse a racing update rather than lose it.\n */\nexport function deleteBranchArgs(branch: string, expectedTipSha: string): string[] {\n return ['origin', `--force-with-lease=refs/heads/${branch}:${expectedTipSha}`, `:refs/heads/${branch}`];\n}\n\n/**\n * Whether a failed push was the lease refusing. The wording varies by git version and transport:\n * \"stale info\" (client-side check), \"is at X but expected Y\" (receive-pack), and \"incorrect old value\n * provided\" (the local/file transport, which is what CI's e2e remotes use). Over-matching is the safe\n * direction — the only consequence is reporting a branch as kept rather than as a failed delete.\n */\nexport function isStaleLeaseRejection(message: string): boolean {\n return /stale info|force-with-lease|but expected|incorrect old value/i.test(message);\n}\n\n/** Whether `origin` has the given branch. Assumes a `git fetch` isn't required (uses `ls-remote`). */\nexport async function branchExistsOnRemote(branch: string): Promise<boolean> {\n const out = await git.raw(['ls-remote', '--heads', 'origin', branch]);\n return out.trim().length > 0;\n}\n\n/**\n * Every branch `origin` has, by short name. `ls-remote` rather than `git branch -r` so the answer\n * cannot be narrowed by the checkout's refspec or staleness.\n */\nexport async function listRemoteBranches(): Promise<string[]> {\n const out = await git.raw(['ls-remote', '--heads', 'origin']);\n const prefix = 'refs/heads/';\n return out\n .split('\\n')\n .map((line) => line.split('\\t')[1]?.trim())\n .filter((ref): ref is string => Boolean(ref && ref.startsWith(prefix)))\n .map((ref) => ref.slice(prefix.length));\n}\n\n/**\n * Is `maybeAncestor` reachable from `descendant` (true for identical commits)? Not `merge-base\n * --is-ancestor`: it answers via the exit code, and simple-git's `raw` resolves on exit 1, so \"not an\n * ancestor\" would read as yes. Throws when the commits share no history or a revision cannot resolve.\n */\nexport async function isAncestor(maybeAncestor: string, descendant: string): Promise<boolean> {\n const ancestorSha = (await git.revparse([maybeAncestor])).trim();\n const mergeBase = (await git.raw(['merge-base', maybeAncestor, descendant])).trim();\n if (!ancestorSha || !mergeBase) throw new Error(`could not compare ${maybeAncestor} against ${descendant}`);\n return ancestorSha === mergeBase;\n}\n\n/** The prefix `git symbolic-ref refs/remotes/origin/HEAD` puts in front of the default branch name. */\nexport const ORIGIN_HEAD_REF_PREFIX = 'refs/remotes/origin/';\n\n/**\n * The default branch name out of `git symbolic-ref refs/remotes/origin/HEAD` output, or undefined for\n * an unrecognised shape. Strips the prefix; never splits on `/` — branch names may contain slashes\n * (`release/main`), and truncating would protect the wrong branch.\n */\nexport function parseOriginHeadRef(symbolicRefOutput: string): string | undefined {\n const ref = symbolicRefOutput.trim();\n if (!ref.startsWith(ORIGIN_HEAD_REF_PREFIX)) return undefined;\n return ref.slice(ORIGIN_HEAD_REF_PREFIX.length) || undefined;\n}\n\n/** The prefix a symref answer from `git ls-remote --symref origin HEAD` puts before the branch name. */\nconst LS_REMOTE_SYMREF_PREFIX = 'ref: refs/heads/';\n\n/**\n * The default branch name out of `git ls-remote --symref origin HEAD` output, or undefined when no\n * symref line is present (some servers omit it). Same slash discipline as {@link parseOriginHeadRef}.\n */\nexport function parseLsRemoteSymref(lsRemoteOutput: string): string | undefined {\n const line = lsRemoteOutput.split('\\n').find((l) => l.startsWith(LS_REMOTE_SYMREF_PREFIX));\n if (!line) return undefined;\n const name = line.slice(LS_REMOTE_SYMREF_PREFIX.length).split('\\t')[0].trim();\n return name || undefined;\n}\n\n/**\n * The remote's own answer for its default branch. Unlike the local `origin/HEAD` symref or enumerating\n * `origin/*`, this cannot be stale or narrowed — only unanswered (undefined hands on to the fallback).\n */\nexport async function remoteHeadBranch(run: GitArgsRunner = realGitRaw): Promise<string | undefined> {\n try {\n const out = await run(['ls-remote', '--symref', 'origin', 'HEAD']);\n return parseLsRemoteSymref(String(out ?? ''));\n } catch {\n return undefined;\n }\n}\n\n/**\n * The git repository's root, or undefined outside a repo. A bit workspace is not necessarily the repo\n * root, and GitHub only discovers workflows at `<repo>/.github/workflows`.\n */\nexport async function gitRepoRoot(): Promise<string | undefined> {\n try {\n const root = (await git.raw(['rev-parse', '--show-toplevel'])).trim();\n return root || undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Reading `git config`, injectable so the identity decision can be unit tested without a repository.\n * There is deliberately no writer — see {@link resolveGitIdentity}.\n */\nexport type GitConfigIO = {\n /** the configured value, or undefined when unset (or when git cannot answer) */\n get(key: string): Promise<string | undefined>;\n};\n\nexport const realGitConfigIO: GitConfigIO = {\n get: (key) =>\n git\n .raw(['config', '--get', key])\n .then((out) => (out.trim().length ? out.trim() : undefined))\n // A missing key and \"not a git repository\" both exit non-zero; both mean \"no identity to keep\".\n .catch(() => undefined),\n};\n\n/**\n * The identity the sync commits with when nothing else says otherwise. A contract: the scaffolded\n * workflow templates (`init-scaffold.ts`) and the GitHub Action's defaults must carry the same pair,\n * or the same repository commits under different authors depending on entry point.\n */\nexport const DEFAULT_GIT_USER_NAME = 'bit-sync[bot]';\nexport const DEFAULT_GIT_USER_EMAIL = 'bit-sync[bot]@users.noreply.github.com';\n\nexport type GitIdentity = { name: string; email: string };\n\n/**\n * Who the sync commits as. Read-only on purpose: writing these keys into the repository's config\n * would outlive the run and re-author the developer's own later commits as the bot. Precedence per\n * key: configured git identity > `GIT_USER_NAME`/`GIT_USER_EMAIL` > default.\n */\nexport async function resolveGitIdentity(\n io: GitConfigIO = realGitConfigIO,\n env: NodeJS.ProcessEnv = process.env\n): Promise<GitIdentity> {\n return {\n name: (await io.get('user.name')) || env.GIT_USER_NAME || DEFAULT_GIT_USER_NAME,\n email: (await io.get('user.email')) || env.GIT_USER_EMAIL || DEFAULT_GIT_USER_EMAIL,\n };\n}\n\n/** The `git -c` pair that applies an identity to ONE invocation, leaving the repo's config alone. */\nexport function identityArgs(identity: GitIdentity): string[] {\n return ['-c', `user.name=${identity.name}`, '-c', `user.email=${identity.email}`];\n}\n\nexport type IdentityDeps = { run?: GitArgsRunner; io?: GitConfigIO; env?: NodeJS.ProcessEnv };\n\n/**\n * Run a git command that may author a commit. Every such command needs the identity — a fresh CI\n * checkout has none, and git refuses to commit (\"Please tell me who you are\") without one.\n */\nexport async function gitWithIdentity(args: string[], deps: IdentityDeps = {}): Promise<string> {\n const { run = realGitRaw, io = realGitConfigIO, env = process.env } = deps;\n const identity = await resolveGitIdentity(io, env);\n return String((await run([...identityArgs(identity), ...args])) ?? '');\n}\n\n/** `git commit` under the sync identity; `extraArgs` carries flags such as `--allow-empty`. */\nexport async function commitWithIdentity(\n message: string,\n opts: IdentityDeps & { extraArgs?: string[] } = {}\n): Promise<void> {\n const { extraArgs = [], ...deps } = opts;\n await gitWithIdentity(['commit', '-m', message, ...extraArgs], deps);\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1 @@
1
+ export {};