threadlines 0.2.14 → 0.2.16

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.
@@ -23,7 +23,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.getBitbucketContext = getBitbucketContext;
25
25
  const simple_git_1 = __importDefault(require("simple-git"));
26
- const child_process_1 = require("child_process");
27
26
  const diff_1 = require("./diff");
28
27
  const logger_1 = require("../utils/logger");
29
28
  /**
@@ -43,8 +42,9 @@ async function getBitbucketContext(repoRoot) {
43
42
  const context = detectContext();
44
43
  const reviewContext = detectReviewContext();
45
44
  const commitSha = getCommitSha();
46
- // Get commit author (from git log - Bitbucket doesn't provide this as env var)
47
- const commitAuthor = await getCommitAuthor(repoRoot);
45
+ // Get commit author using shared function (git log)
46
+ // getCommitAuthor throws on failure with descriptive error
47
+ const commitAuthor = await (0, diff_1.getCommitAuthor)(repoRoot);
48
48
  // Get commit message if we have a SHA
49
49
  let commitMessage;
50
50
  if (commitSha) {
@@ -69,36 +69,26 @@ async function getBitbucketContext(repoRoot) {
69
69
  * Get diff for Bitbucket Pipelines environment
70
70
  *
71
71
  * Strategy:
72
- * - PR context: Fetch destination branch on-demand, compare source vs target (full PR diff)
72
+ * - PR context: Uses shared getPRDiff() - fetches destination branch, compares against HEAD
73
73
  * - Any push (main or feature branch): Compare last commit only (HEAD~1...HEAD)
74
74
  *
75
75
  * Note: We fetch the destination branch on-demand so this works with shallow clones.
76
76
  * Users don't need `depth: full` in their bitbucket-pipelines.yml.
77
77
  */
78
78
  async function getDiff(repoRoot) {
79
- const git = (0, simple_git_1.default)(repoRoot);
80
79
  const prId = process.env.BITBUCKET_PR_ID;
81
80
  const prDestinationBranch = process.env.BITBUCKET_PR_DESTINATION_BRANCH;
82
- // PR Context: Fetch destination branch and compare
81
+ // PR Context: Use shared getPRDiff() implementation
83
82
  if (prId) {
84
83
  if (!prDestinationBranch) {
85
84
  throw new Error('Bitbucket PR context detected but BITBUCKET_PR_DESTINATION_BRANCH is not set. ' +
86
85
  'This should be automatically provided by Bitbucket Pipelines.');
87
86
  }
88
- // Fetch destination branch on-demand (works with shallow clones)
89
- logger_1.logger.debug(`Fetching destination branch: origin/${prDestinationBranch}`);
90
- await git.fetch(['origin', `${prDestinationBranch}:refs/remotes/origin/${prDestinationBranch}`, '--depth=1']);
91
- logger_1.logger.debug(`PR #${prId}, using origin/${prDestinationBranch}...HEAD`);
92
- const diff = await git.diff([`origin/${prDestinationBranch}...HEAD`, '-U200']);
93
- const diffSummary = await git.diffSummary([`origin/${prDestinationBranch}...HEAD`]);
94
- const changedFiles = diffSummary.files.map(f => f.file);
95
- return { diff: diff || '', changedFiles };
87
+ return (0, diff_1.getPRDiff)(repoRoot, prDestinationBranch, logger_1.logger);
96
88
  }
97
89
  // Any push (main or feature branch): Review last commit only
98
- const diff = await git.diff(['HEAD~1...HEAD', '-U200']);
99
- const diffSummary = await git.diffSummary(['HEAD~1...HEAD']);
100
- const changedFiles = diffSummary.files.map(f => f.file);
101
- return { diff: diff || '', changedFiles };
90
+ // Use shared getCommitDiff (defaults to HEAD)
91
+ return (0, diff_1.getCommitDiff)(repoRoot);
102
92
  }
