@leverege/build-tools 2.52.0-beta.2 → 2.52.0-pedro.10

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": "@leverege/build-tools",
3
- "version": "2.52.0-beta.2",
3
+ "version": "2.52.0-pedro.10",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -47,6 +47,7 @@
47
47
  "pull-git": "src/pull-git.sh",
48
48
  "push-my-chart": "src/push-my-chart.mjs",
49
49
  "refresh-npm-token": "src/refresh-npm-token.mjs",
50
+ "refresh-py-idx": "src/refresh-py-idx.mjs",
50
51
  "send-to-slack": "src/send-to-slack.mjs",
51
52
  "tag-release": "src/tag-release.mjs",
52
53
  "unleash": "src/unleash.mjs"
@@ -61,10 +62,10 @@
61
62
  "command-line-usage": "^7.0.3",
62
63
  "deepmerge": "^4.3.1",
63
64
  "enquirer": "^2.4.1",
64
- "execa": "^9.4.0",
65
+ "execa": "^9.3.1",
65
66
  "glob": "^11.0.0",
66
67
  "handlebars": "^4.7.8",
67
- "inquirer": "^11.0.2",
68
+ "inquirer": "^10.2.2",
68
69
  "js-yaml": "^4.1.0",
69
70
  "ms": "^2.1.3",
70
71
  "npm-registry-fetch": "^17.1.0",
@@ -73,11 +74,11 @@
73
74
  "read-pkg": "^9.0.1",
74
75
  "readline-sync": "^1.4.10",
75
76
  "semver": "^7.6.3",
76
- "simple-git": "^3.27.0",
77
- "zx": "^8.1.7"
77
+ "simple-git": "^3.26.0",
78
+ "zx": "^8.1.6"
78
79
  },
79
80
  "devDependencies": {
80
81
  "@leverege/eslint-config-leverege": "^4.2.0",
81
82
  "npm": "^10.8.3"
82
83
  }
83
- }
84
+ }
package/src/Docker.mjs CHANGED
@@ -40,12 +40,8 @@ COPY ./.npmrc /usr/src/app/.npmrc
40
40
  RUN apk --no-cache add openssh-client && \
41
41
  apk --update add --no-cache --virtual build-dep g++ gcc libgcc \\
42
42
  libstdc++ linux-headers make {{apkadds}} && \
