@theholocron/holocron-plugin-github 5.0.0-alpha.377 → 5.0.0-alpha.379
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/dist/index.mjs +38 -1
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -152,6 +152,18 @@ var GitHubEnvironments = class {
|
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
154
|
// src/capabilities/issues-migration.ts
|
|
155
|
+
/**
|
|
156
|
+
* Export and import of GitHub issues for `holocron migrate`.
|
|
157
|
+
*
|
|
158
|
+
* GitHub holds less than a planning tool, so what does not fit is named in each result's `dropped` list rather than
|
|
159
|
+
* lost quietly:
|
|
160
|
+
* - priority is a label (`priority: high`), because on GitHub it lives in a project field, not on the issue;
|
|
161
|
+
* - an import cannot backdate an issue or a comment, and cannot post as someone else, so the original date and
|
|
162
|
+
* author go in the text;
|
|
163
|
+
* - an import does not set parent or blocked-by links (they need GraphQL node ids of issues made in the same run);
|
|
164
|
+
* - an assignee is not mapped (a Linear user is not a GitHub login).
|
|
165
|
+
* A migrated issue ends with `<!-- holocron:migrated-from <url> -->`, which is how a second run finds it.
|
|
166
|
+
*/
|
|
155
167
|
const MARKER = /<!-- holocron:migrated-from (\S+) -->/;
|
|
156
168
|
const PRIORITY_LABEL = /^priority\s*[:/-]\s*(urgent|high|medium|low)$/i;
|
|
157
169
|
const LINK_QUERY = `query($owner: String!, $name: String!, $number: Int!) { repository(owner: $owner, name: $name) {
|
|
@@ -177,11 +189,36 @@ function categoryOf(issue) {
|
|
|
177
189
|
if (names.some((n) => n === "status:in-progress" || n === "status: in progress")) return "in-progress";
|
|
178
190
|
return "open";
|
|
179
191
|
}
|
|
192
|
+
/** How many issues an export fetches at once. GitHub's secondary rate limit punishes bursts of requests, not volume. */
|
|
193
|
+
const EXPORT_CONCURRENCY = 4;
|
|
194
|
+
/** Runs `fn` over `items`, at most `limit` at a time, and returns the results in the order of `items`. */
|
|
195
|
+
async function mapLimit(items, limit, fn) {
|
|
196
|
+
const results = new Array(items.length);
|
|
197
|
+
let next = 0;
|
|
198
|
+
const worker = async () => {
|
|
199
|
+
while (next < items.length) {
|
|
200
|
+
const at = next++;
|
|
201
|
+
results[at] = await fn(items[at]);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
205
|
+
return results;
|
|
206
|
+
}
|
|
207
|
+
/** GitHub's own answer to a burst of requests (403 or 429 naming the secondary limit) becomes a message that says what to do. */
|
|
208
|
+
function explainSecondaryLimit(err) {
|
|
209
|
+
if (err instanceof ProviderApiError && (err.status === 403 || err.status === 429) && String(err.details).includes("secondary rate limit")) return /* @__PURE__ */ new Error("GitHub's secondary rate limit stopped the export. It limits bursts of requests, not the hourly quota, and nothing has been written. Wait a couple of minutes and run the command again.");
|
|
210
|
+
return err;
|
|
211
|
+
}
|
|
180
212
|
/** Reads the issues `selector` names, with their comments, priority label, parent and blockers. */
|
|
181
213
|
async function exportIssues(client, repo, selector) {
|
|
214
|
+
return readIssues(client, repo, selector).catch((err) => {
|
|
215
|
+
throw explainSecondaryLimit(err);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function readIssues(client, repo, selector) {
|
|
182
219
|
const [owner = "", name = ""] = repo.split("/", 2);
|
|
183
220
|
let raw;
|
|
184
|
-
if (selector.keys) raw = await
|
|
221
|
+
if (selector.keys) raw = await mapLimit(selector.keys, EXPORT_CONCURRENCY, (k) => client.issues.getIssue(repo, parseNumber(k)));
|
|
185
222
|
else if (selector.milestone) {
|
|
186
223
|
const milestone = await findMilestone(client, repo, selector.milestone);
|
|
187
224
|
if (!milestone) throw new Error(`Milestone "${selector.milestone}" not found in ${repo}.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-github",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.379",
|
|
4
4
|
"description": "Holocron plugin for GitHub. Implements source, ci, secrets, environments, and issues capabilities against the GitHub REST API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"tsx": "4.23.15",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"vitest": "^5.1.0",
|
|
47
|
-
"@theholocron/cli": "5.0.0-alpha.
|
|
47
|
+
"@theholocron/cli": "5.0.0-alpha.379",
|
|
48
|
+
"@theholocron/eslint-config": "^8.9.0-alpha.12",
|
|
48
49
|
"@theholocron/fs-utils": "^1.17.0-alpha.2",
|
|
49
50
|
"@theholocron/http-client": "^1.58.0-alpha.22",
|
|
50
|
-
"@theholocron/github-client": "^1.58.0-alpha.22",
|
|
51
|
-
"@theholocron/misc-utils": "^1.17.0-alpha.2",
|
|
52
|
-
"@theholocron/eslint-config": "^8.9.0-alpha.12",
|
|
53
51
|
"@theholocron/tsconfig": "^8.9.0-alpha.12",
|
|
52
|
+
"@theholocron/misc-utils": "^1.17.0-alpha.2",
|
|
53
|
+
"@theholocron/github-client": "^1.58.0-alpha.22",
|
|
54
54
|
"@theholocron/tsdown-config": "^8.9.0-alpha.12",
|
|
55
55
|
"@theholocron/vitest-config": "^8.9.0-alpha.12"
|
|
56
56
|
},
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@theholocron/fs-utils": "^1.16.0",
|
|
59
59
|
"@theholocron/github-client": "^1.58.0",
|
|
60
60
|
"@theholocron/misc-utils": "^1.16.0",
|
|
61
|
-
"@theholocron/cli": "5.0.0-alpha.
|
|
61
|
+
"@theholocron/cli": "5.0.0-alpha.379"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=22"
|