@larkiny/astro-github-loader 0.11.3 ā 0.13.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/README.md +35 -55
- package/dist/github.assets.d.ts +70 -0
- package/dist/github.assets.js +253 -0
- package/dist/github.auth.js +13 -9
- package/dist/github.cleanup.d.ts +3 -2
- package/dist/github.cleanup.js +30 -23
- package/dist/github.constants.d.ts +0 -16
- package/dist/github.constants.js +0 -16
- package/dist/github.content.d.ts +5 -131
- package/dist/github.content.js +152 -794
- package/dist/github.dryrun.d.ts +9 -5
- package/dist/github.dryrun.js +49 -25
- package/dist/github.link-transform.d.ts +2 -2
- package/dist/github.link-transform.js +68 -57
- package/dist/github.loader.js +30 -46
- package/dist/github.logger.d.ts +2 -2
- package/dist/github.logger.js +33 -24
- package/dist/github.paths.d.ts +76 -0
- package/dist/github.paths.js +190 -0
- package/dist/github.storage.d.ts +16 -0
- package/dist/github.storage.js +115 -0
- package/dist/github.types.d.ts +40 -4
- package/dist/index.d.ts +8 -6
- package/dist/index.js +3 -6
- package/dist/test-helpers.d.ts +130 -0
- package/dist/test-helpers.js +194 -0
- package/package.json +3 -1
- package/src/github.assets.spec.ts +717 -0
- package/src/github.assets.ts +365 -0
- package/src/github.auth.spec.ts +245 -0
- package/src/github.auth.ts +24 -10
- package/src/github.cleanup.spec.ts +380 -0
- package/src/github.cleanup.ts +91 -47
- package/src/github.constants.ts +0 -17
- package/src/github.content.spec.ts +305 -454
- package/src/github.content.ts +259 -957
- package/src/github.dryrun.spec.ts +598 -0
- package/src/github.dryrun.ts +108 -54
- package/src/github.link-transform.spec.ts +1345 -0
- package/src/github.link-transform.ts +177 -95
- package/src/github.loader.spec.ts +75 -50
- package/src/github.loader.ts +101 -76
- package/src/github.logger.spec.ts +795 -0
- package/src/github.logger.ts +77 -35
- package/src/github.paths.spec.ts +523 -0
- package/src/github.paths.ts +259 -0
- package/src/github.storage.spec.ts +377 -0
- package/src/github.storage.ts +135 -0
- package/src/github.types.ts +54 -9
- package/src/index.ts +43 -6
- package/src/test-helpers.ts +215 -0
package/src/github.dryrun.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { Octokit } from "octokit";
|
|
5
|
+
import type { Logger } from "./github.logger.js";
|
|
4
6
|
import type { ImportOptions, LoaderContext } from "./github.types.js";
|
|
5
7
|
|
|
6
|
-
const STATE_FILENAME =
|
|
8
|
+
const STATE_FILENAME = ".github-import-state.json";
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Represents the state of a single import configuration
|
|
@@ -55,30 +57,51 @@ export interface RepositoryChangeInfo {
|
|
|
55
57
|
* Creates a unique identifier for an import configuration
|
|
56
58
|
*/
|
|
57
59
|
export function createConfigId(config: ImportOptions): string {
|
|
58
|
-
|
|
60
|
+
if (config.stateKey) {
|
|
61
|
+
return config.stateKey;
|
|
62
|
+
}
|
|
63
|
+
return `${config.owner}/${config.repo}@${config.ref || "main"}`;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* Loads the import state from the state file
|
|
63
68
|
*/
|
|
64
|
-
export async function loadImportState(
|
|
69
|
+
export async function loadImportState(
|
|
70
|
+
workingDir: string,
|
|
71
|
+
logger?: Logger,
|
|
72
|
+
): Promise<StateFile> {
|
|
65
73
|
const statePath = join(workingDir, STATE_FILENAME);
|
|
66
|
-
|
|
74
|
+
|
|
67
75
|
if (!existsSync(statePath)) {
|
|
68
76
|
return {
|
|
69
77
|
imports: {},
|
|
70
|
-
lastChecked: new Date().toISOString()
|
|
78
|
+
lastChecked: new Date().toISOString(),
|
|
71
79
|
};
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
try {
|
|
75
|
-
const content = await fs.readFile(statePath,
|
|
76
|
-
|
|
83
|
+
const content = await fs.readFile(statePath, "utf-8");
|
|
84
|
+
const parsed = JSON.parse(content);
|
|
85
|
+
// Validate the parsed state has the expected shape
|
|
86
|
+
if (
|
|
87
|
+
!parsed ||
|
|
88
|
+
typeof parsed !== "object" ||
|
|
89
|
+
!("imports" in parsed) ||
|
|
90
|
+
typeof parsed.imports !== "object"
|
|
91
|
+
) {
|
|
92
|
+
const msg = `Malformed state file at ${statePath}, starting fresh`;
|
|
93
|
+
// eslint-disable-next-line no-console -- fallback when no logger provided
|
|
94
|
+
logger ? logger.warn(msg) : console.warn(msg);
|
|
95
|
+
return { imports: {}, lastChecked: new Date().toISOString() };
|
|
96
|
+
}
|
|
97
|
+
return parsed;
|
|
77
98
|
} catch (error) {
|
|
78
|
-
|
|
99
|
+
const msg = `Failed to load import state from ${statePath}, starting fresh: ${error}`;
|
|
100
|
+
// eslint-disable-next-line no-console -- fallback when no logger provided
|
|
101
|
+
logger ? logger.warn(msg) : console.warn(msg);
|
|
79
102
|
return {
|
|
80
103
|
imports: {},
|
|
81
|
-
lastChecked: new Date().toISOString()
|
|
104
|
+
lastChecked: new Date().toISOString(),
|
|
82
105
|
};
|
|
83
106
|
}
|
|
84
107
|
}
|
|
@@ -86,13 +109,19 @@ export async function loadImportState(workingDir: string): Promise<StateFile> {
|
|
|
86
109
|
/**
|
|
87
110
|
* Saves the import state to the state file
|
|
88
111
|
*/
|
|
89
|
-
async function saveImportState(
|
|
112
|
+
async function saveImportState(
|
|
113
|
+
workingDir: string,
|
|
114
|
+
state: StateFile,
|
|
115
|
+
logger?: Logger,
|
|
116
|
+
): Promise<void> {
|
|
90
117
|
const statePath = join(workingDir, STATE_FILENAME);
|
|
91
|
-
|
|
118
|
+
|
|
92
119
|
try {
|
|
93
|
-
await fs.writeFile(statePath, JSON.stringify(state, null, 2),
|
|
120
|
+
await fs.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
94
121
|
} catch (error) {
|
|
95
|
-
|
|
122
|
+
const msg = `Failed to save import state to ${statePath}: ${error}`;
|
|
123
|
+
// eslint-disable-next-line no-console -- fallback when no logger provided
|
|
124
|
+
logger ? logger.warn(msg) : console.warn(msg);
|
|
96
125
|
}
|
|
97
126
|
}
|
|
98
127
|
|
|
@@ -100,9 +129,9 @@ async function saveImportState(workingDir: string, state: StateFile): Promise<vo
|
|
|
100
129
|
* Gets the latest commit information for a repository path
|
|
101
130
|
*/
|
|
102
131
|
export async function getLatestCommitInfo(
|
|
103
|
-
octokit:
|
|
132
|
+
octokit: Octokit,
|
|
104
133
|
config: ImportOptions,
|
|
105
|
-
signal?: AbortSignal
|
|
134
|
+
signal?: AbortSignal,
|
|
106
135
|
): Promise<{ sha: string; message: string; date: string } | null> {
|
|
107
136
|
const { owner, repo, ref = "main" } = config;
|
|
108
137
|
|
|
@@ -113,7 +142,7 @@ export async function getLatestCommitInfo(
|
|
|
113
142
|
repo,
|
|
114
143
|
sha: ref,
|
|
115
144
|
per_page: 1,
|
|
116
|
-
request: { signal }
|
|
145
|
+
request: { signal },
|
|
117
146
|
});
|
|
118
147
|
|
|
119
148
|
if (data.length === 0) {
|
|
@@ -123,11 +152,19 @@ export async function getLatestCommitInfo(
|
|
|
123
152
|
const latestCommit = data[0];
|
|
124
153
|
return {
|
|
125
154
|
sha: latestCommit.sha,
|
|
126
|
-
message: latestCommit.commit.message.split(
|
|
127
|
-
date:
|
|
155
|
+
message: latestCommit.commit.message.split("\n")[0], // First line only
|
|
156
|
+
date:
|
|
157
|
+
latestCommit.commit.committer?.date ||
|
|
158
|
+
latestCommit.commit.author?.date ||
|
|
159
|
+
new Date().toISOString(),
|
|
128
160
|
};
|
|
129
|
-
} catch (error:
|
|
130
|
-
if (
|
|
161
|
+
} catch (error: unknown) {
|
|
162
|
+
if (
|
|
163
|
+
typeof error === "object" &&
|
|
164
|
+
error !== null &&
|
|
165
|
+
"status" in error &&
|
|
166
|
+
(error as { status: number }).status === 404
|
|
167
|
+
) {
|
|
131
168
|
throw new Error(`Repository not found: ${owner}/${repo}`);
|
|
132
169
|
}
|
|
133
170
|
throw error;
|
|
@@ -138,26 +175,28 @@ export async function getLatestCommitInfo(
|
|
|
138
175
|
* Checks a single repository for changes
|
|
139
176
|
*/
|
|
140
177
|
async function checkRepositoryForChanges(
|
|
141
|
-
octokit:
|
|
178
|
+
octokit: Octokit,
|
|
142
179
|
config: ImportOptions,
|
|
143
180
|
currentState: ImportState,
|
|
144
|
-
signal?: AbortSignal
|
|
181
|
+
signal?: AbortSignal,
|
|
145
182
|
): Promise<RepositoryChangeInfo> {
|
|
146
183
|
const configName = config.name || `${config.owner}/${config.repo}`;
|
|
147
184
|
|
|
148
185
|
try {
|
|
149
186
|
const latestCommit = await getLatestCommitInfo(octokit, config, signal);
|
|
150
|
-
|
|
187
|
+
|
|
151
188
|
if (!latestCommit) {
|
|
152
189
|
return {
|
|
153
190
|
config,
|
|
154
191
|
state: currentState,
|
|
155
192
|
needsReimport: false,
|
|
156
|
-
error: "No commits found in repository"
|
|
193
|
+
error: "No commits found in repository",
|
|
157
194
|
};
|
|
158
195
|
}
|
|
159
196
|
|
|
160
|
-
const needsReimport =
|
|
197
|
+
const needsReimport =
|
|
198
|
+
!currentState.lastCommitSha ||
|
|
199
|
+
currentState.lastCommitSha !== latestCommit.sha;
|
|
161
200
|
|
|
162
201
|
return {
|
|
163
202
|
config,
|
|
@@ -165,15 +204,14 @@ async function checkRepositoryForChanges(
|
|
|
165
204
|
needsReimport,
|
|
166
205
|
latestCommitSha: latestCommit.sha,
|
|
167
206
|
latestCommitMessage: latestCommit.message,
|
|
168
|
-
latestCommitDate: latestCommit.date
|
|
207
|
+
latestCommitDate: latestCommit.date,
|
|
169
208
|
};
|
|
170
|
-
|
|
171
|
-
} catch (error: any) {
|
|
209
|
+
} catch (error: unknown) {
|
|
172
210
|
return {
|
|
173
211
|
config,
|
|
174
212
|
state: currentState,
|
|
175
213
|
needsReimport: false,
|
|
176
|
-
error: error.message
|
|
214
|
+
error: error instanceof Error ? error.message : String(error),
|
|
177
215
|
};
|
|
178
216
|
}
|
|
179
217
|
}
|
|
@@ -184,9 +222,10 @@ async function checkRepositoryForChanges(
|
|
|
184
222
|
export async function updateImportState(
|
|
185
223
|
workingDir: string,
|
|
186
224
|
config: ImportOptions,
|
|
187
|
-
commitSha?: string
|
|
225
|
+
commitSha?: string,
|
|
226
|
+
logger?: Logger,
|
|
188
227
|
): Promise<void> {
|
|
189
|
-
const state = await loadImportState(workingDir);
|
|
228
|
+
const state = await loadImportState(workingDir, logger);
|
|
190
229
|
const configId = createConfigId(config);
|
|
191
230
|
const configName = config.name || `${config.owner}/${config.repo}`;
|
|
192
231
|
|
|
@@ -195,10 +234,10 @@ export async function updateImportState(
|
|
|
195
234
|
repoId: configId,
|
|
196
235
|
lastCommitSha: commitSha,
|
|
197
236
|
lastImported: new Date().toISOString(),
|
|
198
|
-
ref: config.ref ||
|
|
237
|
+
ref: config.ref || "main",
|
|
199
238
|
};
|
|
200
239
|
|
|
201
|
-
await saveImportState(workingDir, state);
|
|
240
|
+
await saveImportState(workingDir, state, logger);
|
|
202
241
|
}
|
|
203
242
|
|
|
204
243
|
/**
|
|
@@ -207,14 +246,14 @@ export async function updateImportState(
|
|
|
207
246
|
export async function performDryRun(
|
|
208
247
|
configs: ImportOptions[],
|
|
209
248
|
context: LoaderContext,
|
|
210
|
-
octokit:
|
|
249
|
+
octokit: Octokit,
|
|
211
250
|
workingDir: string = process.cwd(),
|
|
212
|
-
signal?: AbortSignal
|
|
251
|
+
signal?: AbortSignal,
|
|
213
252
|
): Promise<RepositoryChangeInfo[]> {
|
|
214
253
|
const { logger } = context;
|
|
215
|
-
|
|
254
|
+
|
|
216
255
|
logger.info("š Performing dry run - checking for repository changes...");
|
|
217
|
-
|
|
256
|
+
|
|
218
257
|
// Load current state
|
|
219
258
|
const state = await loadImportState(workingDir);
|
|
220
259
|
const results: RepositoryChangeInfo[] = [];
|
|
@@ -222,33 +261,40 @@ export async function performDryRun(
|
|
|
222
261
|
// Check each configuration
|
|
223
262
|
for (const config of configs) {
|
|
224
263
|
if (config.enabled === false) {
|
|
225
|
-
logger.debug(
|
|
264
|
+
logger.debug(
|
|
265
|
+
`Skipping disabled config: ${config.name || `${config.owner}/${config.repo}`}`,
|
|
266
|
+
);
|
|
226
267
|
continue;
|
|
227
268
|
}
|
|
228
269
|
|
|
229
270
|
const configId = createConfigId(config);
|
|
230
271
|
const configName = config.name || `${config.owner}/${config.repo}`;
|
|
231
|
-
|
|
272
|
+
|
|
232
273
|
// Get current state for this config
|
|
233
274
|
const currentState: ImportState = state.imports[configId] || {
|
|
234
275
|
name: configName,
|
|
235
276
|
repoId: configId,
|
|
236
|
-
ref: config.ref ||
|
|
277
|
+
ref: config.ref || "main",
|
|
237
278
|
};
|
|
238
279
|
|
|
239
280
|
logger.debug(`Checking ${configName}...`);
|
|
240
|
-
|
|
281
|
+
|
|
241
282
|
try {
|
|
242
|
-
const changeInfo = await checkRepositoryForChanges(
|
|
283
|
+
const changeInfo = await checkRepositoryForChanges(
|
|
284
|
+
octokit,
|
|
285
|
+
config,
|
|
286
|
+
currentState,
|
|
287
|
+
signal,
|
|
288
|
+
);
|
|
243
289
|
results.push(changeInfo);
|
|
244
|
-
} catch (error:
|
|
290
|
+
} catch (error: unknown) {
|
|
245
291
|
if (signal?.aborted) throw error;
|
|
246
|
-
|
|
292
|
+
|
|
247
293
|
results.push({
|
|
248
294
|
config,
|
|
249
295
|
state: currentState,
|
|
250
296
|
needsReimport: false,
|
|
251
|
-
error: `Failed to check repository: ${error.message}
|
|
297
|
+
error: `Failed to check repository: ${error instanceof Error ? error.message : String(error)}`,
|
|
252
298
|
});
|
|
253
299
|
}
|
|
254
300
|
}
|
|
@@ -263,16 +309,20 @@ export async function performDryRun(
|
|
|
263
309
|
/**
|
|
264
310
|
* Formats and displays the dry run results
|
|
265
311
|
*/
|
|
266
|
-
export function displayDryRunResults(
|
|
312
|
+
export function displayDryRunResults(
|
|
313
|
+
results: RepositoryChangeInfo[],
|
|
314
|
+
logger: { info: (msg: string) => void },
|
|
315
|
+
): void {
|
|
267
316
|
logger.info("\nš Repository Import Status:");
|
|
268
|
-
logger.info("="
|
|
317
|
+
logger.info("=".repeat(50));
|
|
269
318
|
|
|
270
319
|
let needsReimportCount = 0;
|
|
271
320
|
let errorCount = 0;
|
|
272
321
|
|
|
273
322
|
for (const result of results) {
|
|
274
|
-
const configName =
|
|
275
|
-
|
|
323
|
+
const configName =
|
|
324
|
+
result.config.name || `${result.config.owner}/${result.config.repo}`;
|
|
325
|
+
|
|
276
326
|
if (result.error) {
|
|
277
327
|
logger.info(`ā ${configName}: ${result.error}`);
|
|
278
328
|
errorCount++;
|
|
@@ -304,12 +354,16 @@ export function displayDryRunResults(results: RepositoryChangeInfo[], logger: an
|
|
|
304
354
|
}
|
|
305
355
|
}
|
|
306
356
|
|
|
307
|
-
logger.info("="
|
|
308
|
-
logger.info(
|
|
309
|
-
|
|
357
|
+
logger.info("=".repeat(50));
|
|
358
|
+
logger.info(
|
|
359
|
+
`š Summary: ${needsReimportCount} of ${results.length} repositories need re-import, ${errorCount} errors`,
|
|
360
|
+
);
|
|
361
|
+
|
|
310
362
|
if (needsReimportCount > 0) {
|
|
311
363
|
logger.info("\nš” To import updated repositories:");
|
|
312
|
-
logger.info(
|
|
364
|
+
logger.info(
|
|
365
|
+
"1. Delete the target import folders for repositories that need re-import",
|
|
366
|
+
);
|
|
313
367
|
logger.info("2. Run the import process normally (dryRun: false)");
|
|
314
368
|
logger.info("3. Fresh content will be imported automatically");
|
|
315
369
|
} else {
|
|
@@ -336,4 +390,4 @@ function getTimeAgo(date: Date): string {
|
|
|
336
390
|
} else {
|
|
337
391
|
return date.toLocaleDateString();
|
|
338
392
|
}
|
|
339
|
-
}
|
|
393
|
+
}
|