103
93
  /**
104
94
  * Gets repository name for Bitbucket Pipelines
@@ -172,32 +162,3 @@ function detectReviewContext() {
172
162
  function getCommitSha() {
173
163
  return process.env.BITBUCKET_COMMIT;
174
164
  }
175
- /**
176
- * Gets commit author for Bitbucket Pipelines
177
- *
178
- * Bitbucket doesn't provide commit author as an environment variable,
179
- * so we use git log to get it.
180
- *
181
- * This approach is verified by our test script (test-bitbucket-context.ts)
182
- * which successfully retrieves commit author in all scenarios:
183
- * - Direct commit to main
184
- * - Feature branch push
185
- * - PR pipeline
186
- * - Merge commit
187
- */
188
- async function getCommitAuthor(repoRoot) {
189
- // Use raw git commands - this is exactly what the test script uses and we know it works
190
- try {
191
- const name = (0, child_process_1.execSync)('git log -1 --format=%an', { encoding: 'utf-8', cwd: repoRoot }).trim();
192
- const email = (0, child_process_1.execSync)('git log -1 --format=%ae', { encoding: 'utf-8', cwd: repoRoot }).trim();
193
- if (!name || !email) {
194
- throw new Error('git log returned empty name or email');
195
- }
196
- return { name, email };
197
- }
198
- catch (error) {
199
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
200
- throw new Error(`Bitbucket Pipelines: Failed to get commit author from git log. ` +
201
- `Error: ${errorMessage}`);
202
- }
203
- }
package/dist/git/diff.js CHANGED
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getCommitMessage = getCommitMessage;
7
7
  exports.getCommitAuthor = getCommitAuthor;
8
+ exports.getPRDiff = getPRDiff;
8
9
  exports.getCommitDiff = getCommitDiff;
9
10
  const simple_git_1 = __importDefault(require("simple-git"));
10
11
  const child_process_1 = require("child_process");
