@pitcher/cli-scripts 1.0.9 → 2.0.0

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 CHANGED
@@ -2,26 +2,46 @@
2
2
 
3
3
  > Pitcher CLI Tools provide a suite of command-line utilities to streamline your workflow. For the best experience, we recommend installing the tools globally using `npm install -g @pitcher/cli-scripts`.
4
4
 
5
-
6
5
  ## Help
6
+
7
7
  ```bash
8
8
  p --help
9
9
  # or for specific command help:
10
10
  p command --help
11
11
  ```
12
12
 
13
-
14
- ## To run the `copy-instance-files` command:
15
-
16
- ```bash
17
- p copy-instance-files <fromInstance> <toInstance> --org pitcher --api-key myapikey
18
- ```
19
-
20
13
  ## To run the `publish-app` command:
14
+
21
15
  ```bash
16
+ # Basic usage
22
17
  p publish-app <folder> --url https://dev.my.pitcher.com --api-key myapikey
23
- # or for current folder and dev org
18
+
19
+ # Publish to current folder and dev org
24
20
  p publish-app --api-key myapikey
25
- # or for dry run and incrementing app.json's version
21
+
22
+ # Dry run with version increment
26
23
  p publish-app --api-key myapikey --dry-run --increment-version
24
+
25
+ # Publish and install to specific instances
26
+ p publish-app --api-key myapikey --increment-version --install-to "01HG0C5NS1BTQ2ZPE1XCGKSGDH, 01HG0C5NS1BTQ2ZPE1XCGKSGDA"
27
+
28
+ # Publish and install to all instances with a specific environment name
29
+ p publish-app --api-key myapikey --increment-version --install-to-env-name "Demo"
30
+
31
+ # Combine both: install to specific instances AND all instances in an environment
32
+ p publish-app --api-key myapikey --increment-version --install-to "01HG0C5NS1BTQ2ZPE1XCGKSGDH" --install-to-env-name "Demo"
33
+
34
+ # Alternative: Set API key via environment variable
35
+ export NG_API_KEY=myapikey
36
+ p publish-app --increment-version --install-to-env-name "Production"
27
37
  ```
38
+
39
+ ### Options
40
+
41
+ - `--api-key <apiKey>`: API Key for authentication (can also use `NG_API_KEY` env variable)
42
+ - `--org <organization>`: Organization subdomain (default: "dev")
43
+ - `--increment-version`: Increment version in app.json with timestamp
44
+ - `--dry-run`: Perform a dry run without publishing
45
+ - `--publish-url <url>`: Explicit publish URL
46
+ - `--install-to <instanceIds>`: Comma-separated list of instance IDs to install the app to after publishing
47
+ - `--install-to-env-name <envName>`: Environment name to filter instances and install the app to after publishing
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import { createCopyInstanceFilesCommand } from "./src/copyInstanceFiles.js";
4
3
  import { createPublishAppCommand } from "./src/publishApp.js";
5
4
  import { readFileSync } from "fs";
6
5
  import { fileURLToPath } from "url";
@@ -14,6 +13,5 @@ program
14
13
  .description("The Pitcher CLI for various tasks in its ecosystem.")
15
14
  .version(pkg.version);
16
15
  // Add subcommands
17
- program.addCommand(createCopyInstanceFilesCommand());
18
16
  program.addCommand(createPublishAppCommand());
19
17
  program.parse(process.argv);
