Skip to content

Commit eda1ca1

Browse files
committed
feat(code): add commit trailers for attribution
1 parent bba3405 commit eda1ca1

File tree

6 files changed

+38
-11
lines changed

6 files changed

+38
-11
lines changed

apps/code/src/main/services/git/create-pr-saga.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface CreatePrSagaInput {
1818
prBody?: string;
1919
draft?: boolean;
2020
stagedOnly?: boolean;
21+
taskId?: string;
2122
}
2223

2324
export interface CreatePrSagaOutput {
@@ -36,7 +37,7 @@ export interface CreatePrDeps {
3637
commit(
3738
dir: string,
3839
message: string,
39-
stagedOnly?: boolean,
40+
options?: { stagedOnly?: boolean; taskId?: string },
4041
): Promise<CommitOutput>;
4142
getSyncStatus(dir: string): Promise<GitSyncStatus>;
4243
push(dir: string): Promise<PushOutput>;
@@ -125,11 +126,10 @@ export class CreatePrSaga extends Saga<CreatePrSagaInput, CreatePrSagaOutput> {
125126
await this.step({
126127
name: "committing",
127128
execute: async () => {
128-
const result = await this.deps.commit(
129-
directoryPath,
130-
commitMessage!,
131-
input.stagedOnly,
132-
);
129+
const result = await this.deps.commit(directoryPath, commitMessage!, {
130+
stagedOnly: input.stagedOnly,
131+
taskId: input.taskId,
132+
});
133133
if (!result.success) throw new Error(result.message);
134134
return result;
135135
},

apps/code/src/main/services/git/schemas.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ export const commitInput = z.object({
206206
paths: z.array(z.string()).optional(),
207207
allowEmpty: z.boolean().optional(),
208208
stagedOnly: z.boolean().optional(),
209+
taskId: z.string().optional(),
209210
});
210211

211212
export type CommitInput = z.infer<typeof commitInput>;
@@ -250,6 +251,7 @@ export const createPrInput = z.object({
250251
prBody: z.string().optional(),
251252
draft: z.boolean().optional(),
252253
stagedOnly: z.boolean().optional(),
254+
taskId: z.string().optional(),
253255
});
254256

255257
export type CreatePrInput = z.infer<typeof createPrInput>;

apps/code/src/main/services/git/service.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
513513
prBody?: string;
514514
draft?: boolean;
515515
stagedOnly?: boolean;
516+
taskId?: string;
516517
}): Promise<CreatePrOutput> {
517518
const { directoryPath, flowId } = input;
518519

@@ -536,7 +537,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
536537
checkoutBranch: (dir, name) => this.checkoutBranch(dir, name),
537538
getChangedFilesHead: (dir) => this.getChangedFilesHead(dir),
538539
generateCommitMessage: (dir) => this.generateCommitMessage(dir),
539-
commit: (dir, msg, stagedOnly) => this.commit(dir, msg, { stagedOnly }),
540+
commit: (dir, msg, opts) => this.commit(dir, msg, opts),
540541
getSyncStatus: (dir) => this.getGitSyncStatus(dir),
541542
push: (dir) => this.push(dir),
542543
publish: (dir) => this.publish(dir),
@@ -556,6 +557,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
556557
prBody: input.prBody,
557558
draft: input.draft,
558559
stagedOnly: input.stagedOnly,
560+
taskId: input.taskId,
559561
});
560562

561563
if (!result.success) {
@@ -619,7 +621,12 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
619621
public async commit(
620622
directoryPath: string,
621623
message: string,
622-
options?: { paths?: string[]; allowEmpty?: boolean; stagedOnly?: boolean },
624+
options?: {
625+
paths?: string[];
626+
allowEmpty?: boolean;
627+
stagedOnly?: boolean;
628+
taskId?: string;
629+
},
623630
): Promise<CommitOutput> {
624631
const fail = (msg: string): CommitOutput => ({
625632
success: false,

apps/code/src/main/trpc/routers/git.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ export const gitRouter = router({
223223
paths: input.paths,
224224
allowEmpty: input.allowEmpty,
225225
stagedOnly: input.stagedOnly,
226+
taskId: input.taskId,
226227
}),
227228
),
228229

apps/code/src/renderer/features/git-interaction/hooks/useGitInteraction.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ export function useGitInteraction(
230230
prBody: store.prBody.trim() || undefined,
231231
draft: store.createPrDraft || undefined,
232232
stagedOnly: stagedOnly || undefined,
233+
taskId,
233234
});
234235

235236
if (!result.success) {
@@ -349,6 +350,7 @@ export function useGitInteraction(
349350
directoryPath: repoPath,
350351
message,
351352
stagedOnly: stagedOnly || undefined,
353+
taskId,
352354
});
353355

354356
if (!result.success) {

packages/git/src/sagas/commit.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import { GitSaga, type GitSagaInput } from "../git-saga";
22

3+
function buildPostHogTrailers(taskId?: string): string[] {
4+
const trailers = ["Generated-By: PostHog Code"];
5+
if (taskId) trailers.push(`Task-Id: ${taskId}`);
6+
return trailers;
7+
}
8+
39
export interface CommitInput extends GitSagaInput {
410
message: string;
511
paths?: string[];
612
allowEmpty?: boolean;
713
stagedOnly?: boolean;
14+
taskId?: string;
815
}
916

1017
export interface CommitOutput {
@@ -19,7 +26,7 @@ export class CommitSaga extends GitSaga<CommitInput, CommitOutput> {
1926
protected async executeGitOperations(
2027
input: CommitInput,
2128
): Promise<CommitOutput> {
22-
const { message, paths, allowEmpty, stagedOnly } = input;
29+
const { message, paths, allowEmpty, stagedOnly, taskId } = input;
2330

2431
const originalHead = await this.readOnlyStep("get-original-head", () =>
2532
this.git.revparse(["HEAD"]),
@@ -62,11 +69,19 @@ export class CommitSaga extends GitSaga<CommitInput, CommitOutput> {
6269
});
6370
}
6471

72+
const trailers = buildPostHogTrailers(taskId);
73+
74+
const commitOptions: Record<string, null | string[]> = {};
75+
if (allowEmpty) commitOptions["--allow-empty"] = null;
76+
if (trailers.length > 0) commitOptions["--trailer"] = trailers;
77+
78+
const hasOptions = Object.keys(commitOptions).length > 0;
79+
6580
const commitResult = await this.step({
6681
name: "commit",
6782
execute: () =>
68-
allowEmpty
69-
? this.git.commit(message, undefined, { "--allow-empty": null })
83+
hasOptions
84+
? this.git.commit(message, undefined, commitOptions)
7085
: this.git.commit(message),
7186
rollback: async () => {
7287
await this.git.reset(["--soft", originalHead]);

0 commit comments

Comments
 (0)