@lightward/mechanic-cli 0.1.8 → 0.1.10
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 +34 -7
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/commands/github/init.d.ts.map +1 -1
- package/dist/commands/github/init.js +2 -2
- package/dist/commands/globals/delete.d.ts +13 -0
- package/dist/commands/globals/delete.d.ts.map +1 -0
- package/dist/commands/globals/delete.js +28 -0
- package/dist/commands/globals/list.d.ts +10 -0
- package/dist/commands/globals/list.d.ts.map +1 -0
- package/dist/commands/globals/list.js +39 -0
- package/dist/commands/globals/pull.d.ts +11 -0
- package/dist/commands/globals/pull.d.ts.map +1 -0
- package/dist/commands/globals/pull.js +42 -0
- package/dist/commands/globals/push.d.ts +11 -0
- package/dist/commands/globals/push.d.ts.map +1 -0
- package/dist/commands/globals/push.js +57 -0
- package/dist/commands/globals/set.d.ts +13 -0
- package/dist/commands/globals/set.d.ts.map +1 -0
- package/dist/commands/globals/set.js +26 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/secrets/delete.d.ts +13 -0
- package/dist/commands/secrets/delete.d.ts.map +1 -0
- package/dist/commands/secrets/delete.js +28 -0
- package/dist/commands/secrets/list.d.ts +10 -0
- package/dist/commands/secrets/list.d.ts.map +1 -0
- package/dist/commands/secrets/list.js +37 -0
- package/dist/commands/secrets/set.d.ts +25 -0
- package/dist/commands/secrets/set.d.ts.map +1 -0
- package/dist/commands/secrets/set.js +91 -0
- package/dist/commands/shop/deprecations.d.ts +20 -0
- package/dist/commands/shop/deprecations.d.ts.map +1 -0
- package/dist/commands/shop/deprecations.js +95 -0
- package/dist/commands/tasks/diff.d.ts.map +1 -1
- package/dist/commands/tasks/diff.js +4 -3
- package/dist/commands/tasks/list.d.ts.map +1 -1
- package/dist/commands/tasks/list.js +12 -1
- package/dist/commands/tasks/new.d.ts.map +1 -1
- package/dist/commands/tasks/new.js +10 -2
- package/dist/commands/tasks/push.d.ts +2 -1
- package/dist/commands/tasks/push.d.ts.map +1 -1
- package/dist/commands/tasks/push.js +43 -20
- package/dist/commands/tasks/status.d.ts +22 -4
- package/dist/commands/tasks/status.d.ts.map +1 -1
- package/dist/commands/tasks/status.js +66 -9
- package/dist/config-keys.d.ts +2 -0
- package/dist/config-keys.d.ts.map +1 -0
- package/dist/config-keys.js +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -3
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { stdin } from "node:process";
|
|
3
|
+
import { promptForHiddenValue } from "../../auth.js";
|
|
4
|
+
import { BaseCommand } from "../../base-command.js";
|
|
5
|
+
import { validateConfigKey } from "../../config-keys.js";
|
|
6
|
+
import { CliError } from "../../errors.js";
|
|
7
|
+
export function readStdin(input = stdin) {
|
|
8
|
+
if (input.isTTY) {
|
|
9
|
+
throw new CliError("Cannot read secret value from stdin while stdin is a TTY. Pipe a value or use --value-env.", 2);
|
|
10
|
+
}
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
let value = "";
|
|
13
|
+
input.setEncoding("utf8");
|
|
14
|
+
input.on("data", (chunk) => {
|
|
15
|
+
value += chunk;
|
|
16
|
+
});
|
|
17
|
+
input.on("end", () => {
|
|
18
|
+
resolve(value);
|
|
19
|
+
});
|
|
20
|
+
input.on("error", reject);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export async function readSecretValueFromStdin(input = stdin) {
|
|
24
|
+
const value = await readStdin(input);
|
|
25
|
+
if (value.length === 0) {
|
|
26
|
+
throw new CliError("Secret value cannot be blank.", 2);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
export function readSecretValueFromEnv(envName, env = process.env) {
|
|
31
|
+
const value = env[envName];
|
|
32
|
+
if (value === undefined) {
|
|
33
|
+
throw new CliError(`Environment variable ${envName} is not set.`, 2);
|
|
34
|
+
}
|
|
35
|
+
if (value.length === 0) {
|
|
36
|
+
throw new CliError(`Environment variable ${envName} is empty.`, 2);
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
export default class SecretsSet extends BaseCommand {
|
|
41
|
+
static summary = "Set one shop secret.";
|
|
42
|
+
static description = "Set one write-only shop secret. The secret value is never printed.";
|
|
43
|
+
static args = {
|
|
44
|
+
key: Args.string({ required: true, description: "Secret key, using lower snake-case." }),
|
|
45
|
+
};
|
|
46
|
+
static flags = {
|
|
47
|
+
"from-stdin": Flags.boolean({
|
|
48
|
+
description: "Read the secret value from stdin.",
|
|
49
|
+
exclusive: ["value-env"],
|
|
50
|
+
}),
|
|
51
|
+
force: Flags.boolean({
|
|
52
|
+
char: "f",
|
|
53
|
+
description: "Replace the value when this secret key already exists.",
|
|
54
|
+
}),
|
|
55
|
+
"value-env": Flags.string({
|
|
56
|
+
description: "Read the secret value from the named environment variable.",
|
|
57
|
+
exclusive: ["from-stdin"],
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
async run() {
|
|
61
|
+
const { args, flags } = await this.parse(SecretsSet);
|
|
62
|
+
const key = validateConfigKey(args.key);
|
|
63
|
+
const project = await this.loadProject();
|
|
64
|
+
const client = await this.verifiedClientForProject(project);
|
|
65
|
+
const existing = (await client.listSecrets()).secrets?.some((secret) => secret.key === key);
|
|
66
|
+
if (existing && !flags.force) {
|
|
67
|
+
throw new CliError(`Secret ${key} already exists. Re-run with --force to replace its value.`, 2);
|
|
68
|
+
}
|
|
69
|
+
const value = await this.secretValue(flags);
|
|
70
|
+
await client.setSecret(key, value);
|
|
71
|
+
this.log(`${this.success(existing ? "Replaced" : "Set")} secret ${this.taskName(key)}`);
|
|
72
|
+
}
|
|
73
|
+
async secretValue(flags) {
|
|
74
|
+
if (flags["from-stdin"]) {
|
|
75
|
+
return readSecretValueFromStdin();
|
|
76
|
+
}
|
|
77
|
+
if (flags["value-env"]) {
|
|
78
|
+
return readSecretValueFromEnv(flags["value-env"]);
|
|
79
|
+
}
|
|
80
|
+
const value = await promptForHiddenValue({
|
|
81
|
+
blankMessage: "Secret value cannot be blank.",
|
|
82
|
+
missingInputMessage: "Provide a secret value with --from-stdin or --value-env.",
|
|
83
|
+
prompt: "Secret value: ",
|
|
84
|
+
trim: false,
|
|
85
|
+
});
|
|
86
|
+
if (value === null) {
|
|
87
|
+
throw new CliError("Secret value cannot be blank.", 2);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
import type { Project, ShopDeprecationsResponse } from "../../types.js";
|
|
3
|
+
type ShopDeprecation = ShopDeprecationsResponse["deprecations"][number];
|
|
4
|
+
export default class ShopDeprecations extends BaseCommand {
|
|
5
|
+
static summary: string;
|
|
6
|
+
static description: string;
|
|
7
|
+
static args: {
|
|
8
|
+
task: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
taskIdForSelector(project: Project, input: string): Promise<string>;
|
|
15
|
+
deprecationsTable(response: ShopDeprecationsResponse): string[][];
|
|
16
|
+
deprecationCountSummary(response: ShopDeprecationsResponse): string;
|
|
17
|
+
requestSummary(deprecation: ShopDeprecation): string;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=deprecations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecations.d.ts","sourceRoot":"","sources":["../../../src/commands/shop/deprecations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,KAAK,EAAE,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAExE,KAAK,eAAe,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAUxE,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,OAAgB,OAAO,SAA6D;IACpF,OAAgB,WAAW,SAAqH;IAEhJ,OAAgB,IAAI;;MAKlB;IAEF,OAAgB,KAAK;;MAInB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAyCpB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzE,iBAAiB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM,EAAE,EAAE;IAcjE,uBAAuB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM;IAOnE,cAAc,CAAC,WAAW,EAAE,eAAe,GAAG,MAAM;CAcrD"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { CliError } from "../../errors.js";
|
|
4
|
+
import { resolveTaskSelector } from "../../tasks.js";
|
|
5
|
+
function formatCount(value) {
|
|
6
|
+
return typeof value === "number" ? value.toLocaleString() : "--";
|
|
7
|
+
}
|
|
8
|
+
function formatValue(value) {
|
|
9
|
+
return value && value.trim() ? value : "--";
|
|
10
|
+
}
|
|
11
|
+
export default class ShopDeprecations extends BaseCommand {
|
|
12
|
+
static summary = "Show unresolved Shopify API deprecations for this shop.";
|
|
13
|
+
static description = "Show unresolved Shopify API deprecations reported by this shop's tasks, optionally filtered to one linked task.";
|
|
14
|
+
static args = {
|
|
15
|
+
task: Args.string({
|
|
16
|
+
description: "Optional task selector, linked task ID, local slug, task JSON file, or helper directory.",
|
|
17
|
+
required: false,
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
static flags = {
|
|
21
|
+
json: Flags.boolean({
|
|
22
|
+
description: "Print deprecations as JSON for agents, scripts, or editor integrations.",
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { args, flags } = await this.parse(ShopDeprecations);
|
|
27
|
+
const project = await this.loadProject();
|
|
28
|
+
const client = await this.verifiedClientForProject(project);
|
|
29
|
+
const taskId = args.task ? await this.taskIdForSelector(project, args.task) : undefined;
|
|
30
|
+
const response = await client.getShopDeprecations(taskId);
|
|
31
|
+
if (flags.json) {
|
|
32
|
+
this.outputJson(response);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.log(`${this.accent("Shop")} ${response.shop.shopify_domain}`);
|
|
36
|
+
if (taskId) {
|
|
37
|
+
this.log(`${this.accent("Task")} ${this.taskId(project, taskId)}`);
|
|
38
|
+
}
|
|
39
|
+
if (response.deprecations.length === 0) {
|
|
40
|
+
this.log("");
|
|
41
|
+
this.log(this.success("No unresolved Shopify API deprecations."));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.log(`${this.accent("Deprecations")} ${this.deprecationCountSummary(response)}`);
|
|
45
|
+
if (response.truncated) {
|
|
46
|
+
const limit = typeof response.limit === "number" ? response.limit.toLocaleString() : response.deprecations.length.toLocaleString();
|
|
47
|
+
this.log(this.color("yellow", `Showing the first ${limit} unresolved deprecations. Filter with "mechanic shop deprecations <task>" for one task.`));
|
|
48
|
+
}
|
|
49
|
+
this.log("");
|
|
50
|
+
this.table(this.deprecationsTable(response));
|
|
51
|
+
this.log("");
|
|
52
|
+
this.log(this.accent("Details"));
|
|
53
|
+
response.deprecations.forEach((deprecation, index) => {
|
|
54
|
+
this.log(`${index + 1}. ${this.taskName(deprecation.task_name || deprecation.task_id)}`);
|
|
55
|
+
this.log(` ${this.muted("Request")} ${this.requestSummary(deprecation)}`);
|
|
56
|
+
this.log(` ${this.muted("Reason")} ${formatValue(deprecation.reason)}`);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async taskIdForSelector(project, input) {
|
|
60
|
+
const selector = await resolveTaskSelector(project, input, { allowHelperDir: true, allowRemoteId: true });
|
|
61
|
+
if (!selector.remoteId) {
|
|
62
|
+
throw new CliError(`${input} is not linked to a remote Mechanic task. Pull or publish it first.`, 2);
|
|
63
|
+
}
|
|
64
|
+
return selector.remoteId;
|
|
65
|
+
}
|
|
66
|
+
deprecationsTable(response) {
|
|
67
|
+
return [
|
|
68
|
+
["#", "Task", "Task API", "Request API", "Last seen", "Count"],
|
|
69
|
+
...response.deprecations.map((deprecation, index) => [
|
|
70
|
+
String(index + 1),
|
|
71
|
+
this.taskName(deprecation.task_name || deprecation.task_id),
|
|
72
|
+
formatValue(deprecation.task_shopify_api_version),
|
|
73
|
+
formatValue(deprecation.shopify_api_version),
|
|
74
|
+
this.muted(formatValue(deprecation.last_occurrence_at)),
|
|
75
|
+
formatCount(deprecation.occurrences_count),
|
|
76
|
+
]),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
deprecationCountSummary(response) {
|
|
80
|
+
const shown = response.deprecations.length;
|
|
81
|
+
const total = typeof response.total_count === "number" ? response.total_count : shown;
|
|
82
|
+
return `${shown.toLocaleString()}/${total.toLocaleString()} shown`;
|
|
83
|
+
}
|
|
84
|
+
requestSummary(deprecation) {
|
|
85
|
+
const method = formatValue(deprecation.request_method);
|
|
86
|
+
const path = formatValue(deprecation.request_path);
|
|
87
|
+
if (method === "--") {
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
if (path === "--") {
|
|
91
|
+
return method;
|
|
92
|
+
}
|
|
93
|
+
return `${method} ${path}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAapD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,OAAO,SAA2D;IAClF,OAAgB,WAAW,SAA6G;IAExI,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;MAMnB;IAEF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IA2BzB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAapD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,OAAO,SAA2D;IAClF,OAAgB,WAAW,SAA6G;IAExI,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;MAMnB;IAEF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IA2BzB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAwI3B"}
|
|
@@ -51,6 +51,7 @@ export default class TasksDiff extends BaseCommand {
|
|
|
51
51
|
const relativeFile = displayTaskPath(project, file);
|
|
52
52
|
if (!link) {
|
|
53
53
|
hasUnlinkedTasks = true;
|
|
54
|
+
const message = `No remote task is linked yet. Run "mechanic tasks publish ${slug} --dry-run" to review the disabled create plan.`;
|
|
54
55
|
results.push({
|
|
55
56
|
file: relativeFile,
|
|
56
57
|
slug,
|
|
@@ -59,9 +60,9 @@ export default class TasksDiff extends BaseCommand {
|
|
|
59
60
|
remote_changed: false,
|
|
60
61
|
field_differences: false,
|
|
61
62
|
diff: null,
|
|
62
|
-
message
|
|
63
|
+
message,
|
|
63
64
|
});
|
|
64
|
-
differences.push(`# ${relativeFile}\
|
|
65
|
+
differences.push(`# ${relativeFile}\n${message}`);
|
|
65
66
|
continue;
|
|
66
67
|
}
|
|
67
68
|
const localTask = taskForPush(await readTaskFile(file));
|
|
@@ -134,7 +135,7 @@ export default class TasksDiff extends BaseCommand {
|
|
|
134
135
|
summary = "Remote changes found.";
|
|
135
136
|
}
|
|
136
137
|
else if (!hasFieldDifferences && hasUnlinkedTasks) {
|
|
137
|
-
summary = "
|
|
138
|
+
summary = "No remote task linked yet.";
|
|
138
139
|
}
|
|
139
140
|
this.log(summary);
|
|
140
141
|
if (flags["exit-code"]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAcpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,OAAO,SAAkD;IACzE,OAAgB,WAAW,SAAsF;IAEjH,OAAgB,KAAK;;;MAQnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAoD3B"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BaseCommand } from "../../base-command.js";
|
|
3
3
|
import { slugForRemoteId } from "../../config.js";
|
|
4
|
+
function taskShopifyApiVersion(envelope) {
|
|
5
|
+
if (typeof envelope.shopify_api_version === "string" && envelope.shopify_api_version.trim()) {
|
|
6
|
+
return envelope.shopify_api_version;
|
|
7
|
+
}
|
|
8
|
+
const version = envelope.task?.shopify_api_version;
|
|
9
|
+
return typeof version === "string" && version.trim() ? version : null;
|
|
10
|
+
}
|
|
4
11
|
export default class TasksList extends BaseCommand {
|
|
5
12
|
static summary = "List remote tasks without changing anything.";
|
|
6
13
|
static description = "List remote Mechanic tasks without pulling, publishing, or changing local files.";
|
|
@@ -28,6 +35,7 @@ export default class TasksList extends BaseCommand {
|
|
|
28
35
|
id: envelope.id,
|
|
29
36
|
name: typeof envelope.task?.name === "string" ? envelope.task.name : "",
|
|
30
37
|
updated_at: envelope.updated_at || null,
|
|
38
|
+
shopify_api_version: taskShopifyApiVersion(envelope),
|
|
31
39
|
content_hash: envelope.content_hash || null,
|
|
32
40
|
linked: Boolean(link),
|
|
33
41
|
slug: slug || null,
|
|
@@ -37,13 +45,16 @@ export default class TasksList extends BaseCommand {
|
|
|
37
45
|
});
|
|
38
46
|
return;
|
|
39
47
|
}
|
|
40
|
-
const rows = flags.verbose
|
|
48
|
+
const rows = flags.verbose
|
|
49
|
+
? [["Name", "File", "API version", "Updated", "ID", "Hash"]]
|
|
50
|
+
: [["Name", "File", "API version", "Updated", "ID"]];
|
|
41
51
|
for (const envelope of response.tasks || []) {
|
|
42
52
|
const slug = slugForRemoteId(project, envelope.id);
|
|
43
53
|
const link = slug ? project.links.tasks[slug] : null;
|
|
44
54
|
const row = [
|
|
45
55
|
this.taskName(String(envelope.task?.name || "")),
|
|
46
56
|
link ? this.taskName(link.file || `${project.tasksDirName}/${slug}.json`) : this.muted("--"),
|
|
57
|
+
taskShopifyApiVersion(envelope) || this.muted("--"),
|
|
47
58
|
this.muted(envelope.updated_at || ""),
|
|
48
59
|
this.taskId(project, envelope.id),
|
|
49
60
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/new.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/new.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAyCpD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,OAAgB,OAAO,SAAoC;IAC3D,OAAgB,WAAW,SAAiJ;IAC5K,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;MAGnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YA0CZ,kBAAkB;CAoCjC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Args, Flags } from "@oclif/core";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
|
+
import { linkForSlug } from "../../config.js";
|
|
4
5
|
import { CliError } from "../../errors.js";
|
|
5
6
|
import { pathExists } from "../../fs.js";
|
|
6
7
|
import { displayTaskPath, taskPath, taskSlug, unbundleTask, writeTaskFilePath } from "../../tasks.js";
|
|
@@ -59,7 +60,7 @@ export default class TasksNew extends BaseCommand {
|
|
|
59
60
|
const name = taskNameFromInput(requestedName);
|
|
60
61
|
const filePath = taskPath(project, slug);
|
|
61
62
|
const helperDir = filePath.replace(/\.json$/i, "");
|
|
62
|
-
await this.checkExistingPaths(project, filePath, helperDir, Boolean(flags.force));
|
|
63
|
+
await this.checkExistingPaths(project, slug, filePath, helperDir, Boolean(flags.force));
|
|
63
64
|
await writeTaskFilePath(filePath, starterTask(name));
|
|
64
65
|
await unbundleTask(filePath, helperDir);
|
|
65
66
|
const fileDisplay = displayTaskPath(project, filePath);
|
|
@@ -83,7 +84,14 @@ export default class TasksNew extends BaseCommand {
|
|
|
83
84
|
this.log(` mechanic tasks bundle ${this.taskName(slug)}`);
|
|
84
85
|
this.log(` mechanic tasks publish ${this.taskName(slug)}`);
|
|
85
86
|
}
|
|
86
|
-
async checkExistingPaths(project, filePath, helperDir, force) {
|
|
87
|
+
async checkExistingPaths(project, slug, filePath, helperDir, force) {
|
|
88
|
+
const link = linkForSlug(project, slug);
|
|
89
|
+
if (link) {
|
|
90
|
+
throw new CliError([
|
|
91
|
+
`${slug} is already linked to a Mechanic task.`,
|
|
92
|
+
`Run "mechanic tasks pull ${link.remote_id}" to refresh it, or choose another name for a new task.`,
|
|
93
|
+
].join("\n"), 2);
|
|
94
|
+
}
|
|
87
95
|
if (force) {
|
|
88
96
|
return;
|
|
89
97
|
}
|
|
@@ -18,6 +18,7 @@ type PushRow = {
|
|
|
18
18
|
task_id: string | null;
|
|
19
19
|
content_hash: string | null;
|
|
20
20
|
details: string;
|
|
21
|
+
enabled?: boolean | null;
|
|
21
22
|
url?: string;
|
|
22
23
|
};
|
|
23
24
|
type CreateCollision = {
|
|
@@ -55,7 +56,7 @@ export default class TasksPush extends BaseCommand {
|
|
|
55
56
|
rows: PushRow[];
|
|
56
57
|
}>;
|
|
57
58
|
pushRowsToTable(project: Project, statusHeader: string, rows: PushRow[], label: (status: string) => string): string[][];
|
|
58
|
-
|
|
59
|
+
printPublishedTaskLinks(rows: PushRow[]): void;
|
|
59
60
|
planLabel(plan: string): string;
|
|
60
61
|
}
|
|
61
62
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/push.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAoBpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnF,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,CAAC;AAEF,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,25 @@ function createTaskIdempotencyKey(shopDomain, slug) {
|
|
|
18
18
|
hex.slice(20, 32),
|
|
19
19
|
].join("-");
|
|
20
20
|
}
|
|
21
|
+
function remoteTaskEnabled(envelope) {
|
|
22
|
+
if (typeof envelope.enabled === "boolean") {
|
|
23
|
+
return envelope.enabled;
|
|
24
|
+
}
|
|
25
|
+
if (typeof envelope.task.enabled === "boolean") {
|
|
26
|
+
return envelope.task.enabled;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function remoteTaskStateDetail(envelope) {
|
|
31
|
+
const enabled = remoteTaskEnabled(envelope);
|
|
32
|
+
if (enabled === true) {
|
|
33
|
+
return "currently enabled";
|
|
34
|
+
}
|
|
35
|
+
if (enabled === false) {
|
|
36
|
+
return "currently disabled";
|
|
37
|
+
}
|
|
38
|
+
return "";
|
|
39
|
+
}
|
|
21
40
|
export default class TasksPush extends BaseCommand {
|
|
22
41
|
static hidden = true;
|
|
23
42
|
static summary = "Publish one local task, or use --all to publish every local task.";
|
|
@@ -68,7 +87,7 @@ export default class TasksPush extends BaseCommand {
|
|
|
68
87
|
return;
|
|
69
88
|
}
|
|
70
89
|
this.table(this.pushRowsToTable(project, "Action", rows, (status) => this.actionLabel(status)));
|
|
71
|
-
this.
|
|
90
|
+
this.printPublishedTaskLinks(rows);
|
|
72
91
|
}
|
|
73
92
|
async publishTasks(project, preparation, force) {
|
|
74
93
|
const client = await this.verifiedClientForProject(project);
|
|
@@ -91,22 +110,25 @@ export default class TasksPush extends BaseCommand {
|
|
|
91
110
|
task_id: created.id,
|
|
92
111
|
content_hash: created.content_hash,
|
|
93
112
|
details: "created disabled; enable in Mechanic",
|
|
113
|
+
enabled: remoteTaskEnabled(created),
|
|
94
114
|
url: taskAdminUrl(project, created.id),
|
|
95
115
|
});
|
|
96
116
|
continue;
|
|
97
117
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
const remote = force
|
|
119
|
+
? await client.getTask(link.remote_id)
|
|
120
|
+
: remoteBySlug.get(slug) || await client.getTask(link.remote_id);
|
|
121
|
+
if (stableStringify(task) === stableStringify(taskForPush(remote.task))) {
|
|
122
|
+
rows.push({
|
|
123
|
+
file: relativeFile,
|
|
124
|
+
status: "skipped",
|
|
125
|
+
task_id: remote.id,
|
|
126
|
+
content_hash: remote.content_hash,
|
|
127
|
+
details: remoteTaskStateDetail(remote),
|
|
128
|
+
enabled: remoteTaskEnabled(remote),
|
|
129
|
+
url: taskAdminUrl(project, remote.id),
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
110
132
|
}
|
|
111
133
|
const updated = await client.updateTask(link.remote_id, {
|
|
112
134
|
task,
|
|
@@ -120,7 +142,8 @@ export default class TasksPush extends BaseCommand {
|
|
|
120
142
|
status: force ? "forced" : "updated",
|
|
121
143
|
task_id: updated.id,
|
|
122
144
|
content_hash: updated.content_hash,
|
|
123
|
-
details:
|
|
145
|
+
details: remoteTaskStateDetail(updated),
|
|
146
|
+
enabled: remoteTaskEnabled(updated),
|
|
124
147
|
url: taskAdminUrl(project, updated.id),
|
|
125
148
|
});
|
|
126
149
|
}
|
|
@@ -326,7 +349,7 @@ export default class TasksPush extends BaseCommand {
|
|
|
326
349
|
status: "conflict",
|
|
327
350
|
task_id: remote.id,
|
|
328
351
|
content_hash: remote.content_hash,
|
|
329
|
-
details:
|
|
352
|
+
details: remoteChangedSinceLastPullMessage(relativeFile, remote.id, localChangedSinceLastSync(link, task)),
|
|
330
353
|
});
|
|
331
354
|
continue;
|
|
332
355
|
}
|
|
@@ -356,14 +379,14 @@ export default class TasksPush extends BaseCommand {
|
|
|
356
379
|
]),
|
|
357
380
|
];
|
|
358
381
|
}
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
if (
|
|
382
|
+
printPublishedTaskLinks(rows) {
|
|
383
|
+
const publishedRows = rows.filter((row) => ["created", "updated", "forced"].includes(row.status) && row.url);
|
|
384
|
+
if (publishedRows.length === 0) {
|
|
362
385
|
return;
|
|
363
386
|
}
|
|
364
387
|
this.log("");
|
|
365
|
-
this.log(this.accent(
|
|
366
|
-
for (const row of
|
|
388
|
+
this.log(this.accent(publishedRows.length === 1 ? "Open task:" : "Open published tasks:"));
|
|
389
|
+
for (const row of publishedRows) {
|
|
367
390
|
this.log(` ${this.taskName(row.file)} ${this.taskName(row.url || "")}`);
|
|
368
391
|
}
|
|
369
392
|
}
|
|
@@ -7,6 +7,25 @@ type HelperStatus = {
|
|
|
7
7
|
blocked: boolean;
|
|
8
8
|
details?: string;
|
|
9
9
|
};
|
|
10
|
+
type TaskStatusResult = {
|
|
11
|
+
file: string;
|
|
12
|
+
slug: string;
|
|
13
|
+
link: {
|
|
14
|
+
linked: boolean;
|
|
15
|
+
remote_id: string | null;
|
|
16
|
+
};
|
|
17
|
+
local: HelperStatus;
|
|
18
|
+
remote: {
|
|
19
|
+
label: string;
|
|
20
|
+
details?: string;
|
|
21
|
+
task_id?: string;
|
|
22
|
+
task_name?: string;
|
|
23
|
+
updated_at?: string | null;
|
|
24
|
+
enabled?: boolean | null;
|
|
25
|
+
url?: string;
|
|
26
|
+
} | null;
|
|
27
|
+
details: string[];
|
|
28
|
+
};
|
|
10
29
|
export default class TasksStatus extends BaseCommand {
|
|
11
30
|
static summary: string;
|
|
12
31
|
static description: string;
|
|
@@ -19,12 +38,11 @@ export default class TasksStatus extends BaseCommand {
|
|
|
19
38
|
};
|
|
20
39
|
run(): Promise<void>;
|
|
21
40
|
helperStatus(project: Project, file: string, task: JsonObject): Promise<HelperStatus>;
|
|
22
|
-
remoteStatus(client: MechanicClient | null, task: JsonObject, link: ReturnType<typeof linkForSlug
|
|
23
|
-
|
|
24
|
-
details?: string;
|
|
25
|
-
}>;
|
|
41
|
+
remoteStatus(project: Project, client: MechanicClient | null, task: JsonObject, link: ReturnType<typeof linkForSlug>, relativeFile: string): Promise<TaskStatusResult["remote"]>;
|
|
42
|
+
printSingleTaskInfo(project: Project, statuses: TaskStatusResult[], checkRemote: boolean): void;
|
|
26
43
|
localLabel(label: string): string;
|
|
27
44
|
remoteLabel(label: string): string;
|
|
45
|
+
enabledLabel(enabled: boolean | null | undefined): string;
|
|
28
46
|
}
|
|
29
47
|
export {};
|
|
30
48
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/status.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/status.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAgB,MAAM,iBAAiB,CAAC;AAgB5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,gBAAgB,CAAC;AAExE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QACJ,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;IACF,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QACzB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IACT,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AA0BF,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,OAAgB,OAAO,SAAwE;IAC/F,OAAgB,WAAW,SAA+Q;IAE1S,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;MAOnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAuHpB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IA4BrF,YAAY,CAChB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GAAG,IAAI,EAC7B,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,EACpC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAiCtC,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,EAAE,WAAW,EAAE,OAAO,GAAG,IAAI;IAwB/F,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAejC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAmBlC,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM;CAW1D"}
|