@dotcms/dotcli 1.0.0-rc2 → 1.0.0-rc4

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
@@ -1,7 +1,19 @@
1
1
  # dotCMS CLI
2
- The dotCMS CLI is a command-line tool that you can use to populate and modify your dotCMS instances from a command shell.
2
+ The **dotCMS CLI**, sometimes shortened to **dotCLI**, is a standalone tool for interacting with a dotCMS instance through a command shell, allowing a wide array of automated operations and behaviors.
3
+
4
+ ## Getting Started
5
+
6
+ ### Installation
7
+
8
+ ### NPM
9
+
10
+ The simplest and most recommended way to get the dotCMS CLI is from its npm package:
11
+
12
+ ```shell script
13
+ npm install -g @dotcms/dotcli
14
+ ```
15
+ ### Manual JAR Download
3
16
 
4
- ## Quick start
5
17
  1. Download the CLI: The dotCMS CLI is delivered as an uber jar that can be downloaded from [here](https://repo.dotcms.com/artifactory/libs-snapshot-local/com/dotcms/dotcms-cli/).
6
18
  Once downloaded, you just need to run it with:
7
19
 
@@ -9,7 +21,7 @@ The dotCMS CLI is a command-line tool that you can use to populate and modify yo
9
21
  java -jar dotcli.jar
10
22
  ```
11
23
 
12
- 2. Configure the dotCMS instances you want to connect to using a dot-service.yml file. More details on how to do it [on this section](## CLI Instance Configuration). Make sure you make a site active in the yml file, otherwise you will have to active one using the [`instance` command](## Available Commands)
24
+ 2. Configure the dotCMS instances you want to connect to using the `config` command. More details on how to do it on the [Configuration](#Configuration) section.
13
25
 
14
26
  3. Log in to the selected instance
15
27
  ```shell script
@@ -22,6 +34,7 @@ java -jar dotcli.jar login --user={USER} --password
22
34
 
23
35
  | Command | Description |
24
36
  |--------------------------------------------|-------------------------------------------------------------------------------------|
37
+ | [config](cli/docs/config.adoc) | Sets the initial configuration required by all commands to operate |
25
38
  | [content-type](cli/docs/content-type.adoc) | Performs operations over content types. For example: pull, push, remove |
26
39
  | [files](cli/docs/files.adoc) | Performs operations over files. For example: tree, ls, push |
27
40
  | [instance](cli/docs/instance.adoc) | Prints a list of available dotCMS instances |
@@ -39,7 +52,21 @@ You can find more details about how to use the dotCMS CLI in the [Examples](#exa
39
52
 
40
53
  ## Examples
41
54
 
42
- 1. Log in with an admin user
55
+ 1. Run Configuration command to set the initial configuration required by all commands to operate
56
+ ```shell script
57
+ config
58
+ Enter the key/name that will serve to identify the dotCMS instance (must be unique) [local].
59
+ The name is [local]
60
+ Enter the dotCMS base URL (must be a valid URL starting protocol http or https) [http://localhost:8080]
61
+ The URL is [http://localhost:8080]
62
+ Are these values OK? (Enter to confirm or N to cancel) (Y/n)
63
+ ...
64
+ Do you want to continue adding another dotCMS instance? (Y/n)n
65
+ 0. Profile [local], Uri [http://localhost:8080], active [no].
66
+ 1. Profile [local#1], Uri [https://demo.dotcms.com], active [no].
67
+ One of these profiles needs to be made the current active one. Please select the number of the profile you want to activate. 1
68
+ ```
69
+ 2. Log in with an admin user
43
70
  ```shell script
44
71
  login --user=admin@dotCMS.com --password
45
72
  ```
@@ -181,39 +208,35 @@ Example:
181
208
  ../mvnw quarkus:dev -Dquarkus.log.file.path=/Users/my-user/CLI/dotcms-cli.log
182
209
  ```
183
210
 
184
- ## CLI Instance Configuration
211
+ ## Configuration
185
212
 
186
213
  The CLI can be used to manage multiple dotCMS instances. Each instance profile is defined in the `~/.dotcms/dot-service.yml` file.
187
- Whatever profile is active will be used by the CLI to execute the commands.
214
+ Whatever profile is active will be used by the CLI to execute the commands on
188
215
  The selected profile can be obtained by running the `status` command.
189
216
  Here's an example of the default `dot-service.yml` file shipped with the CLI:
190
217
 
191
218
  ```yaml
192
219
  - name: "default"
220
+ url: "http://localhost:8080"
193
221
  credentials:
194
222
  user: "admin@dotcms.com"
195
223
  - name: "demo"
224
+ url: "https://demo.dotcms.com"
196
225
  active: true
197
226
  credentials:
198
227
  user: "admin@dotCMS.com"
199
228
  ```
200
229
 
201
- The profiles declared on this file are paired up with properties defined in an internal `application.properties` file.
202
-
203
- ```properties
204
- # Your configuration properties
205
- dotcms.client.servers.default=http://localhost:8080/api
206
- dotcms.client.servers.demo=https://demo.dotcms.com/api
207
- ```
208
-
209
- Notice how the `dotcms.client.servers` property has a suffix matching the profile name in the `dot-service.yml` file.
230
+ Therefore, in order to add a new instance profile, you need to add a new entry in the `dot-service.yml` as it is shown on the example above.
231
+ The `active` attribute indicates which profile is currently active. The CLI will use the active profile to execute the commands.
232
+ If more than one profile is marked active, this will result in an InvalidStateException.
233
+ The `credentials` section is optional. If the credentials are not provided, the CLI will prompt the user to enter them when the `login` command is executed.
210
234
 
211
- Therefore, in order to add a new instance profile, you need to add a new entry in the `dot-service.yml` file and a new property extending the `application.properties` file.
212
- Application properties can be extended via system properties, environment variables, `.env` file or in `$PWD/config/application.properties` file.
235
+ If the `dot-service.yml` file does not exist, the CLI will prompt to create one No commands can operate without having a valid configuration in place.
213
236
 
214
- To learn more about how to extend the `application.properties` file see the Quarkus configuration guide [Here](https://es.quarkus.io/guides/config-reference#application-properties-file)
237
+ The CLI provides a `config` command to set the initial configuration required by all commands to operate. The command will guide you through the process of adding a new instance profile to the `dot-service.yml` file. and setting the active profile.
238
+ See the [Configuration](cli/docs/config.adoc) section for more details. And the [Examples](#examples) section for a practical example.
215
239
 
216
- In future versions this process will be facilitated by the CLI itself.
217
240
 
218
241
  ### Workspace
219
242
 
@@ -222,6 +245,7 @@ The workspace is basically a set of directories and files used to house and orga
222
245
  Additionally, a marker file called `.dot-workspace.yml` indicates to the CLI that the current directory is a valid workspace.
223
246
  In the following table you can see the different directories and files that conform a workspace.
224
247
 
248
+
225
249
  | File/Directory | Type | Description |
226
250
  |----------------------|------|-------------------------|
227
251
  | `content-types/` | Dir | Content-Types directory |
@@ -241,7 +265,7 @@ In order to incorporate the CLI into your GitHub Actions workflow, you need to:
241
265
  - In Your repository General Settings, enable the following permissions:
242
266
  - Workflow Permissions : Read and Write permissions
243
267
  - In Your repository General Settings, Secrets and variables, Actions
244
- - Create a new variable called `DOT_API_URL` and set the value to a valid dotCMS URL. e.g. `https://demo.dotcms.com/api`
268
+ - Create a new variable called `DOT_API_URL` and set the value to a valid dotCMS URL. e.g. `https://demo.dotcms.com`
245
269
  ![How to create a variable](doc_images/create_variable.png)
246
270
  - Create a new secret called `DOT_TOKEN` and set the value to a valid dotCMS CLI token.
247
271
  ![How to create a secret](doc_images/create_secret.png)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotcms/dotcli",
3
- "version": "1.0.0-rc2",
3
+ "version": "1.0.0-rc4",
4
4
  "scripts": {
5
5
  "postinstall": "node src/postinstall.js install",
6
6
  "postuninstall": "node src/postinstall.js uninstall && npm prune"
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
 
3
+ // Dependencies
3
4
  const path = require('path');
4
5
  const fs = require('fs').promises;
5
6
  const os = require('os');
6
7
 
8
+ // Architecture and platform mappings
7
9
  const ARCHITECTURE_MAPPING = {
8
10
  "x64": "x86_64",
9
11
  "arm64": "aarch_64"
@@ -14,26 +16,35 @@ const PLATFORM_MAPPING = {
14
16
  "linux": "linux"
15
17
  };
16
18
 
19
+ const EXTENSION_MAP = {
20
+ "win32": ".exe",
21
+ "default": ""
22
+ };
23
+
24
+ // Utility functions
17
25
  function getGlobalBinPath() {
18
26
  const npmGlobalPrefix = process.env.PREFIX || process.env.npm_config_prefix || process.env.HOME;
19
27
  return path.join(npmGlobalPrefix, 'bin');
20
28
  }
21
29
 
22
30
  function validatePackageConfig(packageJson) {
31
+ // Validation of package.json configuration
23
32
  if (!packageJson.version || !packageJson.packageName || !packageJson.alias || !packageJson.binaries || typeof packageJson.binaries !== "object") {
24
33
  throw new Error("Invalid package.json. 'version', 'packageName', 'alias' and 'binaries' must be specified.");
25
34
  }
26
35
  }
27
36
 
37
+ // Read and parse package.json
28
38
  async function parsePackageJson() {
29
-
30
39
  console.log("Installing CLI");
40
+
31
41
  const platform = os.platform();
32
42
  const architecture = os.arch();
33
43
 
34
44
  console.log("Platform: " + platform);
35
45
  console.log("Architecture: " + architecture);
36
46
 
47
+ // Check installation support for platform and architecture
37
48
  if (!(os.arch() in ARCHITECTURE_MAPPING) || !(os.platform() in PLATFORM_MAPPING)) {
38
49
  throw new Error(`Installation is not supported for this ${platform}/${architecture} combination.`);
39
50
  }
@@ -48,7 +59,7 @@ async function parsePackageJson() {
48
59
  const packageName = packageJson.packageName;
49
60
  const alias = packageJson.alias;
50
61
  const binaries = packageJson.binaries;
51
- const extension = platform === "win32" ? ".exe" : "";
62
+ const extension = EXTENSION_MAP[platform] || EXTENSION_MAP.default;
52
63
  const binaryKey = `${packageName}-${platform}-${architecture}`;
53
64
  const binaryPath = binaries[binaryKey];
54
65
 
@@ -69,76 +80,86 @@ async function parsePackageJson() {
69
80
  }
70
81
  }
71
82
 
83
+ // Create symlink for the binary
84
+ async function createSymlink(globalBinPath, config) {
85
+ try {
86
+ console.info(`Creating symlink for the relevant binary for your platform ${os.platform()}-${os.arch()}`);
72
87
 
73
- async function createSymlink(binarySource, binaryDestination) {
74
- const globalBinPath = getGlobalBinPath();
75
- const symlinkPath = path.join(globalBinPath, binaryDestination);
88
+ const currentDir = __dirname;
89
+ const targetDir = path.join(currentDir, '..');
90
+ const binarySource = path.join(targetDir, config.binaryPath);
91
+ const binaryDestination = config.alias;
92
+ const fullSymlinkPath = path.join(globalBinPath, binaryDestination);
76
93
 
77
- try {
78
- try {
79
- await fs.access(symlinkPath, fs.constants.F_OK);
80
- // If the symlink exists, remove it.
81
- await fs.unlink(symlinkPath);
82
- console.log(`Existing symlink ${symlinkPath} found and removed.`);
83
- } catch (error) {
84
- // The symlink does not exist, continue.
85
- }
94
+ await fs.symlink(binarySource, fullSymlinkPath);
86
95
 
87
- if (os.platform() === "win32") {
88
- // Create a junction for the binary for Windows.
89
- // await fs.symlink(binarySource, symlinkPath, "junction");
90
- } else {
91
- // Create a symlink for the binary for macOS and Linux.
92
- await fs.symlink(binarySource, symlinkPath);
93
- }
94
- console.info(`Created symlink ${symlinkPath} pointing to ${binarySource}`);
96
+ console.info(`Created symlink ${fullSymlinkPath} pointing to ${binarySource}`);
95
97
  } catch (error) {
96
98
  console.error("Error while creating symlink:", error);
97
99
  throw new Error("Failed to create symlink.");
98
100
  }
99
101
  }
100
102
 
101
- async function installCli() {
102
- const config = await parsePackageJson();
103
+ // Remove symlink if exists
104
+ async function removeSymlinkIfExists(globalBinPath, config) {
105
+ try {
106
+ console.info("Global bin path location:", globalBinPath);
103
107
 
104
- console.log({
105
- config
106
- });
108
+ const files = await fs.readdir(globalBinPath);
109
+ const symlinkFileName = config.alias + config.extension;
110
+ const symlinkPath = files.find(file => file === symlinkFileName);
107
111
 
108
- console.info(`Creating symlink for the relevant binary for your platform ${os.platform()}-${os.arch()}`);
112
+ if (!symlinkPath) {
113
+ console.warn(`Symlink '${symlinkFileName}' not found in the global bin directory.`);
114
+ return;
115
+ }
109
116
 
110
- const currentDir = __dirname;
111
- const targetDir = path.join(currentDir, '..');
112
- const binarySource = path.join(targetDir, config.binaryPath);
113
- const binaryDestination = config.alias;
117
+ const fullSymlinkPath = path.join(globalBinPath, symlinkPath);
118
+ await fs.unlink(fullSymlinkPath);
119
+ console.info(`Removed symlink: ${fullSymlinkPath}`);
120
+ } catch (error) {
121
+ console.warn("Error while removing symlink:", error);
122
+ }
123
+ }
114
124
 
115
- console.info("Installing cli:", binarySource, binaryDestination);
125
+ // Install CLI
126
+ async function installCli() {
127
+ const config = await parsePackageJson();
128
+ const globalBinPath = getGlobalBinPath();
116
129
 
117
- await createSymlink(binarySource, binaryDestination + config.extension);
130
+ try{
131
+ await removeSymlinkIfExists(globalBinPath, config);
132
+ await createSymlink(globalBinPath, config);
133
+ } catch (ex) {
134
+ console.error("Error while installing:", ex);
135
+ throw new Error(`Failed to install ${config.alias}.`);
136
+ }
137
+
138
+ console.info(`${config.alias} installed successfully.`);
118
139
  }
119
140
 
141
+ // Uninstall CLI
120
142
  async function uninstallCli() {
121
143
  const config = await parsePackageJson();
144
+ const globalBinPath = getGlobalBinPath();
122
145
 
123
146
  try {
124
- const globalBinPath = getGlobalBinPath();
125
- const symlinkPath = path.join(globalBinPath, config.alias + config.extension);
126
-
127
- console.info("Removing symlink:", symlinkPath);
128
-
129
- await fs.unlink(symlinkPath);
147
+ await removeSymlinkIfExists(globalBinPath, config);
130
148
  } catch (ex) {
131
149
  console.error("Error while uninstalling:", ex);
150
+ throw new Error(`Failed to uninstall ${config.alias}.`);
132
151
  }
133
152
 
134
- console.info("Uninstalled cli successfully");
153
+ console.info(`${config.alias} uninstalled successfully.`);
135
154
  }
136
155
 
156
+ // Available actions
137
157
  const actions = {
138
158
  "install": installCli,
139
159
  "uninstall": uninstallCli
140
160
  };
141
161
 
162
+ // Execute action based on provided command
142
163
  const [cmd] = process.argv.slice(2);
143
164
  if (cmd && actions[cmd]) {
144
165
  actions[cmd]().then(
@@ -151,4 +172,4 @@ if (cmd && actions[cmd]) {
151
172
  } else {
152
173
  console.log("Invalid command. `install` and `uninstall` are the only supported commands");
153
174
  process.exit(1);
154
- }
175
+ }