@asagiri-design/labels-config 0.2.2
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/README.ja.md +387 -0
- package/README.md +387 -0
- package/dist/chunk-4ZUJQMV7.mjs +285 -0
- package/dist/chunk-DGUMSQAI.mjs +496 -0
- package/dist/chunk-DSI7SDAM.mjs +161 -0
- package/dist/chunk-QJLMZSVA.mjs +496 -0
- package/dist/chunk-QZ7TP4HQ.mjs +7 -0
- package/dist/chunk-VU2JB66N.mjs +103 -0
- package/dist/chunk-ZYHIDOG2.mjs +247 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1417 -0
- package/dist/cli.mjs +436 -0
- package/dist/config/index.d.mts +49 -0
- package/dist/config/index.d.ts +49 -0
- package/dist/config/index.js +554 -0
- package/dist/config/index.mjs +10 -0
- package/dist/github/index.d.mts +113 -0
- package/dist/github/index.d.ts +113 -0
- package/dist/github/index.js +310 -0
- package/dist/github/index.mjs +9 -0
- package/dist/index.d.mts +309 -0
- package/dist/index.d.ts +309 -0
- package/dist/index.js +306 -0
- package/dist/index.mjs +44 -0
- package/dist/types-CkwsO1Iu.d.mts +50 -0
- package/dist/types-CkwsO1Iu.d.ts +50 -0
- package/docs/API.md +309 -0
- package/docs/GETTING_STARTED.md +305 -0
- package/package.json +87 -0
- package/templates/prod-labels.json +106 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { L as LabelConfig } from '../types-CkwsO1Iu.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GitHub CLI Client
|
|
5
|
+
* Wrapper around gh CLI for label operations
|
|
6
|
+
* No token required - uses gh CLI authentication
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface GitHubClientOptions {
|
|
10
|
+
/** Repository owner */
|
|
11
|
+
owner: string;
|
|
12
|
+
/** Repository name */
|
|
13
|
+
repo: string;
|
|
14
|
+
}
|
|
15
|
+
interface GitHubLabel {
|
|
16
|
+
/** Label name */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Hex color (without #) */
|
|
19
|
+
color: string;
|
|
20
|
+
/** Label description */
|
|
21
|
+
description?: string;
|
|
22
|
+
}
|
|
23
|
+
declare class GitHubClient {
|
|
24
|
+
private owner;
|
|
25
|
+
private repo;
|
|
26
|
+
private repoPath;
|
|
27
|
+
constructor(options: GitHubClientOptions);
|
|
28
|
+
/**
|
|
29
|
+
* Execute gh CLI command
|
|
30
|
+
*/
|
|
31
|
+
private exec;
|
|
32
|
+
/**
|
|
33
|
+
* Fetch all labels from repository
|
|
34
|
+
*/
|
|
35
|
+
fetchLabels(): Promise<GitHubLabel[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Create a new label
|
|
38
|
+
*/
|
|
39
|
+
createLabel(label: LabelConfig): Promise<GitHubLabel>;
|
|
40
|
+
/**
|
|
41
|
+
* Update an existing label
|
|
42
|
+
*/
|
|
43
|
+
updateLabel(currentName: string, label: Partial<LabelConfig>): Promise<GitHubLabel>;
|
|
44
|
+
/**
|
|
45
|
+
* Delete a label
|
|
46
|
+
*/
|
|
47
|
+
deleteLabel(name: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if label exists
|
|
50
|
+
*/
|
|
51
|
+
hasLabel(name: string): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* GitHub Label Synchronization
|
|
56
|
+
* Syncs local labels to GitHub repositories
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
interface GitHubSyncOptions extends GitHubClientOptions {
|
|
60
|
+
/** Dry run mode - don't make actual changes */
|
|
61
|
+
dryRun?: boolean;
|
|
62
|
+
/** Delete labels on GitHub that don't exist locally */
|
|
63
|
+
deleteExtra?: boolean;
|
|
64
|
+
/** Verbose logging */
|
|
65
|
+
verbose?: boolean;
|
|
66
|
+
}
|
|
67
|
+
interface SyncResult {
|
|
68
|
+
/** Labels created */
|
|
69
|
+
created: LabelConfig[];
|
|
70
|
+
/** Labels updated */
|
|
71
|
+
updated: LabelConfig[];
|
|
72
|
+
/** Labels deleted */
|
|
73
|
+
deleted: string[];
|
|
74
|
+
/** Labels unchanged */
|
|
75
|
+
unchanged: LabelConfig[];
|
|
76
|
+
/** Errors during sync */
|
|
77
|
+
errors: Array<{
|
|
78
|
+
name: string;
|
|
79
|
+
error: string;
|
|
80
|
+
}>;
|
|
81
|
+
}
|
|
82
|
+
declare class GitHubLabelSync {
|
|
83
|
+
private static readonly BATCH_SIZE;
|
|
84
|
+
private client;
|
|
85
|
+
private options;
|
|
86
|
+
constructor(options: GitHubSyncOptions);
|
|
87
|
+
/**
|
|
88
|
+
* Log message if verbose mode is enabled
|
|
89
|
+
*/
|
|
90
|
+
private log;
|
|
91
|
+
/**
|
|
92
|
+
* Sync labels to GitHub repository with batch operations for better performance
|
|
93
|
+
*/
|
|
94
|
+
syncLabels(localLabels: LabelConfig[]): Promise<SyncResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Check if label has changes
|
|
97
|
+
*/
|
|
98
|
+
private hasChanges;
|
|
99
|
+
/**
|
|
100
|
+
* Fetch labels from GitHub
|
|
101
|
+
*/
|
|
102
|
+
fetchLabels(): Promise<LabelConfig[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Delete a single label
|
|
105
|
+
*/
|
|
106
|
+
deleteLabel(name: string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Update a single label
|
|
109
|
+
*/
|
|
110
|
+
updateLabel(name: string, updates: Partial<LabelConfig>): Promise<void>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export { GitHubClient, type GitHubClientOptions, type GitHubLabel, GitHubLabelSync, type GitHubSyncOptions };
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
|
|
22
|
+
// src/github/index.ts
|
|
23
|
+
var github_exports = {};
|
|
24
|
+
__export(github_exports, {
|
|
25
|
+
GitHubClient: () => GitHubClient,
|
|
26
|
+
GitHubLabelSync: () => GitHubLabelSync
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(github_exports);
|
|
29
|
+
|
|
30
|
+
// src/github/client.ts
|
|
31
|
+
var import_child_process = require("child_process");
|
|
32
|
+
var GitHubClient = class {
|
|
33
|
+
constructor(options) {
|
|
34
|
+
__publicField(this, "owner");
|
|
35
|
+
__publicField(this, "repo");
|
|
36
|
+
__publicField(this, "repoPath");
|
|
37
|
+
this.owner = options.owner;
|
|
38
|
+
this.repo = options.repo;
|
|
39
|
+
this.repoPath = `${this.owner}/${this.repo}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Execute gh CLI command
|
|
43
|
+
*/
|
|
44
|
+
exec(command) {
|
|
45
|
+
try {
|
|
46
|
+
return (0, import_child_process.execSync)(command, {
|
|
47
|
+
encoding: "utf-8",
|
|
48
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
49
|
+
}).trim();
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(`gh CLI command failed: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fetch all labels from repository
|
|
56
|
+
*/
|
|
57
|
+
async fetchLabels() {
|
|
58
|
+
try {
|
|
59
|
+
const output = this.exec(
|
|
60
|
+
`gh label list --repo ${this.repoPath} --json name,color,description --limit 1000`
|
|
61
|
+
);
|
|
62
|
+
if (!output) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const labels = JSON.parse(output);
|
|
66
|
+
return labels.map((label) => ({
|
|
67
|
+
name: label.name,
|
|
68
|
+
color: label.color,
|
|
69
|
+
description: label.description || ""
|
|
70
|
+
}));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error(`Failed to fetch labels from ${this.repoPath}: ${error}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a new label
|
|
77
|
+
*/
|
|
78
|
+
async createLabel(label) {
|
|
79
|
+
try {
|
|
80
|
+
const name = label.name.replace(/"/g, '\\"');
|
|
81
|
+
const description = label.description.replace(/"/g, '\\"');
|
|
82
|
+
const color = label.color.replace("#", "");
|
|
83
|
+
this.exec(
|
|
84
|
+
`gh label create "${name}" --color "${color}" --description "${description}" --repo ${this.repoPath}`
|
|
85
|
+
);
|
|
86
|
+
return {
|
|
87
|
+
name: label.name,
|
|
88
|
+
color: label.color,
|
|
89
|
+
description: label.description
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(`Failed to create label "${label.name}": ${error}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Update an existing label
|
|
97
|
+
*/
|
|
98
|
+
async updateLabel(currentName, label) {
|
|
99
|
+
try {
|
|
100
|
+
const escapedCurrentName = currentName.replace(/"/g, '\\"');
|
|
101
|
+
const args = [];
|
|
102
|
+
if (label.name && label.name !== currentName) {
|
|
103
|
+
args.push(`--name "${label.name.replace(/"/g, '\\"')}"`);
|
|
104
|
+
}
|
|
105
|
+
if (label.color) {
|
|
106
|
+
args.push(`--color "${label.color.replace("#", "")}"`);
|
|
107
|
+
}
|
|
108
|
+
if (label.description !== void 0) {
|
|
109
|
+
args.push(`--description "${label.description.replace(/"/g, '\\"')}"`);
|
|
110
|
+
}
|
|
111
|
+
if (args.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
name: currentName,
|
|
114
|
+
color: label.color || "",
|
|
115
|
+
description: label.description || ""
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
this.exec(
|
|
119
|
+
`gh label edit "${escapedCurrentName}" ${args.join(" ")} --repo ${this.repoPath}`
|
|
120
|
+
);
|
|
121
|
+
return {
|
|
122
|
+
name: label.name || currentName,
|
|
123
|
+
color: label.color || "",
|
|
124
|
+
description: label.description || ""
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Error(`Failed to update label "${currentName}": ${error}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Delete a label
|
|
132
|
+
*/
|
|
133
|
+
async deleteLabel(name) {
|
|
134
|
+
try {
|
|
135
|
+
const escapedName = name.replace(/"/g, '\\"');
|
|
136
|
+
this.exec(`gh label delete "${escapedName}" --repo ${this.repoPath} --yes`);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw new Error(`Failed to delete label "${name}": ${error}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if label exists
|
|
143
|
+
*/
|
|
144
|
+
async hasLabel(name) {
|
|
145
|
+
try {
|
|
146
|
+
const labels = await this.fetchLabels();
|
|
147
|
+
return labels.some((label) => label.name.toLowerCase() === name.toLowerCase());
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/github/sync.ts
|
|
155
|
+
var _GitHubLabelSync = class _GitHubLabelSync {
|
|
156
|
+
constructor(options) {
|
|
157
|
+
__publicField(this, "client");
|
|
158
|
+
__publicField(this, "options");
|
|
159
|
+
this.client = new GitHubClient(options);
|
|
160
|
+
this.options = options;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Log message if verbose mode is enabled
|
|
164
|
+
*/
|
|
165
|
+
log(message) {
|
|
166
|
+
if (this.options.verbose) {
|
|
167
|
+
console.log(`[labels-config] ${message}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Sync labels to GitHub repository with batch operations for better performance
|
|
172
|
+
*/
|
|
173
|
+
async syncLabels(localLabels) {
|
|
174
|
+
this.log("Fetching remote labels...");
|
|
175
|
+
const remoteLabels = await this.client.fetchLabels();
|
|
176
|
+
const result = {
|
|
177
|
+
created: [],
|
|
178
|
+
updated: [],
|
|
179
|
+
deleted: [],
|
|
180
|
+
unchanged: [],
|
|
181
|
+
errors: []
|
|
182
|
+
};
|
|
183
|
+
const remoteLabelMap = new Map(remoteLabels.map((label) => [label.name.toLowerCase(), label]));
|
|
184
|
+
const localLabelMap = new Map(localLabels.map((label) => [label.name.toLowerCase(), label]));
|
|
185
|
+
const toCreate = [];
|
|
186
|
+
const toUpdate = [];
|
|
187
|
+
const toDelete = [];
|
|
188
|
+
for (const label of localLabels) {
|
|
189
|
+
const remoteLabel = remoteLabelMap.get(label.name.toLowerCase());
|
|
190
|
+
if (!remoteLabel) {
|
|
191
|
+
toCreate.push(label);
|
|
192
|
+
} else if (this.hasChanges(label, remoteLabel)) {
|
|
193
|
+
toUpdate.push({ current: remoteLabel.name, updated: label });
|
|
194
|
+
} else {
|
|
195
|
+
result.unchanged.push(label);
|
|
196
|
+
this.log(`Unchanged label: ${label.name}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (this.options.deleteExtra) {
|
|
200
|
+
for (const remoteLabel of remoteLabels) {
|
|
201
|
+
if (!localLabelMap.has(remoteLabel.name.toLowerCase())) {
|
|
202
|
+
toDelete.push(remoteLabel.name);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!this.options.dryRun) {
|
|
207
|
+
for (let i = 0; i < toCreate.length; i += _GitHubLabelSync.BATCH_SIZE) {
|
|
208
|
+
const batch = toCreate.slice(i, i + _GitHubLabelSync.BATCH_SIZE);
|
|
209
|
+
const promises = batch.map(async (label) => {
|
|
210
|
+
try {
|
|
211
|
+
await this.client.createLabel(label);
|
|
212
|
+
result.created.push(label);
|
|
213
|
+
this.log(`Created label: ${label.name}`);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
result.errors.push({
|
|
216
|
+
name: label.name,
|
|
217
|
+
error: error instanceof Error ? error.message : String(error)
|
|
218
|
+
});
|
|
219
|
+
this.log(`Error creating label "${label.name}": ${error}`);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
await Promise.all(promises);
|
|
223
|
+
}
|
|
224
|
+
for (let i = 0; i < toUpdate.length; i += _GitHubLabelSync.BATCH_SIZE) {
|
|
225
|
+
const batch = toUpdate.slice(i, i + _GitHubLabelSync.BATCH_SIZE);
|
|
226
|
+
const promises = batch.map(async ({ current, updated }) => {
|
|
227
|
+
try {
|
|
228
|
+
await this.client.updateLabel(current, updated);
|
|
229
|
+
result.updated.push(updated);
|
|
230
|
+
this.log(`Updated label: ${updated.name}`);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
result.errors.push({
|
|
233
|
+
name: updated.name,
|
|
234
|
+
error: error instanceof Error ? error.message : String(error)
|
|
235
|
+
});
|
|
236
|
+
this.log(`Error updating label "${updated.name}": ${error}`);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
await Promise.all(promises);
|
|
240
|
+
}
|
|
241
|
+
for (let i = 0; i < toDelete.length; i += _GitHubLabelSync.BATCH_SIZE) {
|
|
242
|
+
const batch = toDelete.slice(i, i + _GitHubLabelSync.BATCH_SIZE);
|
|
243
|
+
const promises = batch.map(async (name) => {
|
|
244
|
+
try {
|
|
245
|
+
await this.client.deleteLabel(name);
|
|
246
|
+
result.deleted.push(name);
|
|
247
|
+
this.log(`Deleted label: ${name}`);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
result.errors.push({
|
|
250
|
+
name,
|
|
251
|
+
error: error instanceof Error ? error.message : String(error)
|
|
252
|
+
});
|
|
253
|
+
this.log(`Error deleting label "${name}": ${error}`);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
await Promise.all(promises);
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
result.created.push(...toCreate);
|
|
260
|
+
result.updated.push(...toUpdate.map((op) => op.updated));
|
|
261
|
+
result.deleted.push(...toDelete);
|
|
262
|
+
toCreate.forEach((label) => this.log(`[DRY RUN] Would create label: ${label.name}`));
|
|
263
|
+
toUpdate.forEach(({ updated }) => this.log(`[DRY RUN] Would update label: ${updated.name}`));
|
|
264
|
+
toDelete.forEach((name) => this.log(`[DRY RUN] Would delete label: ${name}`));
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Check if label has changes
|
|
270
|
+
*/
|
|
271
|
+
hasChanges(local, remote) {
|
|
272
|
+
return local.color.toLowerCase() !== remote.color.toLowerCase() || local.description !== (remote.description || "");
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Fetch labels from GitHub
|
|
276
|
+
*/
|
|
277
|
+
async fetchLabels() {
|
|
278
|
+
const labels = await this.client.fetchLabels();
|
|
279
|
+
return labels.map((label) => ({
|
|
280
|
+
name: label.name,
|
|
281
|
+
color: label.color,
|
|
282
|
+
description: label.description || ""
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Delete a single label
|
|
287
|
+
*/
|
|
288
|
+
async deleteLabel(name) {
|
|
289
|
+
if (!this.options.dryRun) {
|
|
290
|
+
await this.client.deleteLabel(name);
|
|
291
|
+
}
|
|
292
|
+
this.log(`Deleted label: ${name}`);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Update a single label
|
|
296
|
+
*/
|
|
297
|
+
async updateLabel(name, updates) {
|
|
298
|
+
if (!this.options.dryRun) {
|
|
299
|
+
await this.client.updateLabel(name, updates);
|
|
300
|
+
}
|
|
301
|
+
this.log(`Updated label: ${name}`);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
__publicField(_GitHubLabelSync, "BATCH_SIZE", 5);
|
|
305
|
+
var GitHubLabelSync = _GitHubLabelSync;
|
|
306
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
307
|
+
0 && (module.exports = {
|
|
308
|
+
GitHubClient,
|
|
309
|
+
GitHubLabelSync
|
|
310
|
+
});
|