43
- npm install -g npm@10
44
-
45
- {{preInstallPluginfile}}
46
-
47
- # Removed --ignore-scripts from npm ci because it was causing issues with python native build
48
- RUN npm ci --only=production --no-optional {{npmlogging}} && \
43
+ npm install -g npm@10 && \
44
+ npm ci --only=production --ignore-scripts --no-optional {{npmlogging}} && \
49
45
  rm -f /usr/src/app/.npmrc /root/.ssh/*
50
46
 
51
47
  # --------------------------------------------------------------
@@ -71,7 +67,7 @@ COPY --chown=node:node --from=intermediate /usr/src/app /usr/src/app
71
67
 
72
68
  USER {{runuser}}
73
69
  COPY ./bashrc /home/node/.bashrc
74
- CMD [ "/bin/bash", "-c", "source /home/node/.bashrc && node index.js" ]`
70
+ CMD [ "/bin/bash", "-c", "node index.js" ]`
75
71
 
76
72
  const pluginTemplate = `
77
73
  # Dockerfile.plugin - optionally extend the service Docker image
@@ -136,19 +132,6 @@ dunm() {
136
132
  du -sh /usr/src/app/node_modules/* | sort -sh
137
133
  }
138
134
  `
139
- const cloudBuildSteps = `
140
- steps:
141
- - name: 'gcr.io/cloud-builders/docker'
142
- args:
143
- - 'build'
144
- - '--network=cloudbuild'
145
- - '-t'
146
- - '{{imageName}}:{{imageVersion}}'
147
- - '.'
148
-
149
- images:
150
- - '{{imageName}}:{{imageVersion}}'
151
- `
152
135
 
153
136
  const defaultSettings = {
154
137
  apkadds : '',
@@ -156,7 +139,6 @@ const defaultSettings = {
156
139
  nodeimage : 'iron-alpine',
157
140
  npmlogging : '--silent',
158
141
  pluginfile : '# NO PLUGIN',
159
- preInstallPluginfile : '# NO PRE-INSTALL PLUGIN',
160
142
  regvers : 'package.version',
161
143
  runuser : 'node',
162
144
  }
@@ -186,14 +168,8 @@ const generateDockerfile = ( settings = defaultSettings ) => {
186
168
 
187
169
  const bashrcFile = './docker/bashrc'
188
170
  if ( !fs.existsSync( bashrcFile ) ) {
189
- let bf = bashrcTemplate
190
- const bashrcFilePlugin = './docker/Bashrc.plugin'
191
- if ( fs.existsSync( bashrcFilePlugin ) ) {
192
- const bashrcAdditions = fs.readFileSync( bashrcFilePlugin )
193
- bf = `${bf}\n${bashrcAdditions}`
194
- }
195
171
  warning( `creating the missing ${bashrcFile} file in the docker directory` )
196
- fs.writeFileSync( bashrcFile, bf )
172
+ fs.writeFileSync( bashrcFile, bashrcTemplate )
197
173
  }
198
174
 
199
175
  const dockerfilePlugin = './docker/Dockerfile.plugin'
@@ -218,12 +194,7 @@ const generateDockerfile = ( settings = defaultSettings ) => {
218
194
  errorExit( `***ERROR: something went wrong with ${dockerfilePlugin} plugin creation` )
219
195
  }
220
196
 
221
- const dockerfilePrePlugin = './docker/DockerfilePreInstall.plugin'
222
- if ( fs.existsSync( dockerfilePrePlugin ) ) {
223
- defaultSettings.preInstallPluginfile = fs.readFileSync( dockerfilePrePlugin )
224
- }
225
-
226
- const compiled = handlebars.compile( dockerfileTemplate, { noEscape : true } )
197
+ const compiled = handlebars.compile( dockerfileTemplate )
227
198
  const replaced = compiled( settings )
228
199
  // temporarily write the Dockerfile to the docker subdir - the container
229
200
  // image builder will then move it over to the build subdir
@@ -255,10 +226,8 @@ const validateCloudBuildBucket = async ( artifactProject ) => {
255
226
  }
256
227
 
257
228
  const buildContainerImage = async ( {
258
- artifactProject, artifactRegistry, containerName,
259
- imageVersion, isNpmWorkspace, options
229
+ artifactProject, artifactRegistry, containerName, imageVersion, isNpmWorkspace,
260
230
  } ) => {
261
- const { useCloudBuildFile } = options || {}
262
231
  const buildWorkspace = './build/workspace' // passed to Cloud Build
263
232
  const imageName = `${artifactRegistry}/images/${containerName}`
264
233
 
@@ -287,24 +256,14 @@ const buildContainerImage = async ( {
287
256
  const { fullName : builderName } = await getGcpAccount()
288
257
  const gcloudBuild = `time gcloud builds submit --project ${artifactProject}`
289
258
  const gcloudLogs = `--gcs-log-dir ${formCloudBuildBucketName( artifactProject )}/log`
290
- let gcloudTags = `--tag ${imageName}:${imageVersion}`
291
-
292
- if ( useCloudBuildFile ) {
293
- const cloudBuildFile = './docker/cloudbuild.yaml'
294
- gcloudTags = `--config=${cloudBuildFile}`
295
- const cbst = handlebars.compile( cloudBuildSteps, { noEscape : true } )
296
- const cbs = cbst( { artifactProject, artifactRegistry, imageName, imageVersion } )
297
-
298
- fs.writeFileSync( cloudBuildFile, cbs )
299
- }
259
+ const gcloudTags = `--tag ${imageName}:${imageVersion}`
300
260
 
301
261
  log( chalk.green.bold( `
302
- *** Submitting Build as ${builderName} ***
303
- ${gcloudBuild} \\
304
- ${gcloudLogs} \\
305
- ${gcloudTags}
306
- ` ) )
307
-
262
+ *** Submitting Build as ${builderName} ***
263
+ ${gcloudBuild} \\
264
+ ${gcloudLogs} \\
265
+ ${gcloudTags}
266
+ ` ) )
308
267
 
309
268
  if ( process.env.DRY_RUN === '1' ) {
310
269
  log( '\n***Exiting from DRY_RUN\n' )
package/src/Utils.mjs CHANGED
@@ -9,7 +9,7 @@ import YAML from 'js-yaml'
9
9
 
10
10
  let debugEnabled = process.env.BUILD_TOOLS_DEBUG === '1'
11
11
 
12
- export const disableDebug = () => { debugEnabled = false }
12
+ export const disableDebug = () => debugEnabled = false
13
13
 
14
14
  /* eslint-disable no-console */
