@jacebenson/jsn 4.3.7-dev.23 → 4.3.7-dev.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "4.3.7-dev.23",
3
+ "version": "4.3.7-dev.24",
4
4
  "description": "A command-line interface for ServiceNow that follows the Unix philosophy: simple, composable, and scriptable.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -250,17 +250,15 @@ export function midserversCmd(wrap) {
250
250
  ]
251
251
  : dockerStatus.installed && dockerStatus.compose
252
252
  ? [
253
- { name: 'Fix Docker daemon access first', value: 'docker-unavailable' },
253
+ { name: 'Prepare files now, run Docker later', value: 'prepare' },
254
254
  { name: 'Direct install (not available yet)', value: 'direct' },
255
255
  ]
256
256
  : [
257
- { name: 'Install Docker first', value: 'install-docker' },
257
+ { name: 'Prepare files now, run Docker later', value: 'prepare' },
258
258
  { name: 'Direct install (not available yet)', value: 'direct' },
259
259
  ],
260
260
  });
261
261
  if (installMethod === 'direct') throw new Error('Direct MID Server installation is not implemented yet. Choose Docker.');
262
- if (installMethod === 'docker-unavailable') throw new Error('Docker is installed, but this user cannot access the Docker daemon. Start Docker and grant this user access to /var/run/docker.sock, then run this command again.');
263
- if (installMethod === 'install-docker') throw new Error('Automatic Docker installation is not implemented yet. Install Docker and Compose, then run this command again.');
264
262
 
