feat: link commits, branches and pull requests to tickets
A project can turn on a webhook for GitHub, Gitea or GitLab. Pushes and pull or merge requests that name a ticket key show on that ticket under Desarrollo, with the state of each pull request; the secret is shown and rotated from the project settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,9 @@ import {
|
||||
StartSprintCommand,
|
||||
} from './sprint.commands';
|
||||
import { IssueTree } from './issue-tree';
|
||||
import { GIT_HANDLERS, RecordGitMentionsCommand } from '../git/git';
|
||||
import { GitIntegrationsRepository } from '../ports/git-integrations.repository';
|
||||
import { DevLinkKind } from '@task-stream/api/tracker/domain';
|
||||
import { SetIssueParentCommand } from './set-issue-parent.command';
|
||||
import { RankIssueCommand } from './rank-issue.command';
|
||||
import {
|
||||
@@ -67,9 +70,12 @@ import { DeleteCommentCommand } from './delete-comment.command';
|
||||
import { PublishReleaseCommand } from './publish-release.command';
|
||||
import { SetIssueReleaseCommand } from './set-issue-release.command';
|
||||
import { WriteAccess } from './write-access';
|
||||
import { ReadAccess } from '../queries/read-access';
|
||||
import { FindProjectsRepository } from '../ports/find-projects.repository';
|
||||
|
||||
async function setup() {
|
||||
const store = new InMemoryEventStore();
|
||||
const keys = new Map<string, { id: string; projectId: string }>();
|
||||
const sprints = {
|
||||
active: null as { id: string; name: string } | null,
|
||||
findActive: async () => sprints.active,
|
||||
@@ -85,9 +91,16 @@ async function setup() {
|
||||
imports: [CqrsModule.forRoot()],
|
||||
providers: [
|
||||
...COMMAND_HANDLERS,
|
||||
...GIT_HANDLERS,
|
||||
...TRACKER_FACTORIES,
|
||||
WriteAccess,
|
||||
IssueTree,
|
||||
ReadAccess,
|
||||
{
|
||||
provide: FindProjectsRepository,
|
||||
useValue: { findRole: async () => null, findById: async () => null },
|
||||
},
|
||||
{ provide: GitIntegrationsRepository, useValue: {} },
|
||||
AggregateRehydrator,
|
||||
{ provide: EventStore, useValue: store },
|
||||
{ provide: AttachmentStorage, useValue: files },
|
||||
@@ -108,6 +121,8 @@ async function setup() {
|
||||
{
|
||||
provide: FindIssuesRepository,
|
||||
useValue: {
|
||||
// Tickets by key, as the read model would answer (filled by tests).
|
||||
findByKey: async (key: string) => keys.get(key) ?? null,
|
||||
sprintProgress: async () => ({
|
||||
total: 0,
|
||||
finished: 0,
|
||||
@@ -129,6 +144,7 @@ async function setup() {
|
||||
store,
|
||||
files,
|
||||
sprints,
|
||||
keys,
|
||||
run,
|
||||
rehydrator: moduleRef.get(AggregateRehydrator),
|
||||
};
|
||||
@@ -476,6 +492,34 @@ describe('tracker commands (event sourced)', () => {
|
||||
).rejects.toThrow(ForbiddenError);
|
||||
});
|
||||
|
||||
it('links commits and pull requests to the tickets they name', async () => {
|
||||
const { run, rehydrator, keys } = await setup();
|
||||
const projectId = await projectWithTeam(run);
|
||||
const issue = await run(
|
||||
new CreateIssueCommand('dev', projectId, IssueType.TASK, 'Pay', {}),
|
||||
);
|
||||
keys.set(issue.key ?? '', { id: issue.id, projectId });
|
||||
const commit = {
|
||||
text: `${issue.key}: card form (refs TS-999, OTHER-1)`,
|
||||
link: {
|
||||
kind: DevLinkKind.COMMIT,
|
||||
url: 'https://git.example.org/acme/web/commit/abc',
|
||||
title: 'card form',
|
||||
ref: 'abc',
|
||||
state: null,
|
||||
provider: 'gitea',
|
||||
author: 'Ana',
|
||||
at: '2026-10-08T10:00:00Z',
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
await run(new RecordGitMentionsCommand(projectId, [commit, commit])),
|
||||
).toBe(1);
|
||||
const { devLinks } = await rehydrator.rehydrate(issue.id, Issue);
|
||||
expect(devLinks.map((l) => l.ref)).toEqual(['abc']);
|
||||
});
|
||||
|
||||
it('does not let viewers modify tickets', async () => {
|
||||
const { run } = await setup();
|
||||
const projectId = await projectWithTeam(run);
|
||||
|
||||
Reference in New Issue
Block a user