15
15
  export const err = console.error
@@ -135,10 +135,11 @@ const checkAndAnnounce = () => {
135
135
  checkForLatest()
136
136
  .then( ( latestVersion ) => {
137
137
  if ( semverLt( thisVersion, latestVersion ) ) {
138
- console.log( chalk.white( '\n' +
139
- ` Update available ${chalk.yellow( thisVersion )} \u2b62 ${chalk.green( latestVersion )}\n` +
140
- ` Run ${chalk.cyan( 'npm i -g @leverege/build-tools' )} to update\n`
141
- ) )
138
+ console.log( chalk.white`
139
+ Update available ${chalk.grey( thisVersion )} \u2b62 ${chalk.green( latestVersion )}
140
+ Run ${chalk.cyan( 'npm i -g @leverege/build-tools' )} to update
141
+ `
142
+ )
142
143
  process.exit( 1 )
143
144
  } else {
144
145
  process.exit( 0 )
@@ -26,22 +26,8 @@ if ( refreshErr && !process.env.BUILD_TOOLS_DEBUG ) {
26
26
  log( refreshErr ) // most likely the token refreshed message
27
27
  }
28
28
 
29
- const options = {}
30
- let imageVersion = null
31
29
  // Expected to be invoked like docker-to-registry v1.2.3
32
- for ( let n = 2; n < process.argv.length; n++ ) {
33
- if ( process.argv[n].startsWith( '-' ) ) {
34
- const str = process.argv[n].slice( 1 )
35
- if ( str.indexOf( '=' ) > 0 ) {
36
- const [key, value] = str.split( '=' )
37
- options[key] = value
38
- } else {
39
- options[str] = true
40
- }
41
- } else {
42
- imageVersion = process.argv[n]
43
- }
44
- }
30
+ const imageVersion = process.argv[2]
45
31
  if ( !imageVersion ) {
46
32
  errorExit( '***Error: docker-to-registry requires an image version' )
47
33
  }
@@ -165,41 +151,39 @@ if ( giveGuidance ) {
165
151
  }
166
152
 
167
153
  // do chart checks too
168
- if ( options?.helmChecks !== 'false' ) {
169
- const chart = helmChart?.yaml['Chart.yaml']
170
- const values = helmChart?.yaml['values.yaml']
171
- const expectedChartRegistry = `${artifactRegistry}/images`
172
-
173
- if ( expectedChartRegistry !== values.image?.registry ) {
174
- log( `
175
- ${chalk.red.bold( '***Error: mismatched package.json registry and helm/values.yaml' )}
154
+ const chart = helmChart?.yaml['Chart.yaml']
155
+ const values = helmChart?.yaml['values.yaml']
156
+ const expectedChartRegistry = `${artifactRegistry}/images`
176
157
 
177
- The registry specified in the package.json leverege stanza does not line up
178
- with the image registry in helm/values.yaml. The naming convention expects
179
- the '/images' suffix to be added to the package.json registry string and then
180
- stored as the image.registry in the helm/values.yaml file.
158
+ if ( expectedChartRegistry !== values.image?.registry ) {
159
+ log( `
160
+ ${chalk.red.bold( '***Error: mismatched package.json registry and helm/values.yaml' )}
181
161
 
182
- Current settings:
183
- package.json registry => ${chalk.yellow.bold( artifactRegistry )}
184
- values.yaml registry => ${chalk.red.bold( values.image?.registry )}
162
+ The registry specified in the package.json leverege stanza does not line up
163
+ with the image registry in helm/values.yaml. The naming convention expects
164
+ the '/images' suffix to be added to the package.json registry string and then
165
+ stored as the image.registry in the helm/values.yaml file.
185
166
 
186
- Expected settings:
187
- values.yaml registry => ${chalk.green.bold( expectedChartRegistry )}
188
- ` )
167
+ Current settings:
168
+ package.json registry => ${chalk.yellow.bold( artifactRegistry )}
169
+ values.yaml registry => ${chalk.red.bold( values.image?.registry )}
189
170
 
190
- if ( !values.image ) {
191
- log( `
192
- ${chalk.yellow.bold( '***DEPRECATED: legacy helm charts detected' )}
171
+ Expected settings:
172
+ values.yaml registry => ${chalk.green.bold( expectedChartRegistry )}
173
+ ` )
193
174
 
194
- The helm chart structure appears to be based on the pre-ignition helm chart
195
- layouts. The charts must be upgraded before docker-to-registry can complete
196
- its job.
175
+ if ( !values.image ) {
176
+ log( `
177
+ ${chalk.yellow.bold( '***DEPRECATED: legacy helm charts detected' )}
197
178
 
198
- ` )
199
- }
179
+ The helm chart structure appears to be based on the pre-ignition helm chart
180
+ layouts. The charts must be upgraded before docker-to-registry can complete
181
+ its job.
200
182
 
201
- process.exit( 1 )
183
+ ` )
202
184
  }
185
+
186
+ process.exit( 1 )
203
187
  }
204
188
 
205
189
  // Give the summary and the user a chance to proceed or not
@@ -272,7 +256,7 @@ if ( isNpmWorkspace ) {
272
256
  await shellCmd( 'npm install --package-lock-only --workspaces false', { stdio : 'inherit' } )
273
257
  }
274
258
 
275
- await docker.buildContainerImage( { ...repoDescr, options, imageVersion } )
259
+ await docker.buildContainerImage( { artifactProject, artifactRegistry, containerName, imageVersion, isNpmWorkspace } )
276
260
 
277
261
  if ( willGitTag ) {
278
262
  const tagDescription = `docker_${imageVersion}`
@@ -213,6 +213,7 @@ try {
213
213
  const hostingPrefix = CHANNEL ? '' : 'hosting:'
214
214
 
215
215
  await $( { ac } )`npm run clean && DOTENV_CONFIG_PATH=${path.join( process.cwd(), '.env.temp' )} DEPLOYMENT_TARGET=${TARGET.siteId} npm run build && firebase use ${TARGET.projectId} && firebase ${deployType} --only ${hostingPrefix}${TARGET.siteId}`
216
+ .pipe( process.stdout )
216
217
  } catch ( err ) {
217
218
  console.error( err )
218
219
  }
@@ -130,9 +130,8 @@ const ac = new AbortController()
130
130
 
131
131
  EXIT_EVENTS
132
132
  .forEach( ( event ) => {
133
- process.on( event, ( d ) => {
133
+ process.on( event, () => {
134
134
  if ( !exited ) {
135
- console.error( d )
136
135
  exited = true
137
136
  fs.removeSync( path.join( process.cwd(), '.env.temp' ) )
138
137
  ac.abort()
@@ -143,7 +142,7 @@ EXIT_EVENTS
143
142
  await fs.writeFile( path.join( process.cwd(), '.env.temp' ), envFileContent )
144
143
 
145
144
  try {
146
- await $( { ac } )`DOTENV_CONFIG_PATH=${path.join( process.cwd(), '.env.temp' )} DEPLOYMENT_TARGET=${TARGET.siteId} npm run serve --colors`
145
+ await $( { ac } )`DOTENV_CONFIG_PATH=${path.join( process.cwd(), '.env.temp' )} DEPLOYMENT_TARGET=${TARGET.siteId} npm run serve --colors`.pipe( process.stdout )
147
146
  } catch ( err ) {
148
147
  console.error( err )
149
148
  }
@@ -17,17 +17,6 @@ config:
17
17
  # # timescale
18
18
  # PG_HOST: "cnpg-db-tsdb-basic-r.cnpg-operands"
19
19
 
20
- # The settings on the left are for the "standard" legacy chart settings which
21
- # specifiy the old k8s secrets setups and are the defaults values from the
22
- # api-server helm chart.
23
- psqlSecurity: # v----- these are for CNPG -----v
24
- PSQL_SECRET_NAME: "authz-postgres" # "cnpg-db-psql-stack-postgres-pw"
25
- PSQL_SECRET_KEY: "postgresql-password" # "password"
26
- TSDB_SECRET_NAME: "postgresql-password" # "cnpg-db-tsdb-basic-postgres-pw"
27
- TSDB_SECRET_KEY: "postgresql-password" # "password"
28
- TSDB_DENSE_SECRET_NAME: "postgresql-password" # "cnpg-db-tsdb-dense-postgres-pw"
29
- TSDB_DENSE_SECRET_KEY: "postgresql-password" # "password"
30
-
31
20
  autoscaling:
32
21
  minReplicas: 3
33
22
  maxReplicas: 12
@@ -1,6 +1,3 @@
1
1
  #!/bin/bash
2
2
 
3
- # ensures the PG_PASSWORD secret exists and correct
4
- ensureCnpgSecret PG_PASSWORD $SERVICE-postgres-pw
5
-
6
3
  kubectl apply -f cnpg-db-psql-stack/cluster.yaml $K8S_WHAT
@@ -1,6 +1,3 @@
1
1
  #!/bin/bash
2
-
3
- # ensures the TSDB_PASSWORD secret exists and correct
4
- ensureCnpgSecret TSDB_PASSWORD $SERVICE-postgres-pw
5
-
2
+ #
6
3
  kubectl apply -f cnpg-db-tsdb-basic/cluster.yaml $K8S_WHAT
@@ -1,6 +1,3 @@
1
1
  #!/bin/bash
2
-
3
- # ensures the TSDB_DENSE_PASSWORD secret exists and correct
4
- ensureCnpgSecret TSDB_DENSE_PASSWORD $SERVICE-postgres-pw
5
-
2
+ #
6
3
  kubectl apply -f cnpg-db-tsdb-dense/cluster.yaml $K8S_WHAT
@@ -2,7 +2,7 @@
2
2
  #
3
3
  showInstalling "Elastic Search 8"
4
4
 
5
- [ -z "$ELASTIC_CHART_VERSION" ] && ELASTIC_CHART_VERSION="21.3.17"
5
+ [ -z "$ELASTIC_CHART_VERSION" ] && ELASTIC_CHART_VERSION="21.3.10"
6
6
 
7
7
  OCI_CHART="oci://registry-1.docker.io/bitnamicharts/elasticsearch"
8
8
 
@@ -4,7 +4,7 @@ showInstalling "Redis"
4
4
 
5
5
  OCI_CHART="oci://registry-1.docker.io/bitnamicharts/redis"
6
6
 
7
- [ -z "$REDIS_CHART_VERSION" ] && REDIS_CHART_VERSION="20.1.3"
7
+ [ -z "$REDIS_CHART_VERSION" ] && REDIS_CHART_VERSION="20.0.5"
8
8
 
9
9
  helm upgrade --install redis $OCI_CHART \
10
10
  --values redis/redis-local.yaml \
package/src/helmdn.sh CHANGED
@@ -38,6 +38,7 @@ PLATFORM=(
38
38
  )
39
39
 
40
40
  SYSTEM=(
41
+ cert-manager
41
42
  elastic
42
43
  preemptible-killer
43
44
  redis
package/src/helmup.sh CHANGED
@@ -543,15 +543,14 @@ function initializeCnpgOperand() {
543
543
  local IMAGINE_PW_NAME="$CNPG_SERVICE-imagine-pw"
544
544
  local POSTGRES_PW_NAME="$CNPG_SERVICE-postgres-pw"
545
545
 
546
- # TODO: we use postgres root password for access - that is bad - fix it?
547
- # printf "\nCreating imagine db password for `color g $CNPG_SERVICE`\n"
548
- # kubectl create secret generic -n $CNPG_NAMESPACE "$IMAGINE_PW_NAME" \
549
- # --from-literal=username="imagine" \
550
- # --from-literal=password="$(generatePassword)" &> $DEVNULL
551
- # warnOnError $? "the `color y $IMAGINE_PW_NAME` secret may already exist"
546
+ printf "\nCreating imagine db password for `color g $CNPG_SERVICE`\n"
547
+ kubectl create secret generic -n $CNPG_NAMESPACE "$IMAGINE_PW_NAME" \
548
+ --from-literal=username="imagine" \
549
+ --from-literal=password="$(generatePassword)" &> $DEVNULL
550
+ warnOnError $? "the `color y $IMAGINE_PW_NAME` secret may already exist"
552
551
 
553
- # # reflect the secret to default namespace
554
- # reflectSecretIntoNamespaces $IMAGINE_PW_NAME $CNPG_NAMESPACE "default"
552
+ # reflect the secret to default namespace
553
+ reflectSecretIntoNamespaces $IMAGINE_PW_NAME $CNPG_NAMESPACE "default"
555
554
 
556
555
  printf "\nCreating postgres db password for `color g $CNPG_SERVICE`\n"
557
556
  kubectl create secret generic -n $CNPG_NAMESPACE "$POSTGRES_PW_NAME" \
@@ -671,23 +670,6 @@ EOSNAPSC
671
670
  sleep 2 # hold up processing for a moment to allow IAM mods to propagate
672
671
  }
673
672
 
674
- function ensureCnpgSecret() {
675
- local mgrSecret=$1
676
- local k8sSecret=$2
677
-
678
- service-man --from-helmup --sync-secrets ${mgrSecret}=${k8sSecret}
679
-
680
- if [ $? -ne 0 ];
681
- then
682
- cat<<BAD_SECRETS
683
- `color r "There appears to be a problem with one, or both of the secrets. Please
684
- rectify the situation and try again."`
685
-
686
- BAD_SECRETS
687
- exit 1
688
- fi
689
- }
690
-
691
673
  function installVeleroEnvironment() {
692
674
  # Follows instructions => https://github.com/vmware-tanzu/velero-plugin-for-gcp
693
675
  showInstalling "Velero Backup Management"
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable max-len */
3
+ /* eslint-disable security/detect-non-literal-fs-filename */
4
+ /* eslint-disable no-console */
5
+
6
+ import { execSync } from 'child_process';
7
+ import fs from 'fs';
8
+ import os from 'os';
9
+ import path from 'path';
10
+
11
+ // Execute gcloud command to get the token
12
+ const TOKEN = execSync( 'gcloud auth print-access-token' ).toString().trim();
13
+
14
+ // Write to pip.conf
15
+ const pipConfigDir = path.join( os.homedir(), '.config', 'pip' );
16
+ fs.mkdirSync( pipConfigDir, { recursive : true } );
17
+ const pipConfigContent = `
18
+ [global]
19
+ extra-index-url = https://oauth2accesstoken:${TOKEN}@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple/
20
+ `;
21
+ fs.writeFileSync( path.join( pipConfigDir, 'pip.conf' ), pipConfigContent );
22
+ console.log( 'Updated ~/.config/pip/pip.conf' );
23
+
24
+ // Write to pypirc
25
+ const pypircContent = `
26
+ [distutils]
27
+ index-servers =
28
+ leverege-python-packages
29
+
30
+ [leverege-python-packages]
31
+ repository: https://us-python.pkg.dev/leverege-registry/leverege-python-packages/
32
+ username: oauth2accesstoken
33
+ password: ${TOKEN}
34
+ `;
35
+ fs.writeFileSync( path.join( os.homedir(), '.pypirc' ), pypircContent );
36
+ console.log( 'Updated ~/.pypirc\n' );
37
+
38
+ // Escape for sed
39
+
40
+ const UV_EXTRA_INDEX_URL = `https://foo:${TOKEN}@us-python.pkg.dev/leverege-registry/leverege-python-packages/simple`;
41
+
42
+ const SHELLS = [ 'bash', 'dash', 'zsh', 'fish' ];
43
+
44
+ SHELLS.forEach( ( shell ) => {
45
+ try {
46
+ execSync( `command -v ${shell}` );
47
+ let configFile;
48
+ switch ( shell ) {
49
+ case 'bash':
50
+ configFile = path.join( os.homedir(), '.bashrc' );
51
+ break;
52
+ case 'dash':
53
+ configFile = path.join( os.homedir(), '.profile' );
54
+ break;
55
+ case 'zsh':
56
+ configFile = path.join( os.homedir(), '.zshrc' );
57
+ break;
58
+ case 'fish':
59
+ configFile = path.join( os.homedir(), '.config', 'fish', 'config.fish' );
60
+ break;
61
+ default:
62
+ console.log( `Shell ${shell} is not supported.` );
63
+ return;
64
+ }
65
+
66
+ console.log( `Updating shell configuration in ${configFile}` );
67
+
68
+ let configContent = fs.readFileSync( configFile, 'utf8' );
69
+ const regex = /export UV_EXTRA_INDEX_URL=.*/;
70
+ const newLine = `export UV_EXTRA_INDEX_URL=${UV_EXTRA_INDEX_URL}`;
71
+
72
+ if ( regex.test( configContent ) ) {
73
+ configContent = configContent.replace( regex, newLine );
74
+ } else {
75
+ configContent += `\n${newLine}\n`;
76
+ }
77
+
78
+ fs.writeFileSync( configFile, configContent );
79
+ } catch ( error ) {
80
+ console.log( `Shell ${shell} is not installed.` );
81
+ }
82
+ } );
83
+
84
+ function getParentShell() {
85
+ let ppid = execSync( 'ps -p $$ -o ppid=' ).toString().trim();
86
+ let shell = execSync( `ps -p ${ppid} -o comm=` ).toString().trim();
87
+
88
+ while ( ppid !== '1' ) {
89
+ ppid = execSync( `ps -p ${ppid} -o ppid=` ).toString().trim();
90
+ shell = execSync( `ps -p ${ppid} -o comm=` ).toString().trim();
91
+
92
+ const shellMatch = shell.match( /bash|dash|zsh|fish/ );
93
+ if ( shellMatch ) {
94
+ return shellMatch[0];
95
+ }
96
+ }
97
+
98
+ return null
99
+ }
100
+
101
+ const shell = getParentShell();
102
+
103
+ if ( !shell ) {
104
+ console.log( '\nCould not determine parent shell. Please source your shell configuration to apply the changes.\n' );
105
+ console.log( 'Example (bash): source ~/.bashrc' );
106
+ console.log( 'Example (dash): source ~/.profile' );
107
+ console.log( 'Example (zsh): source ~/.zshrc' );
108
+ console.log( 'Example (fish): source ~/.config/fish/config.fish' );
109
+ process.exit( 1 );
110
+ }
111
+
112
+ console.log( `\nReloading ${shell} shell with new configuration.\n` );
113
+
114
+ switch ( shell ) {
115
+ case 'bash':
116
+ case 'dash':
117
+ case 'zsh':
118
+ case 'fish':
119
+ execSync( shell, { stdio : 'inherit' } );
120
+ break;
121
+ default:
122
+ console.log( `Shell ${shell} is not supported.\n` );
123
+ }