@cat-factory/integrations 0.6.0
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/LICENSE +21 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/datadog/DatadogClient.d.ts +54 -0
- package/dist/modules/datadog/DatadogClient.d.ts.map +1 -0
- package/dist/modules/datadog/DatadogClient.js +132 -0
- package/dist/modules/datadog/DatadogClient.js.map +1 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.d.ts +30 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.d.ts.map +1 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.js +101 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.js.map +1 -0
- package/dist/modules/datadog/datadog.logic.d.ts +37 -0
- package/dist/modules/datadog/datadog.logic.d.ts.map +1 -0
- package/dist/modules/datadog/datadog.logic.js +90 -0
- package/dist/modules/datadog/datadog.logic.js.map +1 -0
- package/dist/modules/documents/ConfluenceProvider.d.ts +29 -0
- package/dist/modules/documents/ConfluenceProvider.d.ts.map +1 -0
- package/dist/modules/documents/ConfluenceProvider.js +180 -0
- package/dist/modules/documents/ConfluenceProvider.js.map +1 -0
- package/dist/modules/documents/DocumentConnectionService.d.ts +30 -0
- package/dist/modules/documents/DocumentConnectionService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentConnectionService.js +69 -0
- package/dist/modules/documents/DocumentConnectionService.js.map +1 -0
- package/dist/modules/documents/DocumentImportService.d.ts +34 -0
- package/dist/modules/documents/DocumentImportService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentImportService.js +83 -0
- package/dist/modules/documents/DocumentImportService.js.map +1 -0
- package/dist/modules/documents/DocumentLinkService.d.ts +31 -0
- package/dist/modules/documents/DocumentLinkService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentLinkService.js +75 -0
- package/dist/modules/documents/DocumentLinkService.js.map +1 -0
- package/dist/modules/documents/DocumentPlannerService.d.ts +23 -0
- package/dist/modules/documents/DocumentPlannerService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentPlannerService.js +96 -0
- package/dist/modules/documents/DocumentPlannerService.js.map +1 -0
- package/dist/modules/documents/GitHubDocsProvider.d.ts +42 -0
- package/dist/modules/documents/GitHubDocsProvider.d.ts.map +1 -0
- package/dist/modules/documents/GitHubDocsProvider.js +86 -0
- package/dist/modules/documents/GitHubDocsProvider.js.map +1 -0
- package/dist/modules/documents/NotionProvider.d.ts +32 -0
- package/dist/modules/documents/NotionProvider.d.ts.map +1 -0
- package/dist/modules/documents/NotionProvider.js +221 -0
- package/dist/modules/documents/NotionProvider.js.map +1 -0
- package/dist/modules/documents/confluence.logic.d.ts +37 -0
- package/dist/modules/documents/confluence.logic.d.ts.map +1 -0
- package/dist/modules/documents/confluence.logic.js +133 -0
- package/dist/modules/documents/confluence.logic.js.map +1 -0
- package/dist/modules/documents/documents.logic.d.ts +22 -0
- package/dist/modules/documents/documents.logic.d.ts.map +1 -0
- package/dist/modules/documents/documents.logic.js +138 -0
- package/dist/modules/documents/documents.logic.js.map +1 -0
- package/dist/modules/documents/github-docs.logic.d.ts +52 -0
- package/dist/modules/documents/github-docs.logic.d.ts.map +1 -0
- package/dist/modules/documents/github-docs.logic.js +94 -0
- package/dist/modules/documents/github-docs.logic.js.map +1 -0
- package/dist/modules/documents/notion.logic.d.ts +31 -0
- package/dist/modules/documents/notion.logic.d.ts.map +1 -0
- package/dist/modules/documents/notion.logic.js +142 -0
- package/dist/modules/documents/notion.logic.js.map +1 -0
- package/dist/modules/email/EmailConnectionService.d.ts +34 -0
- package/dist/modules/email/EmailConnectionService.d.ts.map +1 -0
- package/dist/modules/email/EmailConnectionService.js +82 -0
- package/dist/modules/email/EmailConnectionService.js.map +1 -0
- package/dist/modules/email/adapters.d.ts +39 -0
- package/dist/modules/email/adapters.d.ts.map +1 -0
- package/dist/modules/email/adapters.js +79 -0
- package/dist/modules/email/adapters.js.map +1 -0
- package/dist/modules/environments/EnvironmentConnectionService.d.ts +42 -0
- package/dist/modules/environments/EnvironmentConnectionService.d.ts.map +1 -0
- package/dist/modules/environments/EnvironmentConnectionService.js +120 -0
- package/dist/modules/environments/EnvironmentConnectionService.js.map +1 -0
- package/dist/modules/environments/EnvironmentProvisioningService.d.ts +56 -0
- package/dist/modules/environments/EnvironmentProvisioningService.d.ts.map +1 -0
- package/dist/modules/environments/EnvironmentProvisioningService.js +153 -0
- package/dist/modules/environments/EnvironmentProvisioningService.js.map +1 -0
- package/dist/modules/environments/EnvironmentTeardownService.d.ts +24 -0
- package/dist/modules/environments/EnvironmentTeardownService.d.ts.map +1 -0
- package/dist/modules/environments/EnvironmentTeardownService.js +54 -0
- package/dist/modules/environments/EnvironmentTeardownService.js.map +1 -0
- package/dist/modules/environments/HttpEnvironmentProvider.d.ts +30 -0
- package/dist/modules/environments/HttpEnvironmentProvider.d.ts.map +1 -0
- package/dist/modules/environments/HttpEnvironmentProvider.js +316 -0
- package/dist/modules/environments/HttpEnvironmentProvider.js.map +1 -0
- package/dist/modules/environments/environments.logic.d.ts +50 -0
- package/dist/modules/environments/environments.logic.d.ts.map +1 -0
- package/dist/modules/environments/environments.logic.js +257 -0
- package/dist/modules/environments/environments.logic.js.map +1 -0
- package/dist/modules/github/GitHubInstallationService.d.ts +66 -0
- package/dist/modules/github/GitHubInstallationService.d.ts.map +1 -0
- package/dist/modules/github/GitHubInstallationService.js +143 -0
- package/dist/modules/github/GitHubInstallationService.js.map +1 -0
- package/dist/modules/github/GitHubService.d.ts +29 -0
- package/dist/modules/github/GitHubService.d.ts.map +1 -0
- package/dist/modules/github/GitHubService.js +61 -0
- package/dist/modules/github/GitHubService.js.map +1 -0
- package/dist/modules/github/GitHubSyncService.d.ts +97 -0
- package/dist/modules/github/GitHubSyncService.d.ts.map +1 -0
- package/dist/modules/github/GitHubSyncService.js +241 -0
- package/dist/modules/github/GitHubSyncService.js.map +1 -0
- package/dist/modules/github/RepoProvisioningService.d.ts +26 -0
- package/dist/modules/github/RepoProvisioningService.d.ts.map +1 -0
- package/dist/modules/github/RepoProvisioningService.js +36 -0
- package/dist/modules/github/RepoProvisioningService.js.map +1 -0
- package/dist/modules/github/WebhookService.d.ts +28 -0
- package/dist/modules/github/WebhookService.d.ts.map +1 -0
- package/dist/modules/github/WebhookService.js +156 -0
- package/dist/modules/github/WebhookService.js.map +1 -0
- package/dist/modules/github/projection.logic.d.ts +95 -0
- package/dist/modules/github/projection.logic.d.ts.map +1 -0
- package/dist/modules/github/projection.logic.js +94 -0
- package/dist/modules/github/projection.logic.js.map +1 -0
- package/dist/modules/github/provisioning.logic.d.ts +11 -0
- package/dist/modules/github/provisioning.logic.d.ts.map +1 -0
- package/dist/modules/github/provisioning.logic.js +18 -0
- package/dist/modules/github/provisioning.logic.js.map +1 -0
- package/dist/modules/incident/incident.logic.d.ts +16 -0
- package/dist/modules/incident/incident.logic.d.ts.map +1 -0
- package/dist/modules/incident/incident.logic.js +23 -0
- package/dist/modules/incident/incident.logic.js.map +1 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.d.ts +26 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.d.ts.map +1 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.js +84 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.js.map +1 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.d.ts +27 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.d.ts.map +1 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.js +65 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.js.map +1 -0
- package/dist/modules/providers/ApiKeyService.d.ts +73 -0
- package/dist/modules/providers/ApiKeyService.d.ts.map +1 -0
- package/dist/modules/providers/ApiKeyService.js +122 -0
- package/dist/modules/providers/ApiKeyService.js.map +1 -0
- package/dist/modules/providers/LocalModelEndpointService.d.ts +52 -0
- package/dist/modules/providers/LocalModelEndpointService.d.ts.map +1 -0
- package/dist/modules/providers/LocalModelEndpointService.js +131 -0
- package/dist/modules/providers/LocalModelEndpointService.js.map +1 -0
- package/dist/modules/providers/PersonalSubscriptionService.d.ts +94 -0
- package/dist/modules/providers/PersonalSubscriptionService.d.ts.map +1 -0
- package/dist/modules/providers/PersonalSubscriptionService.js +218 -0
- package/dist/modules/providers/PersonalSubscriptionService.js.map +1 -0
- package/dist/modules/providers/ProviderSubscriptionService.d.ts +75 -0
- package/dist/modules/providers/ProviderSubscriptionService.d.ts.map +1 -0
- package/dist/modules/providers/ProviderSubscriptionService.js +130 -0
- package/dist/modules/providers/ProviderSubscriptionService.js.map +1 -0
- package/dist/modules/providers/localModelUrl.d.ts +7 -0
- package/dist/modules/providers/localModelUrl.d.ts.map +1 -0
- package/dist/modules/providers/localModelUrl.js +67 -0
- package/dist/modules/providers/localModelUrl.js.map +1 -0
- package/dist/modules/providers/providers.logic.d.ts +23 -0
- package/dist/modules/providers/providers.logic.d.ts.map +1 -0
- package/dist/modules/providers/providers.logic.js +46 -0
- package/dist/modules/providers/providers.logic.js.map +1 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.d.ts +51 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.d.ts.map +1 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.js +304 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.js.map +1 -0
- package/dist/modules/runners/RunnerPoolConnectionService.d.ts +47 -0
- package/dist/modules/runners/RunnerPoolConnectionService.d.ts.map +1 -0
- package/dist/modules/runners/RunnerPoolConnectionService.js +98 -0
- package/dist/modules/runners/RunnerPoolConnectionService.js.map +1 -0
- package/dist/modules/runners/RunnerPoolTransport.d.ts +11 -0
- package/dist/modules/runners/RunnerPoolTransport.d.ts.map +1 -0
- package/dist/modules/runners/RunnerPoolTransport.js +61 -0
- package/dist/modules/runners/RunnerPoolTransport.js.map +1 -0
- package/dist/modules/runners/runners.logic.d.ts +16 -0
- package/dist/modules/runners/runners.logic.d.ts.map +1 -0
- package/dist/modules/runners/runners.logic.js +52 -0
- package/dist/modules/runners/runners.logic.js.map +1 -0
- package/dist/modules/slack/SlackApiClient.d.ts +67 -0
- package/dist/modules/slack/SlackApiClient.d.ts.map +1 -0
- package/dist/modules/slack/SlackApiClient.js +132 -0
- package/dist/modules/slack/SlackApiClient.js.map +1 -0
- package/dist/modules/slack/SlackConnectionService.d.ts +41 -0
- package/dist/modules/slack/SlackConnectionService.d.ts.map +1 -0
- package/dist/modules/slack/SlackConnectionService.js +136 -0
- package/dist/modules/slack/SlackConnectionService.js.map +1 -0
- package/dist/modules/slack/SlackMemberMappingService.d.ts +17 -0
- package/dist/modules/slack/SlackMemberMappingService.d.ts.map +1 -0
- package/dist/modules/slack/SlackMemberMappingService.js +28 -0
- package/dist/modules/slack/SlackMemberMappingService.js.map +1 -0
- package/dist/modules/slack/SlackNotificationChannel.d.ts +45 -0
- package/dist/modules/slack/SlackNotificationChannel.d.ts.map +1 -0
- package/dist/modules/slack/SlackNotificationChannel.js +84 -0
- package/dist/modules/slack/SlackNotificationChannel.js.map +1 -0
- package/dist/modules/slack/SlackSettingsService.d.ts +16 -0
- package/dist/modules/slack/SlackSettingsService.d.ts.map +1 -0
- package/dist/modules/slack/SlackSettingsService.js +41 -0
- package/dist/modules/slack/SlackSettingsService.js.map +1 -0
- package/dist/modules/slack/slack.logic.d.ts +55 -0
- package/dist/modules/slack/slack.logic.d.ts.map +1 -0
- package/dist/modules/slack/slack.logic.js +149 -0
- package/dist/modules/slack/slack.logic.js.map +1 -0
- package/dist/modules/tasks/GitHubIssuesProvider.d.ts +50 -0
- package/dist/modules/tasks/GitHubIssuesProvider.d.ts.map +1 -0
- package/dist/modules/tasks/GitHubIssuesProvider.js +92 -0
- package/dist/modules/tasks/GitHubIssuesProvider.js.map +1 -0
- package/dist/modules/tasks/JiraProvider.d.ts +29 -0
- package/dist/modules/tasks/JiraProvider.d.ts.map +1 -0
- package/dist/modules/tasks/JiraProvider.js +114 -0
- package/dist/modules/tasks/JiraProvider.js.map +1 -0
- package/dist/modules/tasks/TaskConnectionService.d.ts +30 -0
- package/dist/modules/tasks/TaskConnectionService.d.ts.map +1 -0
- package/dist/modules/tasks/TaskConnectionService.js +69 -0
- package/dist/modules/tasks/TaskConnectionService.js.map +1 -0
- package/dist/modules/tasks/TaskImportService.d.ts +34 -0
- package/dist/modules/tasks/TaskImportService.d.ts.map +1 -0
- package/dist/modules/tasks/TaskImportService.js +96 -0
- package/dist/modules/tasks/TaskImportService.js.map +1 -0
- package/dist/modules/tasks/TaskLinkService.d.ts +30 -0
- package/dist/modules/tasks/TaskLinkService.d.ts.map +1 -0
- package/dist/modules/tasks/TaskLinkService.js +56 -0
- package/dist/modules/tasks/TaskLinkService.js.map +1 -0
- package/dist/modules/tasks/github-issues.logic.d.ts +35 -0
- package/dist/modules/tasks/github-issues.logic.d.ts.map +1 -0
- package/dist/modules/tasks/github-issues.logic.js +67 -0
- package/dist/modules/tasks/github-issues.logic.js.map +1 -0
- package/dist/modules/tasks/jira.logic.d.ts +28 -0
- package/dist/modules/tasks/jira.logic.d.ts.map +1 -0
- package/dist/modules/tasks/jira.logic.js +151 -0
- package/dist/modules/tasks/jira.logic.js.map +1 -0
- package/dist/modules/tasks/tasks.logic.d.ts +12 -0
- package/dist/modules/tasks/tasks.logic.d.ts.map +1 -0
- package/dist/modules/tasks/tasks.logic.js +17 -0
- package/dist/modules/tasks/tasks.logic.js.map +1 -0
- package/dist/modules/tracker/TicketTrackerService.d.ts +45 -0
- package/dist/modules/tracker/TicketTrackerService.d.ts.map +1 -0
- package/dist/modules/tracker/TicketTrackerService.js +52 -0
- package/dist/modules/tracker/TicketTrackerService.js.map +1 -0
- package/dist/modules/tracker/base64.d.ts +2 -0
- package/dist/modules/tracker/base64.d.ts.map +1 -0
- package/dist/modules/tracker/base64.js +18 -0
- package/dist/modules/tracker/base64.js.map +1 -0
- package/dist/modules/tracker/github.create.logic.d.ts +16 -0
- package/dist/modules/tracker/github.create.logic.d.ts.map +1 -0
- package/dist/modules/tracker/github.create.logic.js +25 -0
- package/dist/modules/tracker/github.create.logic.js.map +1 -0
- package/dist/modules/tracker/jira.create.logic.d.ts +31 -0
- package/dist/modules/tracker/jira.create.logic.d.ts.map +1 -0
- package/dist/modules/tracker/jira.create.logic.js +59 -0
- package/dist/modules/tracker/jira.create.logic.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GitHubService.js","sourceRoot":"","sources":["../../../src/modules/github/GitHubService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Clock } from '@cat-factory/kernel';
|
|
2
|
+
import type { GitHubClient } from '@cat-factory/kernel';
|
|
3
|
+
import type { BranchProjectionRepository, CheckRunProjectionRepository, CommitProjectionRepository, GitHubInstallationRepository, IssueProjectionRepository, PullRequestProjectionRepository, RepoProjectionRepository } from '@cat-factory/kernel';
|
|
4
|
+
import type { GitHubAvailableRepo, GitHubRepo, RepoTreeEntry } from '@cat-factory/kernel';
|
|
5
|
+
export interface GitHubSyncServiceDependencies {
|
|
6
|
+
githubClient: GitHubClient;
|
|
7
|
+
githubInstallationRepository: GitHubInstallationRepository;
|
|
8
|
+
repoProjectionRepository: RepoProjectionRepository;
|
|
9
|
+
branchProjectionRepository: BranchProjectionRepository;
|
|
10
|
+
pullRequestProjectionRepository: PullRequestProjectionRepository;
|
|
11
|
+
issueProjectionRepository: IssueProjectionRepository;
|
|
12
|
+
commitProjectionRepository: CommitProjectionRepository;
|
|
13
|
+
checkRunProjectionRepository: CheckRunProjectionRepository;
|
|
14
|
+
clock: Clock;
|
|
15
|
+
/**
|
|
16
|
+
* Bounds the initial commit backfill: when a repo has no commit cursor yet
|
|
17
|
+
* (its first sync), commits are listed only from `now - commitBackfillHorizonMs`
|
|
18
|
+
* rather than from the dawn of the repo. This keeps a large/monorepo connect
|
|
19
|
+
* from inserting its entire history in one step. Subsequent syncs use the
|
|
20
|
+
* (more recent) cursor. Undefined means backfill the full history (legacy).
|
|
21
|
+
*/
|
|
22
|
+
commitBackfillHorizonMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class GitHubSyncService {
|
|
25
|
+
private readonly deps;
|
|
26
|
+
constructor(deps: GitHubSyncServiceDependencies);
|
|
27
|
+
/**
|
|
28
|
+
* The repos the workspace's installation can access, annotated with whether
|
|
29
|
+
* this workspace currently links each one. Repos are linked *explicitly* per
|
|
30
|
+
* workspace, so the connect UI lists these and the user picks a subset.
|
|
31
|
+
*/
|
|
32
|
+
listAvailableRepos(workspaceId: string): Promise<GitHubAvailableRepo[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Mark (or unmark) a repo as a monorepo (hosting several services, each pinned
|
|
35
|
+
* to a subdirectory). The flag is board-owned state on the projection. Lazily
|
|
36
|
+
* links the repo first if the workspace doesn't track it yet — the "add an
|
|
37
|
+
* existing repo as a service" flow flips this toggle before the repo is added,
|
|
38
|
+
* so it may not be linked. Throws only if the repo isn't accessible to the
|
|
39
|
+
* installation (the App hasn't been granted it).
|
|
40
|
+
*/
|
|
41
|
+
setRepoMonorepo(workspaceId: string, repoGithubId: number, isMonorepo: boolean): Promise<GitHubRepo>;
|
|
42
|
+
/**
|
|
43
|
+
* List the entries of a directory (one level) in a tracked repo, on its default
|
|
44
|
+
* branch, so the monorepo service picker can browse the repo and pin a service to a
|
|
45
|
+
* subdirectory. `path` is repo-root-relative ('' = the root). Directories are
|
|
46
|
+
* returned first (the picker navigates into those), then files for context.
|
|
47
|
+
*/
|
|
48
|
+
listRepoDirectory(workspaceId: string, repoGithubId: number, path?: string): Promise<RepoTreeEntry[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Link a single repo into this workspace without disturbing the rest (unlike
|
|
51
|
+
* {@link setLinkedRepos}, which sets the exact set and tombstones the others).
|
|
52
|
+
* Projects + deep-syncs the repo the first time it's seen, and is a no-op that
|
|
53
|
+
* returns the live row when it's already tracked. Returns null when the repo is
|
|
54
|
+
* not accessible to the installation (the App hasn't been granted it yet) — the
|
|
55
|
+
* caller surfaces a "grant the App access" hint. Backs the "add an existing
|
|
56
|
+
* repo as a board service" flow, where the workspace may not link the repo yet.
|
|
57
|
+
*/
|
|
58
|
+
linkRepo(workspaceId: string, repoGithubId: number): Promise<GitHubRepo | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Set the exact set of repos this workspace links. Projects the newly selected
|
|
61
|
+
* repos (from those the installation can access), tombstones any deselected
|
|
62
|
+
* repo, then deep-syncs each linked repo. Returns the workspace's live repos.
|
|
63
|
+
*/
|
|
64
|
+
setLinkedRepos(workspaceId: string, repoGithubIds: number[]): Promise<GitHubRepo[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Run one incremental fetch→upsert→cursor cycle for a single resource kind.
|
|
67
|
+
* Reads the prior cursor, fetches (conditionally, via the prior ETag/`since`),
|
|
68
|
+
* upserts when there's anything new, then advances the cursor. The differences
|
|
69
|
+
* between resources (how the request is shaped and how the next cursor is
|
|
70
|
+
* derived) are supplied by the callbacks; the cursor bookkeeping is shared.
|
|
71
|
+
*/
|
|
72
|
+
private syncResource;
|
|
73
|
+
/** Every workspace in the installation's org that actually links this repo. */
|
|
74
|
+
private linkedWorkspaces;
|
|
75
|
+
/**
|
|
76
|
+
* Incrementally resync one repo's branches, PRs, issues, commits and checks. The repo
|
|
77
|
+
* is fetched from GitHub ONCE (installation-scoped cursor) and each projection is fanned
|
|
78
|
+
* out to every workspace in the org that links it, so two teams sharing a repo cost one
|
|
79
|
+
* API round-trip, not two. Pass `full` (at link time) to bypass the shared cursor and
|
|
80
|
+
* fully populate a freshly-linked workspace.
|
|
81
|
+
*/
|
|
82
|
+
syncRepo(repo: GitHubRepo, options?: {
|
|
83
|
+
full?: boolean;
|
|
84
|
+
}): Promise<void>;
|
|
85
|
+
/** Resync a single tracked repo by its GitHub id (used by the queue consumer). */
|
|
86
|
+
syncRepoById(workspaceId: string, repoGithubId: number): Promise<void>;
|
|
87
|
+
/** Incremental resync of every repo this workspace links. */
|
|
88
|
+
resyncWorkspace(workspaceId: string): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Full backfill for an installation: deep-sync the linked repos of every
|
|
91
|
+
* workspace it backs (the connector workspace plus all workspaces in its
|
|
92
|
+
* account). Repos are linked explicitly, so backfill refreshes what's linked
|
|
93
|
+
* rather than rediscovering the whole installation.
|
|
94
|
+
*/
|
|
95
|
+
backfillInstallation(installationId: number): Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=GitHubSyncService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GitHubSyncService.d.ts","sourceRoot":"","sources":["../../../src/modules/github/GitHubSyncService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,qBAAqB,CAAA;AACtE,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,EACzB,+BAA+B,EAC/B,wBAAwB,EAGzB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAczF,MAAM,WAAW,6BAA6B;IAC5C,YAAY,EAAE,YAAY,CAAA;IAC1B,4BAA4B,EAAE,4BAA4B,CAAA;IAC1D,wBAAwB,EAAE,wBAAwB,CAAA;IAClD,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,+BAA+B,EAAE,+BAA+B,CAAA;IAChE,yBAAyB,EAAE,yBAAyB,CAAA;IACpD,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,4BAA4B,EAAE,4BAA4B,CAAA;IAC1D,KAAK,EAAE,KAAK,CAAA;IACZ;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,6BAA6B,EAAI;IAEpE;;;;OAIG;IACG,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAkB5E;IAED;;;;;;;OAOG;IACG,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,UAAU,CAAC,CAOrB;IAED;;;;;OAKG;IACG,iBAAiB,CACrB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,IAAI,SAAK,GACR,OAAO,CAAC,aAAa,EAAE,CAAC,CAyB1B;IAED;;;;;;;;OAQG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAepF;IAED;;;;OAIG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAuBxF;IAED;;;;;;OAMG;YACW,YAAY;IA+B1B,+EAA+E;YACjE,gBAAgB;IAO9B;;;;;;OAMG;IACG,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAsGhF;IAED,kFAAkF;IAC5E,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3E;IAED,6DAA6D;IACvD,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxD;IAED;;;;;OAKG;IACG,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhE;CACF"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
export class GitHubSyncService {
|
|
2
|
+
deps;
|
|
3
|
+
constructor(deps) {
|
|
4
|
+
this.deps = deps;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The repos the workspace's installation can access, annotated with whether
|
|
8
|
+
* this workspace currently links each one. Repos are linked *explicitly* per
|
|
9
|
+
* workspace, so the connect UI lists these and the user picks a subset.
|
|
10
|
+
*/
|
|
11
|
+
async listAvailableRepos(workspaceId) {
|
|
12
|
+
const installation = await this.deps.githubInstallationRepository.getByWorkspace(workspaceId);
|
|
13
|
+
if (!installation || installation.deletedAt)
|
|
14
|
+
return [];
|
|
15
|
+
const { items } = await this.deps.githubClient.listInstallationRepos(installation.installationId);
|
|
16
|
+
const tracked = new Map((await this.deps.repoProjectionRepository.list(workspaceId)).map((r) => [r.githubId, r]));
|
|
17
|
+
return items.map((r) => ({
|
|
18
|
+
githubId: r.githubId,
|
|
19
|
+
owner: r.owner,
|
|
20
|
+
name: r.name,
|
|
21
|
+
defaultBranch: r.defaultBranch,
|
|
22
|
+
private: r.private,
|
|
23
|
+
linked: tracked.has(r.githubId),
|
|
24
|
+
isMonorepo: tracked.get(r.githubId)?.isMonorepo ?? false,
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Mark (or unmark) a repo as a monorepo (hosting several services, each pinned
|
|
29
|
+
* to a subdirectory). The flag is board-owned state on the projection. Lazily
|
|
30
|
+
* links the repo first if the workspace doesn't track it yet — the "add an
|
|
31
|
+
* existing repo as a service" flow flips this toggle before the repo is added,
|
|
32
|
+
* so it may not be linked. Throws only if the repo isn't accessible to the
|
|
33
|
+
* installation (the App hasn't been granted it).
|
|
34
|
+
*/
|
|
35
|
+
async setRepoMonorepo(workspaceId, repoGithubId, isMonorepo) {
|
|
36
|
+
const existing = await this.linkRepo(workspaceId, repoGithubId);
|
|
37
|
+
if (!existing) {
|
|
38
|
+
throw new Error(`Repo ${repoGithubId} is not accessible to workspace '${workspaceId}'`);
|
|
39
|
+
}
|
|
40
|
+
await this.deps.repoProjectionRepository.setMonorepo(workspaceId, repoGithubId, isMonorepo);
|
|
41
|
+
return { ...existing, isMonorepo };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* List the entries of a directory (one level) in a tracked repo, on its default
|
|
45
|
+
* branch, so the monorepo service picker can browse the repo and pin a service to a
|
|
46
|
+
* subdirectory. `path` is repo-root-relative ('' = the root). Directories are
|
|
47
|
+
* returned first (the picker navigates into those), then files for context.
|
|
48
|
+
*/
|
|
49
|
+
async listRepoDirectory(workspaceId, repoGithubId, path = '') {
|
|
50
|
+
const installation = await this.deps.githubInstallationRepository.getByWorkspace(workspaceId);
|
|
51
|
+
if (!installation || installation.deletedAt)
|
|
52
|
+
return [];
|
|
53
|
+
// Lazily link the repo if the workspace doesn't track it yet (the add-service
|
|
54
|
+
// flow browses the tree before the repo is added), same as setRepoMonorepo.
|
|
55
|
+
const repo = await this.linkRepo(workspaceId, repoGithubId);
|
|
56
|
+
if (!repo) {
|
|
57
|
+
throw new Error(`Repo ${repoGithubId} is not accessible to workspace '${workspaceId}'`);
|
|
58
|
+
}
|
|
59
|
+
const entries = await this.deps.githubClient.listDirectory(installation.installationId, { owner: repo.owner, repo: repo.name }, path.replace(/^\/+|\/+$/g, ''), repo.defaultBranch ?? undefined);
|
|
60
|
+
const mapped = entries.map((e) => ({
|
|
61
|
+
path: e.path,
|
|
62
|
+
name: e.name,
|
|
63
|
+
type: e.type,
|
|
64
|
+
}));
|
|
65
|
+
return mapped.sort((a, b) => {
|
|
66
|
+
if (a.type === 'dir' && b.type !== 'dir')
|
|
67
|
+
return -1;
|
|
68
|
+
if (a.type !== 'dir' && b.type === 'dir')
|
|
69
|
+
return 1;
|
|
70
|
+
return a.name.localeCompare(b.name);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Link a single repo into this workspace without disturbing the rest (unlike
|
|
75
|
+
* {@link setLinkedRepos}, which sets the exact set and tombstones the others).
|
|
76
|
+
* Projects + deep-syncs the repo the first time it's seen, and is a no-op that
|
|
77
|
+
* returns the live row when it's already tracked. Returns null when the repo is
|
|
78
|
+
* not accessible to the installation (the App hasn't been granted it yet) — the
|
|
79
|
+
* caller surfaces a "grant the App access" hint. Backs the "add an existing
|
|
80
|
+
* repo as a board service" flow, where the workspace may not link the repo yet.
|
|
81
|
+
*/
|
|
82
|
+
async linkRepo(workspaceId, repoGithubId) {
|
|
83
|
+
const existing = await this.deps.repoProjectionRepository.get(workspaceId, repoGithubId);
|
|
84
|
+
if (existing)
|
|
85
|
+
return existing;
|
|
86
|
+
const installation = await this.deps.githubInstallationRepository.getByWorkspace(workspaceId);
|
|
87
|
+
if (!installation || installation.deletedAt)
|
|
88
|
+
return null;
|
|
89
|
+
const installationId = installation.installationId;
|
|
90
|
+
const { items } = await this.deps.githubClient.listInstallationRepos(installationId);
|
|
91
|
+
const match = items.find((r) => r.githubId === repoGithubId);
|
|
92
|
+
if (!match)
|
|
93
|
+
return null;
|
|
94
|
+
const repo = { ...match, installationId, syncedAt: this.deps.clock.now() };
|
|
95
|
+
await this.deps.repoProjectionRepository.upsertMany(workspaceId, [repo]);
|
|
96
|
+
// Full pass: the org cursor may already be advanced, so bypass it to populate this
|
|
97
|
+
// newly-linked workspace.
|
|
98
|
+
await this.syncRepo(repo, { full: true });
|
|
99
|
+
return this.deps.repoProjectionRepository.get(workspaceId, repoGithubId);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Set the exact set of repos this workspace links. Projects the newly selected
|
|
103
|
+
* repos (from those the installation can access), tombstones any deselected
|
|
104
|
+
* repo, then deep-syncs each linked repo. Returns the workspace's live repos.
|
|
105
|
+
*/
|
|
106
|
+
async setLinkedRepos(workspaceId, repoGithubIds) {
|
|
107
|
+
const installation = await this.deps.githubInstallationRepository.getByWorkspace(workspaceId);
|
|
108
|
+
if (!installation || installation.deletedAt)
|
|
109
|
+
return [];
|
|
110
|
+
const installationId = installation.installationId;
|
|
111
|
+
const wanted = new Set(repoGithubIds);
|
|
112
|
+
const { items } = await this.deps.githubClient.listInstallationRepos(installationId);
|
|
113
|
+
const selected = items
|
|
114
|
+
.filter((r) => wanted.has(r.githubId))
|
|
115
|
+
.map((r) => ({ ...r, installationId, syncedAt: this.deps.clock.now() }));
|
|
116
|
+
if (selected.length > 0) {
|
|
117
|
+
await this.deps.repoProjectionRepository.upsertMany(workspaceId, selected);
|
|
118
|
+
}
|
|
119
|
+
// Tombstone every previously-linked repo for this installation that is no
|
|
120
|
+
// longer selected (the "seen" set is exactly the new selection).
|
|
121
|
+
await this.deps.repoProjectionRepository.tombstoneMissing(workspaceId, installationId, selected.map((r) => r.githubId), this.deps.clock.now());
|
|
122
|
+
for (const repo of selected)
|
|
123
|
+
await this.syncRepo(repo, { full: true });
|
|
124
|
+
return this.deps.repoProjectionRepository.list(workspaceId);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Run one incremental fetch→upsert→cursor cycle for a single resource kind.
|
|
128
|
+
* Reads the prior cursor, fetches (conditionally, via the prior ETag/`since`),
|
|
129
|
+
* upserts when there's anything new, then advances the cursor. The differences
|
|
130
|
+
* between resources (how the request is shaped and how the next cursor is
|
|
131
|
+
* derived) are supplied by the callbacks; the cursor bookkeeping is shared.
|
|
132
|
+
*/
|
|
133
|
+
async syncResource(installationId, repoGithubId, kind, full, fetch, upsert, nextCursor) {
|
|
134
|
+
const repos = this.deps.repoProjectionRepository;
|
|
135
|
+
// The cursor is installation-scoped (shared across the org's workspaces). A `full`
|
|
136
|
+
// pass ignores it (treats it as empty) so a newly-linked workspace gets fully
|
|
137
|
+
// populated even though the org's cursor is already advanced.
|
|
138
|
+
const cursor = full ? null : await repos.getCursor(installationId, repoGithubId, kind);
|
|
139
|
+
const res = await fetch(cursor);
|
|
140
|
+
if (!res.notModified && res.items.length > 0)
|
|
141
|
+
await upsert(res.items);
|
|
142
|
+
await repos.setCursor(installationId, repoGithubId, kind, nextCursor(cursor, res.etag, this.deps.clock.now()));
|
|
143
|
+
return res;
|
|
144
|
+
}
|
|
145
|
+
/** Every workspace in the installation's org that actually links this repo. */
|
|
146
|
+
async linkedWorkspaces(installationId, repoGithubId) {
|
|
147
|
+
const all = await this.deps.githubInstallationRepository.listWorkspacesForInstallation(installationId);
|
|
148
|
+
// One batched query for the whole org, instead of a `get` per workspace.
|
|
149
|
+
return this.deps.repoProjectionRepository.linkedWorkspaces(repoGithubId, all);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Incrementally resync one repo's branches, PRs, issues, commits and checks. The repo
|
|
153
|
+
* is fetched from GitHub ONCE (installation-scoped cursor) and each projection is fanned
|
|
154
|
+
* out to every workspace in the org that links it, so two teams sharing a repo cost one
|
|
155
|
+
* API round-trip, not two. Pass `full` (at link time) to bypass the shared cursor and
|
|
156
|
+
* fully populate a freshly-linked workspace.
|
|
157
|
+
*/
|
|
158
|
+
async syncRepo(repo, options = {}) {
|
|
159
|
+
const full = options.full ?? false;
|
|
160
|
+
const ref = { owner: repo.owner, repo: repo.name };
|
|
161
|
+
const id = repo.githubId;
|
|
162
|
+
const installationId = repo.installationId;
|
|
163
|
+
const client = this.deps.githubClient;
|
|
164
|
+
const workspaces = await this.linkedWorkspaces(installationId, id);
|
|
165
|
+
const fanOut = async (apply) => {
|
|
166
|
+
for (const ws of workspaces)
|
|
167
|
+
await apply(ws);
|
|
168
|
+
};
|
|
169
|
+
// ETag-conditional cursor: carry the prior ETag forward when the fetch
|
|
170
|
+
// returns none. PRs/issues (`stampSince`) additionally record a fresh
|
|
171
|
+
// `sinceIso` lower bound for the next delta; branches don't paginate by date.
|
|
172
|
+
const etagCursor = (stampSince) => (prev, etag, now) => ({
|
|
173
|
+
etag: etag ?? prev?.etag ?? null,
|
|
174
|
+
lastSyncedAt: now,
|
|
175
|
+
sinceIso: stampSince ? new Date(now).toISOString() : null,
|
|
176
|
+
});
|
|
177
|
+
// Branches — conditional GET via ETag.
|
|
178
|
+
const branches = await this.syncResource(installationId, id, 'branches', full, (cursor) => client.listBranches(installationId, ref, cursor?.etag ?? undefined), (items) => fanOut((ws) => this.deps.branchProjectionRepository.upsertMany(ws, items)), etagCursor(false));
|
|
179
|
+
const defaultBranchSha = branches.items.find((b) => b.name === repo.defaultBranch)?.headSha ?? null;
|
|
180
|
+
// Pull requests — delta by `since` (GitHub's updated_at lower bound).
|
|
181
|
+
await this.syncResource(installationId, id, 'pulls', full, (cursor) => client.listPullRequests(installationId, ref, {
|
|
182
|
+
since: cursor?.sinceIso ?? undefined,
|
|
183
|
+
etag: cursor?.etag ?? undefined,
|
|
184
|
+
}), (items) => fanOut((ws) => this.deps.pullRequestProjectionRepository.upsertMany(ws, items)), etagCursor(true));
|
|
185
|
+
// Issues — delta by `since`.
|
|
186
|
+
await this.syncResource(installationId, id, 'issues', full, (cursor) => client.listIssues(installationId, ref, {
|
|
187
|
+
since: cursor?.sinceIso ?? undefined,
|
|
188
|
+
etag: cursor?.etag ?? undefined,
|
|
189
|
+
}), (items) => fanOut((ws) => this.deps.issueProjectionRepository.upsertMany(ws, items)), etagCursor(true));
|
|
190
|
+
// Commits — delta by `since` on the default branch. On the first sync there
|
|
191
|
+
// is no cursor, so fall back to the backfill horizon (if configured) instead
|
|
192
|
+
// of fetching the repo's entire commit history in one step.
|
|
193
|
+
const commitBackfillSince = this.deps.commitBackfillHorizonMs !== undefined
|
|
194
|
+
? new Date(this.deps.clock.now() - this.deps.commitBackfillHorizonMs).toISOString()
|
|
195
|
+
: undefined;
|
|
196
|
+
await this.syncResource(installationId, id, 'commits', full, (cursor) => client.listCommits(installationId, ref, {
|
|
197
|
+
since: cursor?.sinceIso ?? commitBackfillSince,
|
|
198
|
+
}), (items) => fanOut((ws) => this.deps.commitProjectionRepository.upsertMany(ws, items)), (_prev, _etag, now) => ({
|
|
199
|
+
etag: null,
|
|
200
|
+
lastSyncedAt: now,
|
|
201
|
+
sinceIso: new Date(now).toISOString(),
|
|
202
|
+
}));
|
|
203
|
+
// Check runs for the default-branch head (CI gating signal). Not cursor-based.
|
|
204
|
+
if (defaultBranchSha) {
|
|
205
|
+
const checks = await client.listCheckRuns(installationId, ref, defaultBranchSha);
|
|
206
|
+
if (checks.items.length > 0) {
|
|
207
|
+
await fanOut((ws) => this.deps.checkRunProjectionRepository.upsertMany(ws, checks.items));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Stamp the repo row as freshly synced for every workspace that links it.
|
|
211
|
+
const now = this.deps.clock.now();
|
|
212
|
+
await fanOut((ws) => this.deps.repoProjectionRepository.upsertMany(ws, [{ ...repo, syncedAt: now }]));
|
|
213
|
+
}
|
|
214
|
+
/** Resync a single tracked repo by its GitHub id (used by the queue consumer). */
|
|
215
|
+
async syncRepoById(workspaceId, repoGithubId) {
|
|
216
|
+
const repo = await this.deps.repoProjectionRepository.get(workspaceId, repoGithubId);
|
|
217
|
+
if (repo)
|
|
218
|
+
await this.syncRepo(repo);
|
|
219
|
+
}
|
|
220
|
+
/** Incremental resync of every repo this workspace links. */
|
|
221
|
+
async resyncWorkspace(workspaceId) {
|
|
222
|
+
const repos = await this.deps.repoProjectionRepository.list(workspaceId);
|
|
223
|
+
for (const repo of repos)
|
|
224
|
+
await this.syncRepo(repo);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Full backfill for an installation: deep-sync the linked repos of every
|
|
228
|
+
* workspace it backs (the connector workspace plus all workspaces in its
|
|
229
|
+
* account). Repos are linked explicitly, so backfill refreshes what's linked
|
|
230
|
+
* rather than rediscovering the whole installation.
|
|
231
|
+
*/
|
|
232
|
+
async backfillInstallation(installationId) {
|
|
233
|
+
const installation = await this.deps.githubInstallationRepository.getByInstallationId(installationId);
|
|
234
|
+
if (!installation || installation.deletedAt)
|
|
235
|
+
return;
|
|
236
|
+
const workspaceIds = await this.deps.githubInstallationRepository.listWorkspacesForInstallation(installationId);
|
|
237
|
+
for (const ws of workspaceIds)
|
|
238
|
+
await this.resyncWorkspace(ws);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=GitHubSyncService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GitHubSyncService.js","sourceRoot":"","sources":["../../../src/modules/github/GitHubSyncService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CreateRepoInput, GitHubProvisioningClient, ProvisionedRepo } from '@cat-factory/kernel';
|
|
2
|
+
/** Why a repo wasn't created directly (informational; the caller delegates regardless). */
|
|
3
|
+
export type DelegationReason = 'insufficient_permissions' | 'forbidden' | 'already_exists';
|
|
4
|
+
export type ProvisionResult = {
|
|
5
|
+
status: 'created';
|
|
6
|
+
repo: ProvisionedRepo;
|
|
7
|
+
} | {
|
|
8
|
+
status: 'delegated';
|
|
9
|
+
reason: DelegationReason;
|
|
10
|
+
};
|
|
11
|
+
export interface RepoProvisioningServiceDependencies {
|
|
12
|
+
/** Mints tokens for the installation's owning App (the registry resolver). */
|
|
13
|
+
client: GitHubProvisioningClient;
|
|
14
|
+
}
|
|
15
|
+
export declare class RepoProvisioningService {
|
|
16
|
+
private readonly deps;
|
|
17
|
+
constructor(deps: RepoProvisioningServiceDependencies);
|
|
18
|
+
/**
|
|
19
|
+
* Create `input.name` under `input.org` using `installationId`'s credentials.
|
|
20
|
+
* Guards proactively on the installation's *granted* permissions (skips a
|
|
21
|
+
* guaranteed-403 round trip); a live 403 or a 422 "already exists" also resolve
|
|
22
|
+
* to `delegated` so the caller's manual/existing-repo path takes over.
|
|
23
|
+
*/
|
|
24
|
+
provision(installationId: number, input: CreateRepoInput): Promise<ProvisionResult>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=RepoProvisioningService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RepoProvisioningService.d.ts","sourceRoot":"","sources":["../../../src/modules/github/RepoProvisioningService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,wBAAwB,EACxB,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAU5B,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,GACxB,0BAA0B,GAC1B,WAAW,GACX,gBAAgB,CAAA;AAEpB,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GAC5C;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAErD,MAAM,WAAW,mCAAmC;IAClD,8EAA8E;IAC9E,MAAM,EAAE,wBAAwB,CAAA;CACjC;AAOD,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,mCAAmC,EAAI;IAE1E;;;;;OAKG;IACG,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAexF;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { canCreateRepo } from './provisioning.logic.js';
|
|
2
|
+
/** Sniff an HTTP status without coupling to a specific error class. */
|
|
3
|
+
function statusOf(err) {
|
|
4
|
+
return err?.status;
|
|
5
|
+
}
|
|
6
|
+
export class RepoProvisioningService {
|
|
7
|
+
deps;
|
|
8
|
+
constructor(deps) {
|
|
9
|
+
this.deps = deps;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create `input.name` under `input.org` using `installationId`'s credentials.
|
|
13
|
+
* Guards proactively on the installation's *granted* permissions (skips a
|
|
14
|
+
* guaranteed-403 round trip); a live 403 or a 422 "already exists" also resolve
|
|
15
|
+
* to `delegated` so the caller's manual/existing-repo path takes over.
|
|
16
|
+
*/
|
|
17
|
+
async provision(installationId, input) {
|
|
18
|
+
const permissions = await this.deps.client.getGrantedPermissions(installationId);
|
|
19
|
+
if (!canCreateRepo(permissions)) {
|
|
20
|
+
return { status: 'delegated', reason: 'insufficient_permissions' };
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const repo = await this.deps.client.createRepoInOrg(installationId, input);
|
|
24
|
+
return { status: 'created', repo };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const status = statusOf(err);
|
|
28
|
+
if (status === 403)
|
|
29
|
+
return { status: 'delegated', reason: 'forbidden' };
|
|
30
|
+
if (status === 422)
|
|
31
|
+
return { status: 'delegated', reason: 'already_exists' };
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=RepoProvisioningService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RepoProvisioningService.js","sourceRoot":"","sources":["../../../src/modules/github/RepoProvisioningService.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAwBvD,uEAAuE;AACvE,SAAS,QAAQ,CAAC,GAAY;IAC5B,OAAQ,GAAkC,EAAE,MAAM,CAAA;AACpD,CAAC;AAED,MAAM,OAAO,uBAAuB;IACL,IAAI;IAAjC,YAA6B,IAAyC;oBAAzC,IAAI;IAAwC,CAAC;IAE1E;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CAAC,cAAsB,EAAE,KAAsB;QAC5D,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,cAAc,CAAC,CAAA;QAChF,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAA;QACpE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;YAC1E,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;YAC5B,IAAI,MAAM,KAAK,GAAG;gBAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;YACvE,IAAI,MAAM,KAAK,GAAG;gBAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;YAC5E,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Clock } from '@cat-factory/kernel';
|
|
2
|
+
import type { BranchProjectionRepository, CheckRunProjectionRepository, CommitProjectionRepository, GitHubInstallationRepository, IssueProjectionRepository, PullRequestProjectionRepository, RepoProjectionRepository } from '@cat-factory/kernel';
|
|
3
|
+
export interface WebhookServiceDependencies {
|
|
4
|
+
githubInstallationRepository: GitHubInstallationRepository;
|
|
5
|
+
repoProjectionRepository: RepoProjectionRepository;
|
|
6
|
+
branchProjectionRepository: BranchProjectionRepository;
|
|
7
|
+
pullRequestProjectionRepository: PullRequestProjectionRepository;
|
|
8
|
+
issueProjectionRepository: IssueProjectionRepository;
|
|
9
|
+
commitProjectionRepository: CommitProjectionRepository;
|
|
10
|
+
checkRunProjectionRepository: CheckRunProjectionRepository;
|
|
11
|
+
clock: Clock;
|
|
12
|
+
}
|
|
13
|
+
export declare class WebhookService {
|
|
14
|
+
private readonly deps;
|
|
15
|
+
constructor(deps: WebhookServiceDependencies);
|
|
16
|
+
/** Apply a verified webhook delivery to the local projections. */
|
|
17
|
+
handle(eventName: string, payload: unknown): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Apply `project` to every workspace backed by this installation that actually
|
|
20
|
+
* links the affected repo. Since repos are linked explicitly per workspace, an
|
|
21
|
+
* event for a repo only updates the boards that chose to track it.
|
|
22
|
+
*/
|
|
23
|
+
private forEachLinkedWorkspace;
|
|
24
|
+
private handleInstallation;
|
|
25
|
+
private installationIdOf;
|
|
26
|
+
private repoIdOf;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=WebhookService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookService.d.ts","sourceRoot":"","sources":["../../../src/modules/github/WebhookService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,EACzB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAyB5B,MAAM,WAAW,0BAA0B;IACzC,4BAA4B,EAAE,4BAA4B,CAAA;IAC1D,wBAAwB,EAAE,wBAAwB,CAAA;IAClD,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,+BAA+B,EAAE,+BAA+B,CAAA;IAChE,yBAAyB,EAAE,yBAAyB,CAAA;IACpD,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,4BAA4B,EAAE,4BAA4B,CAAA;IAC1D,KAAK,EAAE,KAAK,CAAA;CACb;AAQD,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,0BAA0B,EAAI;IAEjE,kEAAkE;IAC5D,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF/D;IAED;;;;OAIG;YACW,sBAAsB;YAatB,kBAAkB;IAgDhC,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,QAAQ;CAKjB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { pullRepoGithubId, toCheckRunProjection, toCommitProjection, toIssueProjection, toPullRequestProjection, } from './projection.logic.js';
|
|
2
|
+
function asObject(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null ? value : null;
|
|
4
|
+
}
|
|
5
|
+
export class WebhookService {
|
|
6
|
+
deps;
|
|
7
|
+
constructor(deps) {
|
|
8
|
+
this.deps = deps;
|
|
9
|
+
}
|
|
10
|
+
/** Apply a verified webhook delivery to the local projections. */
|
|
11
|
+
async handle(eventName, payload) {
|
|
12
|
+
const root = asObject(payload);
|
|
13
|
+
if (!root)
|
|
14
|
+
return;
|
|
15
|
+
if (eventName === 'installation' || eventName === 'installation_repositories') {
|
|
16
|
+
await this.handleInstallation(eventName, root);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const installationId = this.installationIdOf(root);
|
|
20
|
+
if (installationId === null)
|
|
21
|
+
return;
|
|
22
|
+
const installation = await this.deps.githubInstallationRepository.getByInstallationId(installationId);
|
|
23
|
+
if (!installation || installation.deletedAt)
|
|
24
|
+
return;
|
|
25
|
+
const now = this.deps.clock.now();
|
|
26
|
+
switch (eventName) {
|
|
27
|
+
case 'pull_request': {
|
|
28
|
+
const pr = asObject(root.pull_request);
|
|
29
|
+
if (!pr)
|
|
30
|
+
return;
|
|
31
|
+
const repoId = pullRepoGithubId(pr) ?? this.repoIdOf(root);
|
|
32
|
+
if (repoId === null)
|
|
33
|
+
return;
|
|
34
|
+
await this.forEachLinkedWorkspace(installationId, repoId, (ws) => this.deps.pullRequestProjectionRepository.upsertMany(ws, [
|
|
35
|
+
toPullRequestProjection(pr, repoId, now),
|
|
36
|
+
]));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
case 'issues': {
|
|
40
|
+
const issue = asObject(root.issue);
|
|
41
|
+
const repoId = this.repoIdOf(root);
|
|
42
|
+
if (!issue || repoId === null)
|
|
43
|
+
return;
|
|
44
|
+
await this.forEachLinkedWorkspace(installationId, repoId, (ws) => this.deps.issueProjectionRepository.upsertMany(ws, [
|
|
45
|
+
toIssueProjection(issue, repoId, now),
|
|
46
|
+
]));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
case 'push': {
|
|
50
|
+
const repoId = this.repoIdOf(root);
|
|
51
|
+
if (repoId === null)
|
|
52
|
+
return;
|
|
53
|
+
// Update the pushed branch head and project the new commits.
|
|
54
|
+
const ref = typeof root.ref === 'string' ? root.ref : '';
|
|
55
|
+
const after = typeof root.after === 'string' ? root.after : '';
|
|
56
|
+
const commits = Array.isArray(root.commits) ? root.commits : [];
|
|
57
|
+
await this.forEachLinkedWorkspace(installationId, repoId, async (ws) => {
|
|
58
|
+
if (ref.startsWith('refs/heads/') && after) {
|
|
59
|
+
await this.deps.branchProjectionRepository.upsertMany(ws, [
|
|
60
|
+
{
|
|
61
|
+
repoGithubId: repoId,
|
|
62
|
+
name: ref.slice('refs/heads/'.length),
|
|
63
|
+
headSha: after,
|
|
64
|
+
protected: false,
|
|
65
|
+
syncedAt: now,
|
|
66
|
+
},
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
if (commits.length > 0) {
|
|
70
|
+
await this.deps.commitProjectionRepository.upsertMany(ws, commits.map((c) => toCommitProjection(c, repoId, now)));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
case 'check_run': {
|
|
76
|
+
const check = asObject(root.check_run);
|
|
77
|
+
const repoId = this.repoIdOf(root);
|
|
78
|
+
if (!check || repoId === null)
|
|
79
|
+
return;
|
|
80
|
+
await this.forEachLinkedWorkspace(installationId, repoId, (ws) => this.deps.checkRunProjectionRepository.upsertMany(ws, [
|
|
81
|
+
toCheckRunProjection(check, repoId, now),
|
|
82
|
+
]));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
default:
|
|
86
|
+
// Unhandled event kind: nothing to project incrementally.
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Apply `project` to every workspace backed by this installation that actually
|
|
92
|
+
* links the affected repo. Since repos are linked explicitly per workspace, an
|
|
93
|
+
* event for a repo only updates the boards that chose to track it.
|
|
94
|
+
*/
|
|
95
|
+
async forEachLinkedWorkspace(installationId, repoGithubId, project) {
|
|
96
|
+
const workspaceIds = await this.deps.githubInstallationRepository.listWorkspacesForInstallation(installationId);
|
|
97
|
+
for (const ws of workspaceIds) {
|
|
98
|
+
const repo = await this.deps.repoProjectionRepository.get(ws, repoGithubId);
|
|
99
|
+
if (repo)
|
|
100
|
+
await project(ws);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async handleInstallation(eventName, root) {
|
|
104
|
+
const installationId = this.installationIdOf(root);
|
|
105
|
+
if (installationId === null)
|
|
106
|
+
return;
|
|
107
|
+
const installation = await this.deps.githubInstallationRepository.getByInstallationId(installationId);
|
|
108
|
+
if (!installation)
|
|
109
|
+
return;
|
|
110
|
+
const now = this.deps.clock.now();
|
|
111
|
+
const action = typeof root.action === 'string' ? root.action : '';
|
|
112
|
+
// Lifecycle: suspend/uninstall tombstones the binding (account-wide, since the
|
|
113
|
+
// installation is shared); unsuspend revives it. Repos are NOT auto-projected
|
|
114
|
+
// here — they are linked explicitly per workspace.
|
|
115
|
+
if (eventName === 'installation') {
|
|
116
|
+
if (action === 'deleted' || action === 'suspend') {
|
|
117
|
+
await this.deps.githubInstallationRepository.softDelete(installationId, now);
|
|
118
|
+
}
|
|
119
|
+
else if (action === 'unsuspend') {
|
|
120
|
+
await this.deps.githubInstallationRepository.upsert({ ...installation, deletedAt: null });
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// installation_repositories: a removed repo is no longer accessible, so
|
|
125
|
+
// tombstone it in every workspace that linked it. Added repos just become
|
|
126
|
+
// available — the user links them explicitly, so we don't project them here.
|
|
127
|
+
const removed = Array.isArray(root.repositories_removed)
|
|
128
|
+
? root.repositories_removed
|
|
129
|
+
: [];
|
|
130
|
+
if (removed.length === 0)
|
|
131
|
+
return;
|
|
132
|
+
const workspaceIds = await this.deps.githubInstallationRepository.listWorkspacesForInstallation(installationId);
|
|
133
|
+
for (const ws of workspaceIds) {
|
|
134
|
+
const tracked = await this.deps.repoProjectionRepository.list(ws);
|
|
135
|
+
const removedIds = new Set(removed.map((r) => r.id));
|
|
136
|
+
const remaining = tracked
|
|
137
|
+
.filter((repo) => repo.installationId === installationId && !removedIds.has(repo.githubId))
|
|
138
|
+
.map((repo) => repo.githubId);
|
|
139
|
+
// Only rewrite when this workspace actually tracks one of the removed repos.
|
|
140
|
+
if (remaining.length !== tracked.filter((r) => r.installationId === installationId).length) {
|
|
141
|
+
await this.deps.repoProjectionRepository.tombstoneMissing(ws, installationId, remaining, now);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
installationIdOf(root) {
|
|
146
|
+
const inst = asObject(root.installation);
|
|
147
|
+
const id = inst?.id;
|
|
148
|
+
return typeof id === 'number' ? id : null;
|
|
149
|
+
}
|
|
150
|
+
repoIdOf(root) {
|
|
151
|
+
const repo = asObject(root.repository);
|
|
152
|
+
const id = repo?.id;
|
|
153
|
+
return typeof id === 'number' ? id : null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=WebhookService.js.map
|