@uxland/harmonix-cli 0.0.6 → 0.0.7
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 +3 -1
- package/index.js +36 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,9 @@ registry=https://your-plugin-store.example.com/upload
|
|
|
19
19
|
apiKey=your-api-key
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
The `--registry` and `--api-key` options override the file.
|
|
22
|
+
The `--registry` and `--api-key` options override the file. A project `.harmonixrc` (the nearest one above the plugin folder) overrides the one in your home folder.
|
|
23
|
+
|
|
24
|
+
`harmonix publish` exits with code `1` when the upload fails (for example, the plugin does not exist in the store or the version is already published), so scripts can stop on errors.
|
|
23
25
|
|
|
24
26
|
> `.harmonixrc` contains credentials: never commit it. Projects made with `npm create @uxland/harmonix-plugin@latest` already ignore it.
|
|
25
27
|
|
package/index.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { program } = require(
|
|
4
|
-
const path = require(
|
|
5
|
-
const fs = require(
|
|
6
|
-
const os = require(
|
|
7
|
-
const axios = require(
|
|
8
|
-
const FormData = require(
|
|
9
|
-
|
|
10
|
-
console.log("reached index");
|
|
3
|
+
const { program } = require("commander");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const axios = require("axios");
|
|
8
|
+
const FormData = require("form-data");
|
|
11
9
|
|
|
12
10
|
function getConfig() {
|
|
13
11
|
const config = {};
|
|
12
|
+
|
|
13
|
+
// User-level config (~/.harmonixrc) first, so the project's .harmonixrc overrides it
|
|
14
|
+
const homeConfigPath = path.join(os.homedir(), ".harmonixrc");
|
|
15
|
+
if (fs.existsSync(homeConfigPath)) {
|
|
16
|
+
Object.assign(config, parseConfigFile(homeConfigPath));
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
let dir = process.cwd();
|
|
15
20
|
let steps = 0;
|
|
16
21
|
const maxSteps = 10;
|
|
17
22
|
|
|
18
23
|
while (steps < maxSteps) {
|
|
19
|
-
const configPath = path.join(dir,
|
|
24
|
+
const configPath = path.join(dir, ".harmonixrc");
|
|
20
25
|
if (fs.existsSync(configPath)) {
|
|
21
26
|
Object.assign(config, parseConfigFile(configPath));
|
|
22
27
|
break; // stop at first .harmonixrc found
|
|
@@ -28,27 +33,24 @@ function getConfig() {
|
|
|
28
33
|
steps++;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
// Also merge user-level config (~/.harmonixrc)
|
|
32
|
-
const homeConfigPath = path.join(os.homedir(), '.harmonixrc');
|
|
33
|
-
if (fs.existsSync(homeConfigPath)) {
|
|
34
|
-
Object.assign(config, parseConfigFile(homeConfigPath));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
36
|
return config;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
function parseConfigFile(filePath) {
|
|
41
40
|
const result = {};
|
|
42
41
|
try {
|
|
43
|
-
const content = fs.readFileSync(filePath,
|
|
44
|
-
content.split(
|
|
45
|
-
if (line.trim() && !line.startsWith(
|
|
46
|
-
|
|
42
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
43
|
+
for (const line of content.split("\n")) {
|
|
44
|
+
if (line.trim() && !line.startsWith("#")) {
|
|
45
|
+
// Split on the first '=' only: a value may contain '=' (e.g. base64 padding)
|
|
46
|
+
const separator = line.indexOf("=");
|
|
47
|
+
const key = separator === -1 ? "" : line.slice(0, separator).trim();
|
|
48
|
+
const value = separator === -1 ? "" : line.slice(separator + 1).trim();
|
|
47
49
|
if (key && value) {
|
|
48
|
-
result[key
|
|
50
|
+
result[key] = value;
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
|
-
}
|
|
53
|
+
}
|
|
52
54
|
} catch (err) {
|
|
53
55
|
console.warn(`⚠️ Error reading ${filePath}:`, err.message);
|
|
54
56
|
}
|
|
@@ -56,9 +58,9 @@ function parseConfigFile(filePath) {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
program
|
|
59
|
-
.command(
|
|
60
|
-
.option(
|
|
61
|
-
.option(
|
|
61
|
+
.command("publish")
|
|
62
|
+
.option("--registry <registry>", "Backend URL (overrides .harmonixrc)")
|
|
63
|
+
.option("--api-key <apiKey>", "API Key (overrides .harmonixrc)")
|
|
62
64
|
.action(async (opts) => {
|
|
63
65
|
try {
|
|
64
66
|
const config = getConfig();
|
|
@@ -99,12 +101,19 @@ program
|
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
const headers = {
|
|
102
|
-
|
|
104
|
+
"x-api-key": apiKey,
|
|
103
105
|
};
|
|
104
106
|
const response = await axios.post(registry, formData, { headers });
|
|
105
|
-
|
|
107
|
+
if (response.data?.success === false) {
|
|
108
|
+
throw new Error(response.data.error || response.data.message || "Plugin Store rejected the upload");
|
|
109
|
+
}
|
|
110
|
+
console.log(`✅ Plugin published: ${pluginId}@${versionNumber}`, response.data);
|
|
106
111
|
} catch (err) {
|
|
107
|
-
|
|
112
|
+
// Exit non-zero so scripts and CI can tell a failed publish from a successful one
|
|
113
|
+
const serverError = err.response?.data?.error || err.response?.data?.message;
|
|
114
|
+
const status = err.response ? ` (HTTP ${err.response.status})` : "";
|
|
115
|
+
console.error(`❌ Failed to publish${status}:`, serverError || err.message);
|
|
116
|
+
process.exit(1);
|
|
108
117
|
}
|
|
109
118
|
});
|
|
110
119
|
|