@@ -4,18 +4,97 @@ import axios from "axios";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import FormData from "form-data";
7
- import { fileURLToPath } from 'url';
7
+ import { fileURLToPath } from "url";
8
+ async function fetchAllInstances(baseUrl, apiKey) {
9
+ try {
10
+ const response = await axios.get(`${baseUrl}/api/v1/instances`, {
11
+ headers: {
12
+ accept: "application/json",
13
+ "x-api-key": apiKey,
14
+ },
15
+ });
16
+ const instances = response.data.results || response.data;
17
+ console.log(`Fetched ${instances.length} total instance(s) from API`);
18
+ return instances;
19
+ }
20
+ catch (error) {
21
+ const axiosError = error;
22
+ console.error("Error fetching instances:", axiosError?.response?.data?.detail ?? axiosError.message);
23
+ return [];
24
+ }
25
+ }
26
+ function filterInstancesByEnvName(instances, envName) {
27
+ const filtered = instances.filter((instance) => instance.env_name === envName);
28
+ if (filtered.length === 0) {
29
+ console.warn(`No instances found with environment name "${envName}"`);
30
+ }
31
+ else {
32
+ console.log(`Found ${filtered.length} instance(s) with environment name "${envName}"`);
33
+ }
34
+ return filtered;
35
+ }
36
+ function validateInstanceIds(instances, requestedIds) {
37
+ const instanceMap = new Map(instances.map((inst) => [inst.id, inst]));
38
+ const valid = [];
39
+ const invalid = [];
40
+ for (const id of requestedIds) {
41
+ const instance = instanceMap.get(id);
42
+ if (instance) {
43
+ valid.push(instance);
44
+ }
45
+ else {
46
+ invalid.push(id);
47
+ }
48
+ }
49
+ return { valid, invalid };
50
+ }
51
+ async function installAppToInstances(baseUrl, apiKey, appName, instances) {
52
+ if (instances.length === 0) {
53
+ console.log("No instances to install to.");
54
+ return;
55
+ }
56
+ const instanceIds = instances.map((inst) => inst.id);
57
+ console.log(`\nInstalling app "${appName}" to ${instances.length} instance(s):`);
58
+ instances.forEach((inst) => {
59
+ console.log(` - ${inst.id}${inst.env_name ? ` (${inst.env_name})` : ""}${inst.name ? ` - ${inst.name}` : ""}`);
60
+ });
61
+ try {
62
+ const response = await axios.post(`${baseUrl}/api/v1/apps/${appName}/install/`, { instance_ids: instanceIds }, {
63
+ headers: {
64
+ accept: "application/json",
65
+ "content-type": "application/json",
66
+ "x-api-key": apiKey,
67
+ },
68
+ });
69
+ console.log("\nInstallation response:", response.data);
70
+ console.log(`✓ Successfully installed app to ${instances.length} instance(s)`);
71
+ }
72
+ catch (error) {
73
+ const axiosError = error;
74
+ console.error("\n✗ Error installing app:", axiosError?.response?.data?.detail ?? axiosError.message);
75
+ }
76
+ }
8
77
  export function createPublishAppCommand() {
9
78
  const program = new Command("publish-app");
10
79
  program
11
80
  .arguments("[folder]")
12
- .requiredOption("--api-key <apiKey>", "API Key for authentication")
81
+ .option("--api-key <apiKey>", "API Key for authentication")
13
82
  .option("--org <organization>", "Organization subdomain", "dev")
14
83
  .option("--increment-version", "Increment version in app.json")
15
84
  .option("--dry-run", "Perform a dry run without publishing")
16
85
  .option("--publish-url <url>", "Explicit publish URL")
86
+ .option("--install-to <instanceIds>", "Comma-separated list of instance IDs to install the app to after publishing")
87
+ .option("--install-to-env-name <envName>", "Environment name to filter instances and install the app to after publishing")
17
88
  .description('zip the contents of a folder and publish as an app to a pitcher org URL. for current folder: "p publish-app --api-key myapikey"')
18
89
  .action(async (folder = ".", options) => {
90
+ const apiKey = options.apiKey || process.env.NG_API_KEY;
91
+ if (!apiKey) {
92
+ console.error("Error: API key is required. Provide it via --api-key or set the NG_API_KEY environment variable.");
93
+ process.exit(1);
94
+ }
95
+ if (!options.publishUrl && !options.org)
96
+ options.org = "dev";
97
+ options.apiKey = apiKey;
19
98
  try {
20
99
  const folderPath = path.resolve(folder);
21
100
  if (options.incrementVersion) {
@@ -47,7 +126,8 @@ export function createPublishAppCommand() {
47
126
  console.log(`Zipped ${archive.pointer()} total bytes`);
48
127
  const formData = new FormData();
49
128
  formData.append("file", fs.createReadStream(zipFilePath));
50
- const publishUrl = options.publishUrl || `https://${options.org}.my.pitcher.com/api/v1/apps/publish/`;
129
+ const publishUrl = options.publishUrl ||
130
+ `https://${options.org}.my.pitcher.com/api/v1/apps/publish/`;
51
131
  if (options.dryRun) {
52
132
  console.log("Dry run mode enabled. The following data would be published:");
53
133
  console.log("Publish URL:", publishUrl);
@@ -67,6 +147,51 @@ export function createPublishAppCommand() {
67
147
  },
68
148
  });
69
149
  console.log("Publish response:", response.data);
150
+ const baseUrl = options.publishUrl
151
+ ? new URL(options.publishUrl).origin
152
+ : `https://${options.org}.my.pitcher.com`;
153
+ const appName = response.data?.app?.name;
154
+ if (!appName) {
155
+ console.warn("Could not determine app name from publish response");
156
+ }
157
+ else if (options.installTo || options.installToEnvName) {
158
+ console.log("\n--- Starting installation process ---");
159
+ const allInstances = await fetchAllInstances(baseUrl, options.apiKey);
160
+ if (allInstances.length === 0) {
161
+ console.warn("Cannot proceed with installation: no instances available");
162
+ }
163
+ else {
164
+ let targetInstances = [];
165
+ if (options.installTo) {
166
+ const requestedIds = options.installTo
167
+ .split(",")
168
+ .map((id) => id.trim())
169
+ .filter(Boolean);
170
+ console.log(`\nValidating ${requestedIds.length} requested instance ID(s)...`);
171
+ const { valid, invalid } = validateInstanceIds(allInstances, requestedIds);
172
+ if (invalid.length > 0) {
173
+ console.warn(`⚠ Warning: ${invalid.length} invalid instance ID(s) will be skipped:`);
174
+ invalid.forEach((id) => console.warn(` - ${id}`));
175
+ }
176
+ if (valid.length > 0) {
177
+ console.log(`✓ Found ${valid.length} valid instance(s)`);
178
+ targetInstances = [...targetInstances, ...valid];
179
+ }
180
+ }
181
+ if (options.installToEnvName) {
182
+ console.log(`\nFiltering instances by environment name: "${options.installToEnvName}"`);
183
+ const envInstances = filterInstancesByEnvName(allInstances, options.installToEnvName);
184
+ targetInstances = [...targetInstances, ...envInstances];
185
+ }
186
+ const uniqueInstances = Array.from(new Map(targetInstances.map((inst) => [inst.id, inst])).values());
187
+ if (uniqueInstances.length > 0) {
188
+ await installAppToInstances(baseUrl, options.apiKey, appName, uniqueInstances);
189
+ }
190
+ else {
191
+ console.log("\nNo valid instances found to install to.");
192
+ }
193
+ }
194
+ }
70
195
  }
71
196
  catch (e) {
72
197
  const error = e;
package/package.json CHANGED
@@ -1,13 +1,9 @@
1
1
  {
2
2
  "name": "@pitcher/cli-scripts",
3
- "version": "1.0.9",
3
+ "version": "2.0.0",
4
4
  "description": "The Pitcher CLI for various tasks in its ecosystem.",
5
5
  "author": "Pitcher AG",
6
6
  "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/PitcherAG/ng-scripts.git"
10
- },
11
7
  "keywords": [
12
8
  "pitcher",
13
9
  "cli",
@@ -27,14 +23,14 @@
27
23
  "dist"
28
24
  ],
29
25
  "devDependencies": {
30
- "@types/archiver": "^6.0.2",
31
- "@types/node": "^20.0.0",
32
- "typescript": "^5.4.5"
26
+ "@types/archiver": "^6.0.3",
27
+ "@types/node": "^22.18.0",
28
+ "typescript": "^5.9.2"
33
29
  },
34
30
  "dependencies": {
35
31
  "archiver": "^7.0.1",
36
- "axios": "^1.7.2",
37
- "commander": "^12.1.0",
38
- "form-data": "^4.0.0"
32
+ "axios": "^1.12.0",
33
+ "commander": "^13.1.0",
34
+ "form-data": "^4.0.2"
39
35
  }
40
36
  }
@@ -1,21 +0,0 @@
1
- ## To install dependencies:
2
-
3
- ```bash
4
- yarn install
5
- ```
6
-
7
- ## To work on it
8
-
9
- ```bash
10
- # at the root folder:
11
- npm link
12
- ```
13
-
14
- ## To use version from npm globally installed
15
-
16
- ```bash
17
- # anywhere:
18
- npm unlink @pitcher/cli-scripts
19
- # then
20
- npm install -g @pitcher/cli-scripts
21
- ```