hyouji 0.0.15 → 0.0.16
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 +25 -15
- package/dist/index.js +211 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,6 +94,21 @@ These credentials will be securely saved and reused for future sessions.
|
|
|
94
94
|
8. **Display your settings** - View your saved configuration
|
|
95
95
|
9. **Exit**
|
|
96
96
|
|
|
97
|
+
When you choose a create/delete/import action, you’ll be asked if you want to run in **dry-run mode**. Selecting “yes” shows what would happen without any API calls. Example:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
=== Create preset labels summary ===
|
|
101
|
+
Mode: dry run (no API calls executed)
|
|
102
|
+
Created: 0 Failed: 0 Deleted: 0 Skipped: 24
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Quick dry-run example (generate sample JSON then import it without touching GitHub):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
hyouji # choose “Generate sample JSON”
|
|
109
|
+
hyouji # choose “Import labels from JSON or YAML” -> pick hyouji.json -> select dry-run = yes
|
|
110
|
+
```
|
|
111
|
+
|
|
97
112
|
### Settings Management
|
|
98
113
|
|
|
99
114
|
The tool now includes persistent configuration storage with enhanced security:
|
|
@@ -105,6 +120,12 @@ The tool now includes persistent configuration storage with enhanced security:
|
|
|
105
120
|
- **Automatic migration**: Existing plain text configurations are automatically upgraded to encrypted format
|
|
106
121
|
- **Token security**: Your personal token is never displayed in plain text, only an obfuscated preview is shown
|
|
107
122
|
|
|
123
|
+
### Dry-run Mode & Progress
|
|
124
|
+
|
|
125
|
+
- Select **yes** when prompted for dry-run to avoid any API calls; actions are listed as “Would create/delete…”.
|
|
126
|
+
- Each API call shows short status lines (e.g., “Creating label…”, “Deleted …”).
|
|
127
|
+
- A final summary reports created/deleted/skipped/failed counts and hints for next steps.
|
|
128
|
+
|
|
108
129
|
### Security Features
|
|
109
130
|
|
|
110
131
|
**Token Encryption**:
|
|
@@ -132,22 +153,11 @@ If you want to create/delete a single label, you need to type the followings.
|
|
|
132
153
|
|
|
133
154
|
- label name
|
|
134
155
|
|
|
135
|
-
|
|
136
|
-
If you want to put your own labels, you will need to modify `label.js` file.
|
|
156
|
+
For multiple labels, use the import flow:
|
|
137
157
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
name: "Type: Bug Fix",
|
|
142
|
-
color: "FF8A65",
|
|
143
|
-
description: "Fix features that are not working",
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
name: "Type: Enhancement",
|
|
147
|
-
color: "64B5F7",
|
|
148
|
-
description: "Add new features",
|
|
149
|
-
},
|
|
150
|
-
```
|
|
158
|
+
- Prepare a JSON or YAML file (examples live in `examples/labels.{json,yaml}`).
|
|
159
|
+
- Or generate fresh templates from the menu: **Generate sample JSON/YAML** will create `hyouji.json` or `hyouji.yaml` in your CWD.
|
|
160
|
+
- Run **Import labels** and provide the file path. You can choose **dry-run** first to see what would be created without hitting the API.
|
|
151
161
|
|
|
152
162
|
## Quick Start
|
|
153
163
|
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,14 @@ const labelFilePath = {
|
|
|
56
56
|
name: "filePath",
|
|
57
57
|
message: "Please type the path to your JSON or YAML file"
|
|
58
58
|
};
|
|
59
|
+
const dryRunToggle = {
|
|
60
|
+
type: "toggle",
|
|
61
|
+
name: "dryRun",
|
|
62
|
+
message: "Run in dry-run mode? (no API calls will be made)",
|
|
63
|
+
active: "yes",
|
|
64
|
+
inactive: "no",
|
|
65
|
+
initial: false
|
|
66
|
+
};
|
|
59
67
|
const actionSelector = {
|
|
60
68
|
type: "multiselect",
|
|
61
69
|
name: "action",
|
|
@@ -270,42 +278,67 @@ const extraGuideText = `If you don't see action selector, please hit space key.`
|
|
|
270
278
|
const linkToPersonalToken = "https://github.com/settings/tokens";
|
|
271
279
|
const log$4 = console.log;
|
|
272
280
|
const createLabel = async (configs2, label) => {
|
|
273
|
-
|
|
274
|
-
"
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
+
try {
|
|
282
|
+
log$4(chalk.cyan(`⏳ Creating label "${label.name}"...`));
|
|
283
|
+
const resp = await configs2.octokit.request(
|
|
284
|
+
"POST /repos/{owner}/{repo}/labels",
|
|
285
|
+
{
|
|
286
|
+
owner: configs2.owner,
|
|
287
|
+
repo: configs2.repo,
|
|
288
|
+
name: label.name,
|
|
289
|
+
color: label.color,
|
|
290
|
+
description: label.description
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
const status = resp.status;
|
|
294
|
+
switch (status) {
|
|
295
|
+
case 201:
|
|
296
|
+
log$4(chalk.green(`✓ ${resp.status}: Created ${label.name}`));
|
|
297
|
+
return true;
|
|
298
|
+
case 404:
|
|
299
|
+
log$4(chalk.red(`${resp.status}: Resource not found`));
|
|
300
|
+
return false;
|
|
301
|
+
case 422:
|
|
302
|
+
log$4(chalk.red(`${resp.status}: Validation failed`));
|
|
303
|
+
return false;
|
|
304
|
+
default:
|
|
305
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong`));
|
|
306
|
+
return false;
|
|
281
307
|
}
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
log$4(chalk.red(`${resp.status}: Resource not found`));
|
|
290
|
-
break;
|
|
291
|
-
case 422:
|
|
292
|
-
log$4(chalk.red(`${resp.status}: Validation failed`));
|
|
293
|
-
break;
|
|
294
|
-
default:
|
|
295
|
-
log$4(chalk.yellow(`${resp.status}: Something wrong`));
|
|
296
|
-
break;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
log$4(
|
|
310
|
+
chalk.red(
|
|
311
|
+
`Error creating label "${label.name}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
return false;
|
|
297
315
|
}
|
|
298
316
|
};
|
|
299
317
|
const createLabels = async (configs2) => {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
318
|
+
let created = 0;
|
|
319
|
+
let failed = 0;
|
|
320
|
+
for (const label of labels) {
|
|
321
|
+
const ok = await createLabel(configs2, label);
|
|
322
|
+
if (ok) {
|
|
323
|
+
created++;
|
|
324
|
+
} else {
|
|
325
|
+
failed++;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (failed === 0) {
|
|
329
|
+
log$4(chalk.green("✓ Created all labels successfully"));
|
|
330
|
+
} else {
|
|
331
|
+
log$4(chalk.yellow(`Finished processing labels: ${created} created, ${failed} failed`));
|
|
332
|
+
}
|
|
304
333
|
log$4(chalk.bgBlueBright(extraGuideText));
|
|
334
|
+
return { created, failed };
|
|
305
335
|
};
|
|
306
336
|
const deleteLabel = async (configs2, labelNames) => {
|
|
337
|
+
let deleted = 0;
|
|
338
|
+
let failed = 0;
|
|
307
339
|
for (const labelName of labelNames) {
|
|
308
340
|
try {
|
|
341
|
+
log$4(chalk.cyan(`⏳ Deleting label "${labelName}"...`));
|
|
309
342
|
const resp = await configs2.octokit.request(
|
|
310
343
|
"DELETE /repos/{owner}/{repo}/labels/{name}",
|
|
311
344
|
{
|
|
@@ -315,11 +348,14 @@ const deleteLabel = async (configs2, labelNames) => {
|
|
|
315
348
|
}
|
|
316
349
|
);
|
|
317
350
|
if (resp.status === 204) {
|
|
351
|
+
deleted++;
|
|
318
352
|
log$4(chalk.green(`${resp.status}: Deleted ${labelName}`));
|
|
319
353
|
} else {
|
|
354
|
+
failed++;
|
|
320
355
|
log$4(chalk.yellow(`${resp.status}: Something wrong with ${labelName}`));
|
|
321
356
|
}
|
|
322
357
|
} catch (error) {
|
|
358
|
+
failed++;
|
|
323
359
|
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
324
360
|
log$4(chalk.red(`404: Label "${labelName}" not found`));
|
|
325
361
|
} else {
|
|
@@ -331,6 +367,7 @@ const deleteLabel = async (configs2, labelNames) => {
|
|
|
331
367
|
}
|
|
332
368
|
}
|
|
333
369
|
}
|
|
370
|
+
return { deleted, failed };
|
|
334
371
|
};
|
|
335
372
|
const getLabels = async (configs2) => {
|
|
336
373
|
const resp = await configs2.octokit.request(
|
|
@@ -352,9 +389,11 @@ const deleteLabels = async (configs2) => {
|
|
|
352
389
|
const names = await getLabels(configs2);
|
|
353
390
|
if (names.length === 0) {
|
|
354
391
|
log$4(chalk.yellow("No labels found to delete"));
|
|
355
|
-
return;
|
|
392
|
+
return { deleted: 0, failed: 0 };
|
|
356
393
|
}
|
|
357
394
|
log$4(chalk.blue(`Deleting ${names.length} labels...`));
|
|
395
|
+
let deleted = 0;
|
|
396
|
+
let failed = 0;
|
|
358
397
|
for (const name of names) {
|
|
359
398
|
try {
|
|
360
399
|
const resp = await configs2.octokit.request(
|
|
@@ -366,11 +405,14 @@ const deleteLabels = async (configs2) => {
|
|
|
366
405
|
}
|
|
367
406
|
);
|
|
368
407
|
if (resp.status === 204) {
|
|
408
|
+
deleted++;
|
|
369
409
|
log$4(chalk.green(`${resp.status}: Deleted ${name}`));
|
|
370
410
|
} else {
|
|
411
|
+
failed++;
|
|
371
412
|
log$4(chalk.yellow(`${resp.status}: Something wrong with ${name}`));
|
|
372
413
|
}
|
|
373
414
|
} catch (error) {
|
|
415
|
+
failed++;
|
|
374
416
|
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
375
417
|
log$4(chalk.red(`404: Label "${name}" not found`));
|
|
376
418
|
} else {
|
|
@@ -384,6 +426,7 @@ const deleteLabels = async (configs2) => {
|
|
|
384
426
|
}
|
|
385
427
|
log$4(chalk.blue("Finished deleting labels"));
|
|
386
428
|
log$4(chalk.bgBlueBright(extraGuideText));
|
|
429
|
+
return { deleted, failed };
|
|
387
430
|
};
|
|
388
431
|
const _CryptoUtils = class _CryptoUtils {
|
|
389
432
|
/**
|
|
@@ -977,6 +1020,10 @@ const getConfirmation = async () => {
|
|
|
977
1020
|
const response = await prompts(holdToken);
|
|
978
1021
|
return response.value;
|
|
979
1022
|
};
|
|
1023
|
+
const getDryRunChoice = async () => {
|
|
1024
|
+
const response = await prompts(dryRunToggle);
|
|
1025
|
+
return Boolean(response.dryRun);
|
|
1026
|
+
};
|
|
980
1027
|
const log$3 = console.log;
|
|
981
1028
|
const generateSampleJson = async () => {
|
|
982
1029
|
try {
|
|
@@ -1103,11 +1150,18 @@ const formatSupportedExtensions = () => {
|
|
|
1103
1150
|
return getSupportedExtensions().join(", ");
|
|
1104
1151
|
};
|
|
1105
1152
|
const log$1 = console.log;
|
|
1106
|
-
const importLabelsFromFile = async (configs2, filePath) => {
|
|
1153
|
+
const importLabelsFromFile = async (configs2, filePath, dryRun = false) => {
|
|
1154
|
+
const summary = {
|
|
1155
|
+
attempted: 0,
|
|
1156
|
+
succeeded: 0,
|
|
1157
|
+
failed: 0,
|
|
1158
|
+
skipped: 0
|
|
1159
|
+
};
|
|
1107
1160
|
try {
|
|
1108
1161
|
if (!fs.existsSync(filePath)) {
|
|
1109
1162
|
log$1(chalk.red(`Error: File not found at path: ${filePath}`));
|
|
1110
|
-
|
|
1163
|
+
summary.failed += 1;
|
|
1164
|
+
return summary;
|
|
1111
1165
|
}
|
|
1112
1166
|
const format = detectFileFormat(filePath);
|
|
1113
1167
|
if (!format) {
|
|
@@ -1116,7 +1170,8 @@ const importLabelsFromFile = async (configs2, filePath) => {
|
|
|
1116
1170
|
`Error: Unsupported file format. Supported formats: ${formatSupportedExtensions()}`
|
|
1117
1171
|
)
|
|
1118
1172
|
);
|
|
1119
|
-
|
|
1173
|
+
summary.failed += 1;
|
|
1174
|
+
return summary;
|
|
1120
1175
|
}
|
|
1121
1176
|
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
1122
1177
|
let parsedData;
|
|
@@ -1134,11 +1189,13 @@ const importLabelsFromFile = async (configs2, filePath) => {
|
|
|
1134
1189
|
`Parse error: ${parseError instanceof Error ? parseError.message : "Unknown error"}`
|
|
1135
1190
|
)
|
|
1136
1191
|
);
|
|
1137
|
-
|
|
1192
|
+
summary.failed += 1;
|
|
1193
|
+
return summary;
|
|
1138
1194
|
}
|
|
1139
1195
|
if (!Array.isArray(parsedData)) {
|
|
1140
1196
|
log$1(chalk.red("Error: File must contain an array of label objects"));
|
|
1141
|
-
|
|
1197
|
+
summary.failed += 1;
|
|
1198
|
+
return summary;
|
|
1142
1199
|
}
|
|
1143
1200
|
const validLabels = [];
|
|
1144
1201
|
for (let i = 0; i < parsedData.length; i++) {
|
|
@@ -1224,7 +1281,21 @@ const importLabelsFromFile = async (configs2, filePath) => {
|
|
|
1224
1281
|
}
|
|
1225
1282
|
if (validLabels.length === 0) {
|
|
1226
1283
|
log$1(chalk.red("Error: No valid labels found in file"));
|
|
1227
|
-
|
|
1284
|
+
summary.failed += 1;
|
|
1285
|
+
return summary;
|
|
1286
|
+
}
|
|
1287
|
+
summary.attempted = validLabels.length;
|
|
1288
|
+
if (dryRun) {
|
|
1289
|
+
validLabels.forEach((label) => {
|
|
1290
|
+
summary.skipped += 1;
|
|
1291
|
+
log$1(chalk.yellow(`[dry-run] Would create label "${label.name}"`));
|
|
1292
|
+
});
|
|
1293
|
+
log$1(
|
|
1294
|
+
chalk.blue(
|
|
1295
|
+
`Dry run summary: Would create ${validLabels.length} labels.`
|
|
1296
|
+
)
|
|
1297
|
+
);
|
|
1298
|
+
return summary;
|
|
1228
1299
|
}
|
|
1229
1300
|
log$1(chalk.blue(`Starting import of ${validLabels.length} labels...`));
|
|
1230
1301
|
log$1("");
|
|
@@ -1253,11 +1324,14 @@ const importLabelsFromFile = async (configs2, filePath) => {
|
|
|
1253
1324
|
`✅ Import completed successfully! Created ${successCount} labels.`
|
|
1254
1325
|
)
|
|
1255
1326
|
);
|
|
1327
|
+
summary.succeeded = successCount;
|
|
1256
1328
|
} else {
|
|
1257
1329
|
log$1(chalk.yellow(`⚠️ Import completed with some errors:`));
|
|
1258
1330
|
log$1(chalk.green(` • Successfully created: ${successCount} labels`));
|
|
1259
1331
|
log$1(chalk.red(` • Failed to create: ${errorCount} labels`));
|
|
1260
1332
|
log$1(chalk.blue(` • Total processed: ${validLabels.length} labels`));
|
|
1333
|
+
summary.succeeded = successCount;
|
|
1334
|
+
summary.failed += errorCount;
|
|
1261
1335
|
}
|
|
1262
1336
|
} catch (error) {
|
|
1263
1337
|
log$1(
|
|
@@ -1265,7 +1339,9 @@ const importLabelsFromFile = async (configs2, filePath) => {
|
|
|
1265
1339
|
`Error reading file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1266
1340
|
)
|
|
1267
1341
|
);
|
|
1342
|
+
summary.failed += 1;
|
|
1268
1343
|
}
|
|
1344
|
+
return summary;
|
|
1269
1345
|
};
|
|
1270
1346
|
const getTargetLabel = async () => {
|
|
1271
1347
|
const response = await prompts(deleteLabel$1);
|
|
@@ -1753,6 +1829,32 @@ const initializeConfigs = async () => {
|
|
|
1753
1829
|
return null;
|
|
1754
1830
|
}
|
|
1755
1831
|
};
|
|
1832
|
+
const makeSummary = () => ({
|
|
1833
|
+
created: 0,
|
|
1834
|
+
deleted: 0,
|
|
1835
|
+
skipped: 0,
|
|
1836
|
+
failed: 0,
|
|
1837
|
+
notes: []
|
|
1838
|
+
});
|
|
1839
|
+
const printSummary = (action, summary, dryRun) => {
|
|
1840
|
+
log(chalk.cyan(`
|
|
1841
|
+
=== ${action} summary ===`));
|
|
1842
|
+
if (dryRun) {
|
|
1843
|
+
log(chalk.yellow("Mode: dry run (no API calls executed)"));
|
|
1844
|
+
}
|
|
1845
|
+
log(
|
|
1846
|
+
chalk.green(`Created: ${summary.created}`) + chalk.red(` Failed: ${summary.failed}`) + chalk.blue(` Deleted: ${summary.deleted}`) + chalk.yellow(` Skipped: ${summary.skipped}`)
|
|
1847
|
+
);
|
|
1848
|
+
summary.notes.forEach((note) => log(chalk.gray(`- ${note}`)));
|
|
1849
|
+
if (summary.failed > 0 && !dryRun) {
|
|
1850
|
+
log(
|
|
1851
|
+
chalk.yellow(
|
|
1852
|
+
"Some operations failed. Re-run the command or check your credentials/permissions."
|
|
1853
|
+
)
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
log(chalk.cyan("========================\n"));
|
|
1857
|
+
};
|
|
1756
1858
|
const main = async () => {
|
|
1757
1859
|
if (firstStart) {
|
|
1758
1860
|
configs = await initializeConfigs();
|
|
@@ -1764,36 +1866,104 @@ const main = async () => {
|
|
|
1764
1866
|
while (selectedIndex == 99) {
|
|
1765
1867
|
selectedIndex = await selectAction();
|
|
1766
1868
|
}
|
|
1869
|
+
if (selectedIndex === 8) {
|
|
1870
|
+
console.log("exit");
|
|
1871
|
+
process.exit(0);
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
const dryRun = selectedIndex >= 0 && selectedIndex <= 4 ? await getDryRunChoice() : false;
|
|
1767
1875
|
switch (selectedIndex) {
|
|
1768
1876
|
case 0: {
|
|
1877
|
+
const summary = makeSummary();
|
|
1769
1878
|
const newLabel2 = await getNewLabel();
|
|
1770
|
-
|
|
1879
|
+
if (dryRun) {
|
|
1880
|
+
log(
|
|
1881
|
+
chalk.yellow(
|
|
1882
|
+
`[dry-run] Would create label "${newLabel2.name}" with color "${newLabel2.color ?? "N/A"}"`
|
|
1883
|
+
)
|
|
1884
|
+
);
|
|
1885
|
+
summary.skipped += 1;
|
|
1886
|
+
} else {
|
|
1887
|
+
const ok = await createLabel(configs, newLabel2);
|
|
1888
|
+
if (ok) {
|
|
1889
|
+
summary.created += 1;
|
|
1890
|
+
} else {
|
|
1891
|
+
summary.failed += 1;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
printSummary("Create a label", summary, dryRun);
|
|
1771
1895
|
firstStart = firstStart && false;
|
|
1772
1896
|
break;
|
|
1773
1897
|
}
|
|
1774
1898
|
case 1: {
|
|
1775
|
-
|
|
1899
|
+
const summary = makeSummary();
|
|
1900
|
+
if (dryRun) {
|
|
1901
|
+
log(
|
|
1902
|
+
chalk.yellow(
|
|
1903
|
+
`[dry-run] Would create ${labels.length} preset labels (no API calls)`
|
|
1904
|
+
)
|
|
1905
|
+
);
|
|
1906
|
+
summary.skipped += labels.length;
|
|
1907
|
+
} else {
|
|
1908
|
+
const result = await createLabels(configs);
|
|
1909
|
+
summary.created = result.created;
|
|
1910
|
+
summary.failed = result.failed;
|
|
1911
|
+
}
|
|
1912
|
+
printSummary("Create preset labels", summary, dryRun);
|
|
1776
1913
|
firstStart = firstStart && false;
|
|
1777
1914
|
break;
|
|
1778
1915
|
}
|
|
1779
1916
|
case 2: {
|
|
1917
|
+
const summary = makeSummary();
|
|
1780
1918
|
const targetLabel = await getTargetLabel();
|
|
1781
|
-
|
|
1919
|
+
if (dryRun) {
|
|
1920
|
+
summary.skipped += targetLabel.length;
|
|
1921
|
+
targetLabel.forEach(
|
|
1922
|
+
(name) => log(chalk.yellow(`[dry-run] Would delete label "${name}"`))
|
|
1923
|
+
);
|
|
1924
|
+
} else {
|
|
1925
|
+
const result = await deleteLabel(configs, targetLabel);
|
|
1926
|
+
summary.deleted = result.deleted;
|
|
1927
|
+
summary.failed = result.failed;
|
|
1928
|
+
}
|
|
1929
|
+
printSummary("Delete a label", summary, dryRun);
|
|
1782
1930
|
firstStart = firstStart && false;
|
|
1783
1931
|
break;
|
|
1784
1932
|
}
|
|
1785
1933
|
case 3: {
|
|
1786
|
-
|
|
1934
|
+
const summary = makeSummary();
|
|
1935
|
+
if (dryRun) {
|
|
1936
|
+
log(
|
|
1937
|
+
chalk.yellow(
|
|
1938
|
+
"[dry-run] Would delete all labels in the configured repository"
|
|
1939
|
+
)
|
|
1940
|
+
);
|
|
1941
|
+
summary.skipped += 1;
|
|
1942
|
+
} else {
|
|
1943
|
+
const result = await deleteLabels(configs);
|
|
1944
|
+
summary.deleted = result.deleted;
|
|
1945
|
+
summary.failed = result.failed;
|
|
1946
|
+
summary.notes.push("All labels processed");
|
|
1947
|
+
}
|
|
1948
|
+
printSummary("Delete all labels", summary, dryRun);
|
|
1787
1949
|
firstStart = firstStart && false;
|
|
1788
1950
|
break;
|
|
1789
1951
|
}
|
|
1790
1952
|
case 4: {
|
|
1953
|
+
const summary = makeSummary();
|
|
1791
1954
|
try {
|
|
1792
1955
|
const filePath = await getLabelFilePath();
|
|
1793
1956
|
if (filePath) {
|
|
1794
|
-
await importLabelsFromFile(configs, filePath);
|
|
1957
|
+
const result = await importLabelsFromFile(configs, filePath, dryRun);
|
|
1958
|
+
summary.created = result.succeeded;
|
|
1959
|
+
summary.failed = result.failed;
|
|
1960
|
+
summary.skipped = result.skipped;
|
|
1961
|
+
summary.notes.push(
|
|
1962
|
+
`Processed ${result.attempted} label entries from file`
|
|
1963
|
+
);
|
|
1795
1964
|
} else {
|
|
1796
1965
|
log(chalk.yellow("No file path provided. Returning to main menu."));
|
|
1966
|
+
summary.skipped += 1;
|
|
1797
1967
|
}
|
|
1798
1968
|
} catch (error) {
|
|
1799
1969
|
log(
|
|
@@ -1801,7 +1971,9 @@ const main = async () => {
|
|
|
1801
1971
|
`Error during label import: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1802
1972
|
)
|
|
1803
1973
|
);
|
|
1974
|
+
summary.failed += 1;
|
|
1804
1975
|
}
|
|
1976
|
+
printSummary("Import labels", summary, dryRun);
|
|
1805
1977
|
firstStart = firstStart && false;
|
|
1806
1978
|
break;
|
|
1807
1979
|
}
|
package/package.json
CHANGED