@checkstack/gitops-backend 0.1.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.
@@ -0,0 +1,158 @@
1
+ import type { Logger, SafeDatabase } from "@checkstack/backend-api";
2
+ import type { QueueManager } from "@checkstack/queue-api";
3
+ import type { InternalEntityKindRegistry } from "../kind-registry";
4
+ import type { SecretStore } from "../secret-resolver";
5
+ import * as schema from "../schema";
6
+ import { reconcileProvider } from "./reconciler";
7
+ import { githubScraper } from "../scrapers/github-scraper";
8
+ import { gitlabScraper } from "../scrapers/gitlab-scraper";
9
+ import { eq } from "drizzle-orm";
10
+
11
+ type Db = SafeDatabase<typeof schema>;
12
+
13
+ const SYNC_QUEUE = "gitops-sync";
14
+
15
+ interface SyncJobPayload {
16
+ providerId: string;
17
+ }
18
+
19
+ interface SyncWorkerDeps {
20
+ db: Db;
21
+ logger: Logger;
22
+ queueManager: QueueManager;
23
+ kindRegistry: InternalEntityKindRegistry;
24
+ secretStore: SecretStore;
25
+ }
26
+
27
+ /**
28
+ * Sets up the GitOps sync worker:
29
+ * 1. Registers a consumer on the `gitops-sync` queue
30
+ * 2. Bootstraps recurring jobs for all existing providers
31
+ */
32
+ export async function setupSyncWorker(deps: SyncWorkerDeps): Promise<void> {
33
+ const { db, logger, queueManager, kindRegistry, secretStore } = deps;
34
+
35
+ const queue = queueManager.getQueue<SyncJobPayload>(SYNC_QUEUE);
36
+
37
+ // Register consumer
38
+ await queue.consume(
39
+ async (job) => {
40
+ const { providerId } = job.data;
41
+ logger.info(`GitOps sync: starting sync for provider ${providerId}`);
42
+
43
+ try {
44
+ const result = await runSyncForProvider({
45
+ providerId,
46
+ db,
47
+ logger,
48
+ kindRegistry,
49
+ secretStore,
50
+ });
51
+
52
+ logger.info(
53
+ `GitOps sync: completed for provider ${providerId} — ` +
54
+ `created=${result.created} updated=${result.updated} unchanged=${result.unchanged} ` +
55
+ `orphaned=${result.orphaned} deleted=${result.deleted} errors=${result.errors}`,
56
+ );
57
+ } catch (error) {
58
+ logger.error(
59
+ `GitOps sync: failed for provider ${providerId}: ${error}`,
60
+ );
61
+ }
62
+ },
63
+ { consumerGroup: "gitops-sync-worker" },
64
+ );
65
+
66
+ // Bootstrap: schedule recurring jobs for all existing providers
67
+ const providers = await db.select().from(schema.providers);
68
+
69
+ for (const provider of providers) {
70
+ await scheduleSyncForProvider({
71
+ queueManager,
72
+ providerId: provider.id,
73
+ syncIntervalSeconds: provider.syncInterval,
74
+ });
75
+ }
76
+
77
+ logger.info(
78
+ `GitOps sync worker initialized. ${providers.length} provider(s) scheduled.`,
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Loads a provider from DB and runs the reconciliation cycle.
84
+ */
85
+ async function runSyncForProvider(params: {
86
+ providerId: string;
87
+ db: Db;
88
+ logger: Logger;
89
+ kindRegistry: InternalEntityKindRegistry;
90
+ secretStore: SecretStore;
91
+ }) {
92
+ const { providerId, db, logger, kindRegistry, secretStore } = params;
93
+
94
+ const rows = await db
95
+ .select()
96
+ .from(schema.providers)
97
+ .where(eq(schema.providers.id, providerId));
98
+
99
+ const provider = rows[0];
100
+ if (!provider) {
101
+ throw new Error(`Provider not found: ${providerId}`);
102
+ }
103
+
104
+ const scraper =
105
+ provider.type === "github" ? githubScraper : gitlabScraper;
106
+
107
+ return reconcileProvider({
108
+ providerId: provider.id,
109
+ providerType: provider.type,
110
+ target: provider.target,
111
+ pathPattern: provider.pathPattern,
112
+ authToken: provider.authToken ?? "",
113
+ baseUrl: provider.baseUrl ?? undefined,
114
+ deletionPolicy: provider.deletionPolicy,
115
+ db,
116
+ logger,
117
+ kindRegistry,
118
+ secretStore,
119
+ scraper,
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Schedules (or updates) a recurring sync job for a provider.
125
+ * Called during bootstrap and when providers are created/updated.
126
+ */
127
+ export async function scheduleSyncForProvider(params: {
128
+ queueManager: QueueManager;
129
+ providerId: string;
130
+ syncIntervalSeconds: number;
131
+ }): Promise<void> {
132
+ const { queueManager, providerId, syncIntervalSeconds } = params;
133
+ const queue = queueManager.getQueue<SyncJobPayload>(SYNC_QUEUE);
134
+
135
+ await queue.scheduleRecurring(
136
+ { providerId },
137
+ {
138
+ jobId: `gitops-sync-${providerId}`,
139
+ intervalSeconds: syncIntervalSeconds,
140
+ },
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Enqueues a one-off sync job for a provider (used by triggerSync RPC).
146
+ */
147
+ export async function triggerSyncForProvider(params: {
148
+ queueManager: QueueManager;
149
+ providerId: string;
150
+ }): Promise<void> {
151
+ const { queueManager, providerId } = params;
152
+ const queue = queueManager.getQueue<SyncJobPayload>(SYNC_QUEUE);
153
+
154
+ await queue.enqueue(
155
+ { providerId },
156
+ { jobId: `gitops-sync-${providerId}-manual` },
157
+ );
158
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@checkstack/tsconfig/backend.json",
3
+ "include": ["src"]
4
+ }