@travetto/pack 3.2.4 → 3.2.6

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
@@ -134,6 +134,7 @@ Options:
134
134
  -dp, --docker-port <number> Docker Image Port (default: [])
135
135
  -dx, --docker-push Docker Push Tags (default: false)
136
136
  -dr, --docker-registry <string> Docker Registry
137
+ -du, --docker-runtime-user <string> Docker Runtime user
137
138
  -m, --module <string> Module to run for
138
139
  -h, --help display help for command
139
140
  ```
@@ -231,6 +232,9 @@ cd $ROOT
231
232
  echo "Generating Docker File $DIST/Dockerfile @travetto/pack/support/pack.dockerfile"
232
233
 
233
234
  echo "FROM node:20-alpine" > $DIST/Dockerfile
235
+ echo "" >> $DIST/Dockerfile
236
+ echo "RUN addgroup -g 2000 app && adduser -S -u 2000 app app" >> $DIST/Dockerfile
237
+ echo "USER app" >> $DIST/Dockerfile
234
238
  echo "WORKDIR /app" >> $DIST/Dockerfile
235
239
  echo "COPY . ." >> $DIST/Dockerfile
236
240
  echo "" >> $DIST/Dockerfile
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/pack",
3
- "version": "3.2.4",
3
+ "version": "3.2.6",
4
4
  "description": "Code packing utilities",
5
5
  "keywords": [
6
6
  "travetto",
@@ -1,12 +1,12 @@
1
1
  import fs from 'fs/promises';
2
2
 
3
3
  import { path, RootIndex } from '@travetto/manifest';
4
- import { ExecUtil } from '@travetto/base';
5
4
  import { cliTpl } from '@travetto/cli';
6
5
 
7
6
  import { ActiveShellCommand } from './shell';
8
7
  import { DockerPackConfig, DockerPackFactoryModule } from './types';
9
8
  import { PackOperation } from './operation';
9
+ import { PackUtil } from './util';
10
10
 
11
11
  export class DockerPackOperation {
12
12
 
@@ -47,7 +47,7 @@ export class DockerPackOperation {
47
47
  if (cfg.ejectFile) {
48
48
  yield command;
49
49
  } else {
50
- await ExecUtil.spawn(command[0], command.slice(1), {}).result;
50
+ await PackUtil.runCommand(command);
51
51
  }
52
52
  }
53
53
 
@@ -64,7 +64,7 @@ export class DockerPackOperation {
64
64
  yield cmd;
65
65
  yield ActiveShellCommand.chdir(path.cwd());
66
66
  } else {
67
- await ExecUtil.spawn(cmd[0], cmd.slice(1), { cwd: cfg.workspace, stdio: [0, 'pipe', 2] }).result;
67
+ await PackUtil.runCommand(cmd, { cwd: cfg.workspace, stdio: [0, 'pipe', 2] });
68
68
  }
69
69
  }
70
70
 
@@ -86,7 +86,7 @@ export class DockerPackOperation {
86
86
  }
87
87
  } else {
88
88
  for (const tag of tags) {
89
- await ExecUtil.spawn(cmd[0], [...cmd.slice(1), tag], { stdio: [0, 'pipe', 2] }).result;
89
+ await PackUtil.runCommand([...cmd, tag]);
90
90
  }
91
91
  }
92
92
  }
@@ -1,7 +1,6 @@
1
1
  import fs from 'fs/promises';
2
2
 
3
3
  import { path, RootIndex } from '@travetto/manifest';
4
- import { ExecUtil } from '@travetto/base';
5
4
  import { cliTpl } from '@travetto/cli';
6
5
 
7
6
  import { CommonPackConfig } from './types';
@@ -88,7 +87,7 @@ export class PackOperation {
88
87
  yield bundleCommand;
89
88
  yield ActiveShellCommand.chdir(path.cwd());
90
89
  } else {
91
- await ExecUtil.spawn(bundleCommand[0], bundleCommand.slice(1), { cwd, env, stdio: ['inherit', 'pipe', 'pipe'] }).result;
90
+ await PackUtil.runCommand(bundleCommand, { cwd, env });
92
91
  }
93
92
  }
94
93
 
@@ -211,7 +210,7 @@ export class PackOperation {
211
210
  if (cfg.ejectFile) {
212
211
  yield [...Object.entries(env).map(([k, v]) => `${k}=${v}`), ...cmd];
213
212
  } else {
214
- await ExecUtil.spawn(cmd[0], cmd.slice(1), { env, stdio: ['inherit', 'ignore', 'inherit'] }).result;
213
+ await PackUtil.runCommand(cmd, { env });
215
214
  }
216
215
  }
217
216
 
@@ -229,8 +228,7 @@ export class PackOperation {
229
228
  yield ActiveShellCommand.chdir(path.cwd());
230
229
  } else {
231
230
  await fs.mkdir(path.dirname(cfg.output), { recursive: true });
232
- const [cmd, ...args] = ActiveShellCommand.zip(cfg.output);
233
- await ExecUtil.spawn(cmd, args, { cwd: cfg.workspace }).result;
231
+ await PackUtil.runCommand(ActiveShellCommand.zip(cfg.output), { cwd: cfg.workspace });
234
232
  }
235
233
  }
236
234
  }
@@ -25,6 +25,12 @@ export type DockerPackConfig = {
25
25
  dockerPort?: number[];
26
26
  dockerPush?: boolean;
27
27
  dockerRegistry?: string;
28
+ dockerRuntimeUser: {
29
+ user: string;
30
+ uid: number;
31
+ group: string;
32
+ gid: number;
33
+ };
28
34
  } & CommonPackConfig;
29
35
 
30
36
 
@@ -1,9 +1,10 @@
1
1
  import fs from 'fs/promises';
2
2
 
3
3
  import { path, RootIndex } from '@travetto/manifest';
4
- import { ExecUtil } from '@travetto/base';
4
+ import { AppError, ExecUtil, ExecutionOptions } from '@travetto/base';
5
5
 
6
6
  import { ActiveShellCommand } from './shell';
7
+ import { DockerPackConfig } from './types';
7
8
 
8
9
  export class PackUtil {
9
10
  /**
@@ -62,4 +63,37 @@ export class PackUtil {
62
63
 
63
64
  await stream?.close();
64
65
  }
66
+
67
+ /**
68
+ * Track result response
69
+ */
70
+ static async runCommand(cmd: string[], opts: ExecutionOptions = {}): Promise<void> {
71
+ const { valid, code, stderr, message } = await ExecUtil.spawn(cmd[0], cmd.slice(1), {
72
+ stdio: [0, 'pipe', 'pipe', 'ipc'],
73
+ ...opts,
74
+ catchAsResult: true
75
+ }).result;
76
+ if (!valid) {
77
+ process.exitCode = code;
78
+ throw new AppError(stderr || message || 'An unexpected error has occurred');
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Generate a docker user creation command
84
+ */
85
+ static generateDockerUserCommand(cfg: DockerPackConfig): string {
86
+ const { user, group, uid, gid } = cfg.dockerRuntimeUser;
87
+ if (user !== 'root') {
88
+ return [
89
+ '',
90
+ `RUN which addgroup && \
91
+ ( addgroup -g ${gid} ${group} && adduser -S -u ${uid} ${user} ${group} ) || \
92
+ ( groupadd -g ${gid} ${group} && useradd -r -u ${uid} -g ${group} ${user} )`,
93
+ `USER ${user}`
94
+ ].join('\n');
95
+ } else {
96
+ return '';
97
+ }
98
+ }
65
99
  }
@@ -4,8 +4,11 @@ import { CliCommand, CliFlag, CliUtil, CliValidationError } from '@travetto/cli'
4
4
  import { DockerPackOperation } from './bin/docker-operation';
5
5
  import { BasePackCommand, PackOperationShape } from './pack.base';
6
6
  import { GlobalEnv } from '@travetto/base';
7
+ import { Ignore } from '@travetto/schema';
7
8
 
8
9
  const NODE_MAJOR = GlobalEnv.nodeVersion.replace('v', '').split('.')[0];
10
+ const DEFAULT_USER_ID = 2000;
11
+ const DEFAULT_USER = 'app';
9
12
 
10
13
  /**
11
14
  * Standard docker support for pack
@@ -26,6 +29,16 @@ export class PackDockerCommand extends BasePackCommand {
26
29
  dockerPush = false;
27
30
  @CliFlag({ desc: 'Docker Registry ', short: 'dr', envVars: ['PACK_DOCKER_REGISTRY'] })
28
31
  dockerRegistry?: string;
32
+ @CliFlag({ desc: 'Docker Runtime user ', short: 'du', name: 'docker-runtime-user', envVars: ['PACK_DOCKER_RUNTIME_USER'] })
33
+ dockerRuntimeUserSrc?: string;
34
+
35
+ @Ignore()
36
+ dockerRuntimeUser: {
37
+ user: string;
38
+ uid: number;
39
+ group: string;
40
+ gid: number;
41
+ };
29
42
 
30
43
  async validate(...unknownArgs: string[]): Promise<CliValidationError[] | undefined> {
31
44
  const errs: CliValidationError[] = [];
@@ -47,6 +60,17 @@ export class PackDockerCommand extends BasePackCommand {
47
60
  this.dockerFactory = RootIndex.getFromSource(path.resolve(this.dockerFactory))?.import ?? this.dockerFactory;
48
61
  }
49
62
  this.dockerName = this.dockerName.replace('<module>', CliUtil.getSimpleModuleName(this.module ?? ''));
63
+
64
+ // Finalize user/group and ids
65
+ const [userOrUid, groupOrGid = userOrUid] = (this.dockerRuntimeUserSrc ?? '').split(':');
66
+ const groupIsNum = /^\d+$/.test(groupOrGid);
67
+ const userIsNum = /^\d+$/.test(userOrUid);
68
+
69
+ const uid = userIsNum ? +userOrUid : DEFAULT_USER_ID;
70
+ const gid = groupIsNum ? +groupOrGid : DEFAULT_USER_ID;
71
+ const group = (!groupIsNum ? groupOrGid : undefined) || DEFAULT_USER;
72
+ const user = (!userIsNum ? userOrUid : undefined) || DEFAULT_USER;
73
+ this.dockerRuntimeUser = { user, uid, group, gid };
50
74
  }
51
75
 
52
76
  getOperations(): PackOperationShape<this>[] {
@@ -1,7 +1,9 @@
1
1
  import { DockerPackFactory } from './bin/types';
2
+ import { PackUtil } from './bin/util';
2
3
 
3
4
  export const factory: DockerPackFactory = cfg => `
4
5
  FROM ${cfg.dockerImage}
6
+ ${PackUtil.generateDockerUserCommand(cfg)}
5
7
  WORKDIR /app
6
8
  COPY . .
7
9
  ${cfg.dockerPort?.map(port => `EXPOSE ${port}`).join('\n') ?? ''}