265
263
  const installation = await installDockerMidServer({
266
264
  instanceURL: app.getEffectiveInstance(),
@@ -269,7 +267,12 @@ export function midserversCmd(wrap) {
269
267
  name,
270
268
  releaseName: credentials.releaseName,
271
269
  directory: argv.directory,
270
+ runNow: installMethod !== 'prepare',
272
271
  });
272
+ if (installation.prepared) {
273
+ app.ok(installation, { summary: `Prepared MID Server files in ${installation.installDir}. Run: ${installation.runCommand}` });
274
+ return;
275
+ }
273
276
  let record;
274
277
  try {
275
278
  record = await waitForMidServerUp(app, installation.name, Math.max(1, argv['wait-timeout']), Math.max(500, argv['wait-interval']));
@@ -77,7 +77,16 @@ export function dockerComposeAvailable(run = defaultRun) {
77
77
  }
78
78
 
79
79
  function composeFile({ serviceName }) {
80
- return `services:\n ${serviceName}:\n container_name: ${serviceName}\n image: ${serviceName}:\${MID_RELEASE}\n restart: unless-stopped\n volumes:\n - ./export:/opt/snc_mid_server/agent/export\n environment:\n MID_INSTANCE_URL: \${MID_INSTANCE_URL}\n MID_INSTANCE_USERNAME: \${MID_INSTANCE_USERNAME}\n MID_INSTANCE_PASSWORD: \${MID_INSTANCE_PASSWORD}\n MID_SERVER_NAME: \${MID_SERVER_NAME}\n`;
80
+ return `services:\n ${serviceName}:\n container_name: ${serviceName}\n build: ./recipe\n image: ${serviceName}:\${MID_RELEASE}\n restart: unless-stopped\n volumes:\n - ./export:/opt/snc_mid_server/agent/export\n environment:\n MID_INSTANCE_URL: \${MID_INSTANCE_URL}\n MID_INSTANCE_USERNAME: \${MID_INSTANCE_USERNAME}\n MID_INSTANCE_PASSWORD: \${MID_INSTANCE_PASSWORD}\n MID_SERVER_NAME: \${MID_SERVER_NAME}\n`;
81
+ }
82
+
83
+ function envFile({ instanceURL, username, password, displayName, releaseName }) {
84
+ return `MID_RELEASE=${releaseName}\nMID_INSTANCE_URL=${String(instanceURL).replace(/\/$/, '')}/\nMID_INSTANCE_USERNAME=${username}\nMID_INSTANCE_PASSWORD=${password}\nMID_SERVER_NAME=${displayName}\n`;
85
+ }
86
+
87
+ function instanceName(instanceURL) {
88
+ const hostname = new URL(instanceURL).hostname;
89
+ return hostname.replace(/\.service-now\.com$|\.servicenowservices\.com$/i, '').replace(/[^A-Za-z0-9._-]/g, '_');
81
90
  }
82
91
 
83
92
  export async function installDockerMidServer({
@@ -87,17 +96,17 @@ export async function installDockerMidServer({
87
96
  name,
88
97
  directory,
89
98
  releaseName: suppliedReleaseName,
99
+ runNow = true,
90
100
  fetchImpl = fetch,
91
101
  run = defaultRun,
92
102
  }) {
93
- if (!dockerComposeAvailable(run)) {
94
- throw new Error('Docker and Docker Compose are required for Docker MID Server installation');
95
- }
103
+ const dockerAvailable = detectDocker(run).available;
96
104
  const shortName = normalizeMidName(name);
97
105
  const displayName = `mid.server.${shortName}`;
98
106
  const releaseName = suppliedReleaseName || (await validateMidCredentials(instanceURL, username, password, fetchImpl)).releaseName;
99
107
  const recipeURL = buildMidRecipeUrl(releaseName);
100
- const installDir = directory || path.join(os.homedir(), '.local', 'share', 'jsn', 'midservers', shortName);
108
+ const serviceNowInstance = instanceName(instanceURL);
109
+ const installDir = directory || path.join(os.homedir(), '.servicenow', 'mids', serviceNowInstance, shortName);
101
110
  fs.mkdirSync(installDir, { recursive: true, mode: 0o700 });
102
111
  const exportDir = path.join(installDir, 'export');
103
112
  fs.mkdirSync(exportDir, { recursive: true, mode: 0o700 });
@@ -110,9 +119,8 @@ export async function installDockerMidServer({
110
119
  fs.mkdirSync(recipeDir, { recursive: true, mode: 0o700 });
111
120
  run('unzip', ['-q', archive, '-d', recipeDir]);
112
121
  const composePath = path.join(installDir, 'docker-compose.yaml');
122
+ const persistedRecipeDir = path.join(installDir, 'recipe');
113
123
  const serviceName = `jsn_mid_${shortName.replace(/[^A-Za-z0-9_.-]/g, '_')}`;
114
- run('docker', ['build', '--tag', `${serviceName}:${releaseName}`, recipeDir]);
115
- fs.writeFileSync(composePath, composeFile({ serviceName, instanceURL, displayName }), { mode: 0o600 });
116
124
  const env = {
117
125
  MID_RELEASE: releaseName,
118
126
  MID_INSTANCE_URL: `${String(instanceURL).replace(/\/$/, '')}/`,
@@ -120,6 +128,22 @@ export async function installDockerMidServer({
120
128
  MID_INSTANCE_PASSWORD: password,
121
129
  MID_SERVER_NAME: displayName,
122
130
  };
131
+ fs.rmSync(persistedRecipeDir, { recursive: true, force: true });
132
+ fs.cpSync(recipeDir, persistedRecipeDir, { recursive: true });
133
+ fs.writeFileSync(composePath, composeFile({ serviceName }), { mode: 0o600 });
134
+ fs.writeFileSync(path.join(installDir, '.env'), envFile({ instanceURL, username, password, displayName, releaseName }), { mode: 0o600 });
135
+ if (!dockerAvailable || !runNow) {
136
+ return {
137
+ name: displayName,
138
+ releaseName,
139
+ installDir,
140
+ composePath,
141
+ serviceName,
142
+ prepared: true,
143
+ runCommand: `docker compose -f ${composePath} up -d`,
144
+ };
145
+ }
146
+ run('docker', ['build', '--tag', `${serviceName}:${releaseName}`, persistedRecipeDir]);
123
147
  run('docker', ['compose', '-f', composePath, 'up', '-d'], { cwd: installDir, env });
124
148
  return {
125
149
  name: displayName,