@@ -29,48 +30,106 @@ async function getCommitMessage(repoRoot, sha) {
29
30
  *
30
31
  * Uses raw git log command to extract author information.
31
32
  * Works in all environments where git is available.
33
+ *
34
+ * Throws on error - git commits always have authors, so failure indicates
35
+ * an invalid SHA or repository issue that should surface immediately.
36
+ *
37
+ * Used by: GitHub, GitLab, Bitbucket, Vercel, Local (all CI environments)
32
38
  */
33
39
  async function getCommitAuthor(repoRoot, sha) {
40
+ const commitRef = sha || 'HEAD';
41
+ let output;
34
42
  try {
35
43
  // Use raw git command (same as test scripts) - more reliable than simple-git API
36
- const commitRef = sha || 'HEAD';
37
44
  const command = `git log -1 --format="%an <%ae>" ${commitRef}`;
38
- const output = (0, child_process_1.execSync)(command, {
45
+ output = (0, child_process_1.execSync)(command, {
39
46
  encoding: 'utf-8',
40
47
  cwd: repoRoot
41
48
  }).trim();
42
- // Parse output: "Name <email>"
43
- const match = output.match(/^(.+?)\s*<(.+?)>$/);
44
- if (!match) {
45
- return null;
46
- }
47
- const name = match[1].trim();
48
- const email = match[2].trim();
49
- if (!name || !email) {
50
- return null;
51
- }
52
- return { name, email };
53
49
  }
54
- catch {
55
- return null;
50
+ catch (error) {
51
+ const message = error instanceof Error ? error.message : String(error);
52
+ throw new Error(`Failed to get commit author for ${commitRef}: ${message}`);
53
+ }
54
+ // Parse output: "Name <email>"
55
+ const match = output.match(/^(.+?)\s*<(.+?)>$/);
56
+ if (!match) {
57
+ throw new Error(`Failed to parse commit author for ${commitRef}. ` +
58
+ `Expected format "Name <email>", got: "${output}"`);
59
+ }
60
+ const name = match[1].trim();
61
+ const email = match[2].trim();
62
+ if (!name || !email) {
63
+ throw new Error(`Commit author for ${commitRef} has empty name or email. ` +
64
+ `Got name="${name}", email="${email}"`);
56
65
  }
66
+ return { name, email };
57
67
  }
58
68
  /**
59
- * Get diff for a specific commit
69
+ * Get diff for a PR/MR context in CI environments.
70
+ *
71
+ * This is a shared implementation for CI environments that do shallow clones.
72
+ * It fetches the target branch on-demand and compares it against HEAD.
73
+ *
74
+ * Strategy:
75
+ * 1. Fetch target branch: origin/${targetBranch}:refs/remotes/origin/${targetBranch}
76
+ * 2. Diff: origin/${targetBranch}..HEAD (two dots = direct comparison)
77
+ *
78
+ * Why HEAD instead of origin/${sourceBranch}?
79
+ * - CI shallow clones only have HEAD available by default
80
+ * - origin/${sourceBranch} doesn't exist until explicitly fetched
81
+ * - HEAD IS the source branch in PR/MR pipelines
82
+ *
83
+ * Used by: GitHub, GitLab, Bitbucket (all PR/MR contexts)
84
+ * Future: Azure DevOps will use this when added
85
+ *
86
+ * @param repoRoot - Path to the repository root
87
+ * @param targetBranch - The branch being merged INTO (e.g., "main", "develop")
88
+ * @param logger - Optional logger for debug output
60
89
  */
61
- async function getCommitDiff(repoRoot, sha) {
90
+ async function getPRDiff(repoRoot, targetBranch, logger) {
62
91
  const git = (0, simple_git_1.default)(repoRoot);
63
- // Check if we're in a git repo
64
- const isRepo = await git.checkIsRepo();
65
- if (!isRepo) {
66
- throw new Error('Not a git repository. Threadline requires a git repository.');
92
+ // Fetch target branch on-demand (works with shallow clones)
93
+ logger?.debug(`Fetching target branch: origin/${targetBranch}`);
94
+ try {
95
+ await git.fetch(['origin', `${targetBranch}:refs/remotes/origin/${targetBranch}`, '--depth=1']);
67
96
  }
68
- // Get diff for the commit
69
- // Use git show to get the commit diff
97
+ catch (fetchError) {
98
+ throw new Error(`Failed to fetch target branch origin/${targetBranch}. ` +
99
+ `This is required for PR/MR diff comparison. ` +
100
+ `Error: ${fetchError instanceof Error ? fetchError.message : 'Unknown error'}`);
101
+ }
102
+ // Use two dots (..) for direct comparison (same as GitHub)
103
+ // Two dots: shows all changes in HEAD that aren't in origin/${targetBranch}
104
+ // Three dots: requires finding merge base which can fail with shallow clones
105
+ logger?.debug(`Comparing origin/${targetBranch}..HEAD`);
106
+ const diff = await git.diff([`origin/${targetBranch}..HEAD`, '-U200']);
107
+ const diffSummary = await git.diffSummary([`origin/${targetBranch}..HEAD`]);
108
+ const changedFiles = diffSummary.files.map(f => f.file);
109
+ return {
110
+ diff: diff || '',
111
+ changedFiles
112
+ };
113
+ }
114
+ /**
115
+ * Get diff for a specific commit (or HEAD if no SHA provided).
116
+ *
117
+ * Uses `git show` which works reliably with shallow clones (depth=1).
118
+ * This is safer than `HEAD~1...HEAD` which requires depth >= 2.
119
+ *
120
+ * Used by:
121
+ * - All CI environments for push/commit context (GitHub, GitLab, Bitbucket, Vercel)
122
+ * - Local environment for --commit flag
123
+ *
124
+ * @param repoRoot - Path to the repository root
125
+ * @param sha - Commit SHA to get diff for (defaults to HEAD)
126
+ */
127
+ async function getCommitDiff(repoRoot, sha = 'HEAD') {
128
+ const git = (0, simple_git_1.default)(repoRoot);
129
+ // Get diff using git show
70
130
  let diff;
71
131
  let changedFiles;
72
132
  try {
73
- // Get diff using git show
74
133
  diff = await git.show([sha, '--format=', '--no-color', '-U200']);
75
134
  // Get changed files using git show --name-only
76
135
  const commitFiles = await git.show([sha, '--name-only', '--format=', '--pretty=format:']);
@@ -80,18 +139,8 @@ async function getCommitDiff(repoRoot, sha) {
80
139
  .map(line => line.trim());
81
140
  }
82
141
  catch (error) {
83
- // Fallback: try git diff format
84
- try {
85
- diff = await git.diff([`${sha}^..${sha}`, '-U200']);
86
- // Get files from diff summary
87
- const diffSummary = await git.diffSummary([`${sha}^..${sha}`]);
88
- changedFiles = diffSummary.files.map(f => f.file);
89
- }
90
- catch (diffError) {
91
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
92
- const diffErrorMessage = diffError instanceof Error ? diffError.message : 'Unknown error';
93
- throw new Error(`Commit ${sha} not found or invalid: ${errorMessage || diffErrorMessage}`);
94
- }
142
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
143
+ throw new Error(`Failed to get diff for commit ${sha}: ${errorMessage}`);
95
144
  }
96
145
  return {
97
146
  diff: diff || '',
@@ -43,6 +43,7 @@ async function getGitHubContext(repoRoot) {
43
43
  'This should be automatically provided by GitHub Actions.');
44
44
  }
45
45
  // Get commit author using git commands (same approach as Bitbucket/Local)
46
+ // getCommitAuthor throws on failure with descriptive error
46
47
  const commitAuthor = await (0, diff_1.getCommitAuthor)(repoRoot, commitSha);
47
48
  // Get commit message if we have a SHA
48
49
  let commitMessage;
@@ -54,11 +55,6 @@ async function getGitHubContext(repoRoot) {
54
55
  }
55
56
  // Get PR title if in PR context
56
57
  const prTitle = getPRTitle(context);
57
- // Validate commit author was found
58
- if (!commitAuthor) {
59
- throw new Error(`GitHub Actions: Failed to get commit author from git log for commit ${commitSha || 'HEAD'}. ` +
60
- 'This should be automatically available in the git repository.');
61
- }
62
58
  return {
63
59
  diff,
64
60
  repoName,
@@ -75,53 +71,26 @@ async function getGitHubContext(repoRoot) {
75
71
  * Gets diff for GitHub Actions CI environment
76
72
  *
77
73
  * Strategy:
78
- * - PR context: Fetch base branch on-demand, compare base vs HEAD (full PR diff)
79
- * - Any push (main or feature branch): Compare last commit only (HEAD~1...HEAD)
74
+ * - PR context: Uses shared getPRDiff() - fetches base branch, compares against HEAD
75
+ * - Any push (main or feature branch): Compare last commit only using git show HEAD
80
76
  *
81
77
  * Note: GitHub Actions does shallow clones by default (fetch-depth: 1), so we fetch
82
78
  * the base branch on-demand. HEAD points to the merge commit which contains all PR changes.
83
79
  */
84
80
  async function getDiff(repoRoot) {
85
- const git = (0, simple_git_1.default)(repoRoot);
86
81
  const eventName = process.env.GITHUB_EVENT_NAME;
87
82
  const baseRef = process.env.GITHUB_BASE_REF;
88
- // PR Context: Only pull_request events have GITHUB_BASE_REF set
83
+ // PR Context: Use shared getPRDiff() implementation
89
84
  if (eventName === 'pull_request') {
90
85
  if (!baseRef) {
91
86
  throw new Error('GitHub PR context detected but GITHUB_BASE_REF is missing. ' +
92
87
  'This should be automatically provided by GitHub Actions.');
93
88
  }
94
- // Fetch base branch on-demand (works with shallow clones)
95
- logger_1.logger.debug(`Fetching base branch: origin/${baseRef}`);
96
- try {
97
- await git.fetch(['origin', `${baseRef}:refs/remotes/origin/${baseRef}`, '--depth=1']);
98
- }
99
- catch (fetchError) {
100
- throw new Error(`Failed to fetch base branch origin/${baseRef}. ` +
101
- `This is required for PR diff comparison. Error: ${fetchError instanceof Error ? fetchError.message : 'Unknown error'}`);
102
- }
103
- logger_1.logger.debug(`PR context, using origin/${baseRef}..HEAD (two dots for direct comparison)`);
104
- // Use two dots (..) instead of three dots (...) for direct comparison
105
- // Three dots requires finding merge base which can fail with shallow clones
106
- // Two dots shows all changes in HEAD that aren't in origin/${baseRef}
107
- const diff = await git.diff([`origin/${baseRef}..HEAD`, '-U200']);
108
- const diffSummary = await git.diffSummary([`origin/${baseRef}..HEAD`]);
109
- const changedFiles = diffSummary.files.map(f => f.file);
110
- return {
111
- diff: diff || '',
112
- changedFiles
113
- };
89
+ return (0, diff_1.getPRDiff)(repoRoot, baseRef, logger_1.logger);
114
90
  }
115
91
  // Any push (main or feature branch): Review last commit only
116
- // Use git show HEAD - works with shallow clones and shows the commit diff
117
- const diff = await git.show(['HEAD', '--format=', '--no-color', '-U200']);
118
- // Extract changed files from git show
119
- const showNameOnly = await git.show(['HEAD', '--name-only', '--format=', '--pretty=format:']);
120
- const changedFiles = showNameOnly
121
- .split('\n')
122
- .filter(line => line.trim().length > 0)
123
- .map(line => line.trim());
124
- return { diff: diff || '', changedFiles };
92
+ // Use shared getCommitDiff (defaults to HEAD)
93
+ return (0, diff_1.getCommitDiff)(repoRoot);
125
94
  }
126
95
  /**
127
96
  * Gets repository name for GitHub Actions
@@ -37,8 +37,9 @@ async function getGitLabContext(repoRoot) {
37
37
  const context = detectContext();
38
38
  const reviewContext = detectReviewContext();
39
39
  const commitSha = getCommitSha(context);
40
- // Get commit author (fails loudly if unavailable)
41
- const commitAuthor = await getCommitAuthor();
40
+ // Get commit author using shared function (git log)
41
+ // getCommitAuthor throws on failure with descriptive error
42
+ const commitAuthor = await (0, diff_1.getCommitAuthor)(repoRoot);
42
43
  // Get commit message if we have a SHA
43
44
  let commitMessage;
44
45
  if (commitSha) {
@@ -65,36 +66,26 @@ async function getGitLabContext(repoRoot) {
65
66
  * Get diff for GitLab CI environment
66
67
  *
67
68
  * Strategy:
68
- * - MR context: Fetch target branch, compare source vs target (full MR diff)
69
+ * - MR context: Uses shared getPRDiff() - fetches target branch, compares against HEAD
69
70
  * - Any push (main or feature branch): Compare last commit only (HEAD~1...HEAD)
70
71
  *
71
72
  * Note: GitLab CI does a shallow clone, so we fetch the target branch for MR context.
72
73
  * For regular pushes, HEAD~1...HEAD works without additional fetching.
73
74
  */
74
75
  async function getDiff(repoRoot) {
75
- const git = (0, simple_git_1.default)(repoRoot);
76
76
  const mrIid = process.env.CI_MERGE_REQUEST_IID;
77
77
  const targetBranch = process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME;
78
- const sourceBranch = process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME;
79
- // MR Context: Fetch target branch and compare
78
+ // MR Context: Use shared getPRDiff() implementation
80
79
  if (mrIid) {
81
- if (!targetBranch || !sourceBranch) {
82
- throw new Error('GitLab MR context detected but CI_MERGE_REQUEST_TARGET_BRANCH_NAME or ' +
83
- 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME is missing. ' +
80
+ if (!targetBranch) {
81
+ throw new Error('GitLab MR context detected but CI_MERGE_REQUEST_TARGET_BRANCH_NAME is missing. ' +
84
82
  'This should be automatically provided by GitLab CI.');
85
83
  }
86
- logger_1.logger.debug(`Fetching target branch: origin/${targetBranch}`);
87
- await git.fetch(['origin', `${targetBranch}:refs/remotes/origin/${targetBranch}`, '--depth=1']);
88
- const diff = await git.diff([`origin/${targetBranch}...origin/${sourceBranch}`, '-U200']);
89
- const diffSummary = await git.diffSummary([`origin/${targetBranch}...origin/${sourceBranch}`]);
90
- const changedFiles = diffSummary.files.map(f => f.file);
91
- return { diff: diff || '', changedFiles };
84
+ return (0, diff_1.getPRDiff)(repoRoot, targetBranch, logger_1.logger);
92
85
  }
93
86
  // Any push (main or feature branch): Review last commit only
94
- const diff = await git.diff(['HEAD~1...HEAD', '-U200']);
95
- const diffSummary = await git.diffSummary(['HEAD~1...HEAD']);
96
- const changedFiles = diffSummary.files.map(f => f.file);
97
- return { diff: diff || '', changedFiles };
87
+ // Use shared getCommitDiff (defaults to HEAD)
88
+ return (0, diff_1.getCommitDiff)(repoRoot);
98
89
  }
99
90
  /**
100
91
  * Gets repository name for GitLab CI
@@ -173,28 +164,6 @@ function getCommitSha(context) {
173
164
  }
174
165
  return undefined;
175
166
  }
176
- /**
177
- * Gets commit author for GitLab CI
178
- * Uses CI_COMMIT_AUTHOR environment variable (most reliable)
179
- */
180
- async function getCommitAuthor() {
181
- const commitAuthor = process.env.CI_COMMIT_AUTHOR;
182
- if (!commitAuthor) {
183
- throw new Error('GitLab CI: CI_COMMIT_AUTHOR environment variable is not set. ' +
184
- 'This should be automatically provided by GitLab CI.');
185
- }
186
- // Parse "name <email>" format
187
- const match = commitAuthor.match(/^(.+?)\s*<(.+?)>$/);
188
- if (!match) {
189
- throw new Error(`GitLab CI: CI_COMMIT_AUTHOR format is invalid. ` +
190
- `Expected format: "name <email>", got: "${commitAuthor}". ` +
191
- `This should be automatically provided by GitLab CI in the correct format.`);
192
- }
193
- return {
194
- name: match[1].trim(),
195
- email: match[2].trim()
196
- };
197
- }
198
167
  /**
199
168
  * Gets MR title for GitLab CI
200
169
  */
package/dist/git/local.js CHANGED
@@ -29,7 +29,7 @@ async function getLocalContext(repoRoot, commitSha) {
29
29
  throw new Error('Not a git repository. Threadline requires a git repository.');
30
30
  }
31
31
  // Get all Local context
32
- const diff = commitSha ? await getCommitDiff(repoRoot, commitSha) : await getDiff(repoRoot);
32
+ const diff = commitSha ? await (0, diff_1.getCommitDiff)(repoRoot, commitSha) : await getDiff(repoRoot);
33
33
  const repoName = await getRepoName(repoRoot);
34
34
  const branchName = await getBranchName(repoRoot);
35
35
  const context = commitSha ? { type: 'commit', commitSha } : { type: 'local' };
@@ -96,24 +96,6 @@ async function getDiff(repoRoot) {
96
96
  changedFiles
97
97
  };
98
98
  }
99
- /**
100
- * Get diff for a specific commit (when --commit flag is used)
101
- */
102
- async function getCommitDiff(repoRoot, commitSha) {
103
- const git = (0, simple_git_1.default)(repoRoot);
104
- // Get diff using git show
105
- const diff = await git.show([commitSha, '--format=', '--no-color', '-U200']);
106
- // Get changed files using git show --name-only
107
- const commitFiles = await git.show([commitSha, '--name-only', '--format=', '--pretty=format:']);
108
- const changedFiles = commitFiles
109
- .split('\n')
110
- .filter(line => line.trim().length > 0)
111
- .map(line => line.trim());
112
- return {
113
- diff: diff || '',
114
- changedFiles
115
- };
116
- }
117
99
  /**
118
100
  * Gets repository name for local environment
119
101
  */
@@ -177,15 +159,8 @@ async function getCommitAuthorFromConfig(repoRoot) {
177
159
  }
178
160
  /**
179
161
  * Gets commit author from git log (for specific commits)
162
+ * getCommitAuthor throws on failure with descriptive error
180
163
  */
181
164
  async function getCommitAuthorFromGit(repoRoot, commitSha) {
182
- const gitAuthor = await (0, diff_1.getCommitAuthor)(repoRoot, commitSha);
183
- if (!gitAuthor || !gitAuthor.email) {
184
- throw new Error(`Local: Failed to get commit author from git log for commit ${commitSha}. ` +
185
- 'This should be available in your local git repository.');
186
- }
187
- return {
188
- name: gitAuthor.name,
189
- email: gitAuthor.email
190
- };
165
+ return (0, diff_1.getCommitAuthor)(repoRoot, commitSha);
191
166
  }
@@ -17,7 +17,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.getVercelContext = getVercelContext;
19
19
  const simple_git_1 = __importDefault(require("simple-git"));
20
- const child_process_1 = require("child_process");
21
20
  const diff_1 = require("./diff");
22
21
  /**
23
22
  * Gets all Vercel context
@@ -36,8 +35,9 @@ async function getVercelContext(repoRoot) {
36
35
  const commitSha = getCommitSha();
37
36
  const context = { type: 'commit', commitSha };
38
37
  const reviewContext = 'commit';
39
- // Get commit author (fails loudly if unavailable)
40
- const commitAuthor = await getCommitAuthorForVercel(repoRoot, commitSha);
38
+ // Get commit author using shared function (git log)
39
+ // getCommitAuthor throws on failure with descriptive error
40
+ const commitAuthor = await (0, diff_1.getCommitAuthor)(repoRoot, commitSha);
41
41
  // Get commit message
42
42
  let commitMessage;
43
43
  const message = await (0, diff_1.getCommitMessage)(repoRoot, commitSha);
@@ -58,29 +58,13 @@ async function getVercelContext(repoRoot) {
58
58
  /**
59
59
  * Get diff for Vercel CI environment
60
60
  *
61
- * Vercel provides VERCEL_GIT_COMMIT_SHA which contains the commit being deployed.
62
- * This function gets the diff for that specific commit using git show.
61
+ * Vercel only supports commit context (no PRs).
62
+ * Uses shared getCommitDiff with HEAD (which equals VERCEL_GIT_COMMIT_SHA).
63
63
  */
64
64
  async function getDiff(repoRoot) {
65
- const git = (0, simple_git_1.default)(repoRoot);
66
- // Get commit SHA from Vercel environment variable
67
- const commitSha = process.env.VERCEL_GIT_COMMIT_SHA;
68
- if (!commitSha) {
69
- throw new Error('VERCEL_GIT_COMMIT_SHA environment variable is not set. ' +
70
- 'This should be automatically provided by Vercel CI.');
71
- }
72
- // Get diff using git show - this is the ONLY way we get diff in Vercel
73
- const diff = await git.show([commitSha, '--format=', '--no-color', '-U200']);
74
- // Get changed files using git show --name-only
75
- const commitFiles = await git.show([commitSha, '--name-only', '--format=', '--pretty=format:']);
76
- const changedFiles = commitFiles
77
- .split('\n')
78
- .filter(line => line.trim().length > 0)
79
- .map(line => line.trim());
80
- return {
81
- diff: diff || '', // Empty diff is legitimate (e.g., metadata-only commits, merge commits)
82
- changedFiles
83
- };
65
+ // Use shared getCommitDiff (defaults to HEAD)
66
+ // In Vercel, HEAD is the commit being deployed
67
+ return (0, diff_1.getCommitDiff)(repoRoot);
84
68
  }
85
69
  /**
86
70
  * Gets repository name for Vercel
@@ -116,33 +100,3 @@ function getCommitSha() {
116
100
  }
117
101
  return commitSha;
118
102
  }
119
- /**
120
- * Gets commit author for Vercel
121
- * Uses VERCEL_GIT_COMMIT_AUTHOR_NAME for name, raw git log command for email
122
- *
123
- * Uses raw `git log` command (same as test script) instead of simple-git library
124
- * because simple-git's log method may not work correctly in Vercel's shallow clone.
125
- */
126
- async function getCommitAuthorForVercel(repoRoot, commitSha) {
127
- const authorName = process.env.VERCEL_GIT_COMMIT_AUTHOR_NAME;
128
- if (!authorName) {
129
- throw new Error('Vercel: VERCEL_GIT_COMMIT_AUTHOR_NAME environment variable is not set. ' +
130
- 'This should be automatically provided by Vercel.');
131
- }
132
- // Use raw git log command (same approach as test script) - more reliable than simple-git
133
- try {
134
- const email = (0, child_process_1.execSync)(`git log ${commitSha} -1 --format=%ae`, { encoding: 'utf-8', cwd: repoRoot }).trim();
135
- if (!email) {
136
- throw new Error('Email is empty');
137
- }
138
- return {
139
- name: authorName.trim(),
140
- email: email.trim()
141
- };
142
- }
143
- catch (error) {
144
- throw new Error(`Vercel: Failed to get commit author email from git log for commit ${commitSha}. ` +
145
- `This should be available in Vercel's build environment. ` +
146
- `Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
147
- }
148
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadlines",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "Threadlines CLI - AI-powered linter based on your natural language documentation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {