@metaplay/metaplay-auth 1.9.2 → 1.9.3-next.851

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.
Binary file
package/index.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander'
3
3
  import Docker from 'dockerode'
4
- import { writeFile } from 'fs/promises'
5
- import { exit } from 'process'
4
+ import { writeFile } from 'node:fs/promises'
5
+ import { exit } from 'node:process'
6
6
  import * as semver from 'semver'
7
7
 
8
8
  import { KubeConfig } from '@kubernetes/client-node'
@@ -279,29 +279,34 @@ program
279
279
  .description('login to the Metaplay cloud using a machine account (using credentials in environment variable METAPLAY_CREDENTIALS)')
280
280
  .option('--dev-credentials', 'machine user credentials to use, only for dev purposes, use METAPLAY_CREDENTIALS env variable for better safety!')
281
281
  .action(async (options) => {
282
- // Get credentials from command line or from METAPLAY_CREDENTIALS environment variable
283
- let credentials: string
284
- if (options.devCredentials) {
285
- credentials = options.devCredentials
286
- } else {
287
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
288
- credentials = process.env.METAPLAY_CREDENTIALS!
289
- if (!credentials || credentials === '') {
290
- throw new Error('Unable to find the credentials, the environment variable METAPLAY_CREDENTIALS is not defined!')
282
+ try {
283
+ // Get credentials from command line or from METAPLAY_CREDENTIALS environment variable
284
+ let credentials: string
285
+ if (options.devCredentials) {
286
+ credentials = options.devCredentials
287
+ } else {
288
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
289
+ credentials = process.env.METAPLAY_CREDENTIALS!
290
+ if (!credentials || credentials === '') {
291
+ throw new Error('Unable to find the credentials, the environment variable METAPLAY_CREDENTIALS is not defined!')
292
+ }
291
293
  }
292
- }
293
294
 
294
- // Parse the clientId and clientSecret from the credentials (separate by a '+' character)
295
- // \note We can't be certain that the secret does not contain pluses so split at the first occurrence
296
- const splitOffset = credentials.indexOf('+')
297
- if (splitOffset === -1) {
298
- throw new Error('Invalid format for credentials, you should copy-paste the value from the developer portal verbatim')
299
- }
300
- const clientId = credentials.substring(0, splitOffset)
301
- const clientSecret = credentials.substring(splitOffset + 1)
295
+ // Parse the clientId and clientSecret from the credentials (separate by a '+' character)
296
+ // \note We can't be certain that the secret does not contain pluses so split at the first occurrence
297
+ const splitOffset = credentials.indexOf('+')
298
+ if (splitOffset === -1) {
299
+ throw new Error('Invalid format for credentials, you should copy-paste the value from the developer portal verbatim')
300
+ }
301
+ const clientId = credentials.substring(0, splitOffset)
302
+ const clientSecret = credentials.substring(splitOffset + 1)
302
303
 
303
- // Login with machine user and save the tokens
304
- await machineLoginAndSaveTokens(clientId, clientSecret)
304
+ // Login with machine user and save the tokens
305
+ await machineLoginAndSaveTokens(clientId, clientSecret)
306
+ } catch (error) {
307
+ console.error(`Error during machine login: ${error instanceof Error ? error.message : String(error)}`)
308
+ exit(1)
309
+ }
305
310
  })
306
311
 
307
312
  program
@@ -720,7 +725,10 @@ program
720
725
  })
721
726
  } catch (error) {
722
727
  const errMessage = error instanceof Error ? error.message : String(error)
723
- throw new Error(`Failed to fetch docker image ${imageName} from target environment registry: ${errMessage}`)
728
+ throw new Error(
729
+ `Failed to fetch docker image ${imageName} from target environment registry: ${errMessage}`,
730
+ { cause: error }
731
+ )
724
732
  }
725
733
 
726
734
  // Resolve the labels
@@ -730,7 +738,10 @@ program
730
738
  imageLabels = (await localDockerImage.inspect()).Config.Labels || {}
731
739
  } catch (error) {
732
740
  const errMessage = error instanceof Error ? error.message : String(error)
733
- throw new Error(`Failed to resolve docker image metadata from pulled image ${imageName}: ${errMessage}`)
741
+ throw new Error(
742
+ `Failed to resolve docker image metadata from pulled image ${imageName}: ${errMessage}`,
743
+ { cause: error }
744
+ )
734
745
  }
735
746
  }
736
747
 
@@ -767,7 +778,10 @@ program
767
778
  try {
768
779
  helmChartRange = new semver.Range(helmChartVersionSpec)
769
780
  } catch (error) {
770
- throw new Error(`Helm chart version '${helmChartVersionSpec}' is not a valid SemVer range: ${String(error)}`)
781
+ throw new Error(
782
+ `Helm chart version '${helmChartVersionSpec}' is not a valid SemVer range: ${String(error)}`,
783
+ { cause: error }
784
+ )
771
785
  }
772
786
  }
773
787
 
@@ -925,7 +939,7 @@ program
925
939
  kubeconfig.loadFromString(kubeconfigPayload)
926
940
  } catch (error) {
927
941
  const errMessage = error instanceof Error ? error.message : String(error)
928
- throw new Error(`Failed to load or validate kubeconfig. ${errMessage}`)
942
+ throw new Error(`Failed to load or validate kubeconfig. ${errMessage}`, { cause: error })
929
943
  }
930
944
 
931
945
  // Resolve Helm deployment
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@metaplay/metaplay-auth",
3
3
  "description": "Utility CLI for authenticating with the Metaplay Auth and making authenticated calls to infrastructure endpoints.",
4
- "version": "1.9.2",
4
+ "version": "1.9.3-next.851",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "homepage": "https://metaplay.io",
@@ -17,29 +17,28 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@metaplay/eslint-config": "workspace:*",
20
- "@types/dockerode": "3.3.39",
21
- "@types/express": "5.0.2",
22
- "@types/js-yaml": "4.0.9",
23
- "@types/jsonwebtoken": "9.0.9",
20
+ "@types/dockerode": "3.3.44",
21
+ "@types/express": "5.0.3",
22
+ "@types/js-yaml": "^4.0.9",
23
+ "@types/jsonwebtoken": "9.0.10",
24
24
  "@types/jwk-to-pem": "2.0.3",
25
- "@types/node": "22.15.29",
26
- "@types/semver": "7.7.0",
27
- "esbuild": "0.25.5",
28
- "tsx": "4.19.4",
29
- "typescript": "5.8.3",
30
- "vitest": "3.1.3",
31
- "@aws-sdk/client-ecr": "3.810.0",
32
- "@kubernetes/client-node": "1.0.0-rc7",
33
- "@ory/client": "1.20.11",
34
- "commander": "12.1.0",
35
- "dockerode": "4.0.6",
36
- "h3": "1.15.3",
37
- "js-yaml": "4.1.0",
25
+ "@types/node": "24.5.2",
26
+ "@types/semver": "7.7.1",
27
+ "esbuild": "0.25.10",
28
+ "tsx": "4.20.5",
29
+ "typescript": "5.9.2",
30
+ "vitest": "3.2.4",
31
+ "@aws-sdk/client-ecr": "3.891.0",
32
+ "@kubernetes/client-node": "1.3.0",
33
+ "commander": "14.0.1",
34
+ "dockerode": "4.0.8",
35
+ "h3": "1.15.4",
36
+ "js-yaml": "^4.1.0",
38
37
  "jsonwebtoken": "9.0.2",
39
38
  "jwk-to-pem": "2.0.7",
40
39
  "open": "8.4.2",
41
40
  "semver": "7.7.2",
42
41
  "tslog": "4.9.3",
43
- "eslint": "9.31.0"
42
+ "eslint": "9.35.0"
44
43
  }
45
44
  }
package/src/auth.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
1
2
 
2
3
  import { toNodeListener, createApp, defineEventHandler, getQuery, sendError } from 'h3'
3
4
  import jwt, { JwtPayload } from 'jsonwebtoken'
@@ -6,8 +7,6 @@ import { randomBytes, createHash } from 'node:crypto'
6
7
  import { createServer } from 'node:http'
7
8
  import open from 'open'
8
9
 
9
- import { Configuration, OidcApi } from '@ory/client'
10
-
11
10
  import { portalBaseUrl } from './config.js'
12
11
  import { logger } from './logging.js'
13
12
  import { setSecret, getSecret, removeSecret } from './secret_store.js'
@@ -23,11 +22,6 @@ const clientId = 'c16ea663-ced3-46c6-8f85-38c9681fe1f0'
23
22
  const baseURL = 'https://auth.metaplay.dev'
24
23
  const authorizationEndpoint = `${baseURL}/oauth2/auth`
25
24
  const tokenEndpoint = `${baseURL}/oauth2/token`
26
- const oidcApi = new OidcApi(
27
- new Configuration({
28
- basePath: baseURL,
29
- })
30
- )
31
25
 
32
26
  /**
33
27
  * A helper function which generates a code verifier and challenge for exchanging code from Ory server.
@@ -45,16 +39,9 @@ function generateCodeVerifierAndChallenge(): { verifier: string; challenge: stri
45
39
  * @returns An object containing the user's info.
46
40
  */
47
41
  export async function getUserinfo(token: string): Promise<any> {
48
- logger.debug('Trying to find OIDC well-known endpoints...')
49
- const oidcRes = await oidcApi.discoverOidcConfiguration()
50
-
51
- const userinfoEndpoint = oidcRes.data?.userinfo_endpoint
52
- if (!userinfoEndpoint) {
53
- throw new Error('No userinfo endpoint found in OIDC configuration')
54
- }
55
- logger.debug(`Found userinfo endpoint: ${userinfoEndpoint}`)
56
-
57
- const userinfoRes = await fetch(userinfoEndpoint, {
42
+ // To relieve pressure on Ory endpoints, get the userinfo directly from portal,
43
+ // instead of discovering OIDC endpoints from Ory first. Portal is where Ory would redirect the request anyway.
44
+ const userinfoRes = await fetch(`${portalBaseUrl}/api/external/userinfo`, {
58
45
  headers: {
59
46
  Authorization: `Bearer ${token}`,
60
47
  },
@@ -201,8 +188,30 @@ export async function machineLoginAndSaveTokens(clientId: string, clientSecret:
201
188
  body: params.toString(),
202
189
  })
203
190
 
204
- // Return type checked manually by Teemu on 2024-3-7.
205
- const tokens = (await res.json()) as { access_token: string; token_type: string; expires_in: number; scope: string }
191
+ // Improved error handling for response
192
+ let tokens: { access_token: string; token_type: string; expires_in: number; scope: string }
193
+ if (!res.ok) {
194
+ let message: string | undefined
195
+ try {
196
+ const errorBody: any = await res.json()
197
+ message = errorBody && (errorBody.error_description || errorBody.error || errorBody.message)
198
+ } catch (_error) {
199
+ try {
200
+ message = await res.text()
201
+ } catch (_error) {
202
+ message = undefined
203
+ }
204
+ }
205
+ throw new Error(`Machine login failed: ${message || `HTTP ${res.status}`}`)
206
+ }
207
+ try {
208
+ tokens = (await res.json()) as { access_token: string; token_type: string; expires_in: number; scope: string }
209
+ } catch (error) {
210
+ throw new Error(
211
+ "Invalid response from authentication server. Please check your network connection and credentials and try again.",
212
+ { cause: error }
213
+ )
214
+ }
206
215
 
207
216
  logger.debug('Received machine authentication tokens, saving them for future use...')
208
217
 
@@ -214,7 +223,29 @@ export async function machineLoginAndSaveTokens(clientId: string, clientSecret:
214
223
  },
215
224
  })
216
225
 
217
- const userInfo = (await userInfoResponse.json()) as { given_name: string; family_name: string }
226
+ let userInfo: { given_name: string; family_name: string }
227
+ if (!userInfoResponse.ok) {
228
+ let message: string | undefined
229
+ try {
230
+ const errorBody: any = await userInfoResponse.json()
231
+ message = errorBody && (errorBody.error_description || errorBody.error || errorBody.message)
232
+ } catch (_error) {
233
+ try {
234
+ message = await userInfoResponse.text()
235
+ } catch (_error) {
236
+ message = undefined
237
+ }
238
+ }
239
+ throw new Error(`Failed to fetch user info: ${message || `HTTP ${userInfoResponse.status}`}`)
240
+ }
241
+ try {
242
+ userInfo = (await userInfoResponse.json()) as { given_name: string; family_name: string }
243
+ } catch (error) {
244
+ throw new Error(
245
+ "Invalid response from user info endpoint. Please check your network connection and try again.",
246
+ { cause: error }
247
+ )
248
+ }
218
249
 
219
250
  console.log(
220
251
  `You are now logged in with machine user ${userInfo.given_name} ${userInfo.family_name} (clientId=${clientId}) and can execute the other commands.`
@@ -267,7 +298,6 @@ export async function extendCurrentSession(): Promise<void> {
267
298
  async function extendCurrentSessionWithRefreshToken(
268
299
  refreshToken: string
269
300
  ): Promise<{ id_token: string; access_token: string; refresh_token: string }> {
270
- // TODO: similar to the todo task in getTokensWithAuthorizationCode, http request can be handled by ory/client.
271
301
  const params = new URLSearchParams({
272
302
  grant_type: 'refresh_token',
273
303
  refresh_token: refreshToken,
@@ -292,9 +322,12 @@ async function extendCurrentSessionWithRefreshToken(
292
322
  logger.error(`Failed to refresh tokens via endpoint ${tokenEndpoint}`)
293
323
  logger.error('Fetch error details:', error)
294
324
  if (error.cause?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
295
- throw new Error(`Failed to refresh tokens: SSL certificate validation failed for ${tokenEndpoint}. Is someone trying to tamper with your internet connection?`)
325
+ throw new Error(
326
+ `Failed to refresh tokens: SSL certificate validation failed for ${tokenEndpoint}. Is someone trying to tamper with your internet connection?`,
327
+ { cause: error }
328
+ )
296
329
  }
297
- throw new Error(`Failed to refresh tokens via ${tokenEndpoint}: ${error}`)
330
+ throw new Error(`Failed to refresh tokens via ${tokenEndpoint}: ${error}`, { cause: error })
298
331
  }
299
332
 
300
333
  // Check if the response is OK
@@ -329,7 +362,6 @@ async function getTokensWithAuthorizationCode(
329
362
  verifier: string,
330
363
  code: string
331
364
  ): Promise<{ id_token: string; access_token: string; refresh_token: string }> {
332
- // TODO: the authorization code exchange flow might be better to be handled by ory/client, could check if there's any useful tools there.
333
365
  try {
334
366
  const response = await fetch(tokenEndpoint, {
335
367
  method: 'POST',
@@ -363,7 +395,7 @@ export async function loadTokens(): Promise<TokenSet> {
363
395
  return tokens
364
396
  } catch (error) {
365
397
  if (error instanceof Error) {
366
- throw new Error(`Error loading tokens: ${error.message}`)
398
+ throw new Error(`Error loading tokens: ${error.message}`, { cause: error })
367
399
  }
368
400
 
369
401
  throw error
@@ -392,7 +424,7 @@ export async function saveTokens(tokens: Record<string, string>): Promise<void>
392
424
  showTokenInfo(tokens.access_token)
393
425
  } catch (error) {
394
426
  if (error instanceof Error) {
395
- throw new Error(`Failed to save tokens: ${error.message}`)
427
+ throw new Error(`Failed to save tokens: ${error.message}`, { cause: error })
396
428
  }
397
429
 
398
430
  throw error
@@ -408,7 +440,7 @@ export async function removeTokens(): Promise<void> {
408
440
  logger.debug('Removed tokens.')
409
441
  } catch (error) {
410
442
  if (error instanceof Error) {
411
- throw new Error(`Error removing tokens: ${error.message}`)
443
+ throw new Error(`Error removing tokens: ${error.message}`, { cause: error })
412
444
  }
413
445
 
414
446
  throw error
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander'
2
- import { readFileSync, existsSync } from 'fs'
3
- import path from 'path'
4
- import { exit } from 'process'
2
+ import { readFileSync, existsSync } from 'node:fs'
3
+ import path from 'node:path'
4
+ import { exit } from 'node:process'
5
5
  import * as semver from 'semver'
6
6
 
7
7
  import { pathJoin, executeCommand, ExecuteCommandResult } from './utils.js'
package/src/deployment.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { unlink, writeFile } from 'fs/promises'
2
- import os from 'os'
3
- import path from 'path'
4
- import { exit } from 'process'
1
+ import { unlink, writeFile } from 'node:fs/promises'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { exit } from 'node:process'
5
5
  import { EnvironmentDetails, TargetEnvironment } from 'targetenvironment.js'
6
- import { promises as dns } from 'dns'
7
- import tls from 'tls'
6
+ import { promises as dns } from 'node:dns'
7
+ import tls from 'node:tls'
8
8
 
9
9
  import {
10
10
  KubeConfig,
@@ -48,7 +48,7 @@ async function fetchGameServerPods(k8sApi: CoreV1Api, namespace: string): Promis
48
48
  } catch (error) {
49
49
  // \todo Better error handling ..
50
50
  console.error('Failed to fetch pods from Kubernetes:', error)
51
- throw new Error('Failed to fetch pods from Kubernetes')
51
+ throw new Error('Failed to fetch pods from Kubernetes', { cause: error })
52
52
  }
53
53
  }
54
54
 
@@ -337,7 +337,7 @@ async function fetchPodLogs(k8sApi: CoreV1Api, pod: V1Pod): Promise<string> {
337
337
  } catch (error) {
338
338
  // \todo Better error handling ..
339
339
  console.log('Failed to fetch pod logs from Kubernetes:', error)
340
- throw new Error('Failed to fetch pod logs from Kubernetes')
340
+ throw new Error('Failed to fetch pod logs from Kubernetes', { cause: error })
341
341
  }
342
342
  }
343
343
 
@@ -363,7 +363,9 @@ async function checkGameServerPod(
363
363
  }
364
364
 
365
365
  async function delay(ms: number): Promise<void> {
366
- await new Promise<void>((resolve) => setTimeout(resolve, ms))
366
+ await new Promise<void>((resolve) => {
367
+ setTimeout(resolve, ms)
368
+ })
367
369
  }
368
370
 
369
371
  function anyPodsInPhase(podStatuses: GameServerPodStatus[], phase: GameServerPodPhase): boolean {
@@ -457,7 +459,7 @@ async function waitForDomainResolution(hostname: string): Promise<void> {
457
459
  return
458
460
  } catch (err) {
459
461
  if (Date.now() > timeoutAt) {
460
- throw new Error(`Could not resolve domain ${hostname} before timeout.`)
462
+ throw new Error(`Could not resolve domain ${hostname} before timeout.`, { cause: err })
461
463
  }
462
464
 
463
465
  if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'ENOTFOUND') {
@@ -587,6 +589,7 @@ export async function debugGameServer(targetEnv: TargetEnvironment, targetPodNam
587
589
  if (!metadata?.name) {
588
590
  throw new Error('Unable to resolve name for the Kubernetes pod!')
589
591
  }
592
+ // eslint-disable-next-line no-param-reassign
590
593
  targetPodName = metadata.name
591
594
  } else {
592
595
  const podNames = gameServerPods.map((pod) => pod.metadata?.name).join(', ')
@@ -1,7 +1,7 @@
1
- import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto'
2
- import { promises as fs } from 'fs'
3
- import { homedir } from 'os'
4
- import { join } from 'path'
1
+ import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'node:crypto'
2
+ import { promises as fs } from 'node:fs'
3
+ import { homedir } from 'node:os'
4
+ import { join } from 'node:path'
5
5
 
6
6
  import { logger } from './logging.js'
7
7
 
@@ -36,7 +36,7 @@ async function loadSecrets(): Promise<Secrets> {
36
36
  return {}
37
37
  }
38
38
  if (password.length === 0) {
39
- throw new Error('The file is encrypted. Please set the METAPLAY_AUTH_PASSWORD environment variable.')
39
+ throw new Error('The file is encrypted. Please set the METAPLAY_AUTH_PASSWORD environment variable.', { cause: error })
40
40
  }
41
41
 
42
42
  throw error
package/src/stackapi.ts CHANGED
@@ -4,7 +4,7 @@ export class StackAPI {
4
4
  private readonly stackApiBaseUrl: string
5
5
 
6
6
  constructor(accessToken: string, stackApiBaseUrl: string | undefined) {
7
- if (accessToken == null) {
7
+ if (!accessToken) {
8
8
  throw new Error('accessToken must be provided')
9
9
  }
10
10
 
@@ -133,9 +133,9 @@ export class TargetEnvironment {
133
133
  } catch (error: any) {
134
134
  logger.error(`Failed to fetch ${method} ${url}:`, error)
135
135
  if (error.cause?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
136
- throw new Error(`Failed to to fetch ${url}: SSL certificate validation failed. Is someone trying to tamper with your internet connection?`)
136
+ throw new Error(`Failed to to fetch ${url}: SSL certificate validation failed. Is someone trying to tamper with your internet connection?`, { cause: error })
137
137
  }
138
- throw new Error(`Failed to fetch ${url}: ${error}`)
138
+ throw new Error(`Failed to fetch ${url}: ${error}`, { cause: error })
139
139
  }
140
140
 
141
141
  if (response.status !== 200) {
@@ -183,13 +183,13 @@ export class TargetEnvironment {
183
183
  // User-friendly error messages for well-known HTTP errors.
184
184
  if (error instanceof FetchJsonHttpError) {
185
185
  if (error.response.status === 404) {
186
- throw new Error(`Environment ${this.humanId} does not exist.`)
186
+ throw new Error(`Environment ${this.humanId} does not exist.`, { cause: error })
187
187
  } else if (error.response.status === 403) {
188
- throw new Error(`Your account does not have permissions to access environment ${this.humanId}.`)
188
+ throw new Error(`Your account does not have permissions to access environment ${this.humanId}.`, { cause: error })
189
189
  }
190
190
  }
191
191
  const errorMessage = (error instanceof Error) ? error.message : String(error)
192
- throw new Error(`Failed to fetch Kubernetes KubeConfig: ${errorMessage}`)
192
+ throw new Error(`Failed to fetch Kubernetes KubeConfig: ${errorMessage}`, { cause: error })
193
193
  }
194
194
 
195
195
  if (!kubeExecCredential.spec.cluster) {
package/src/utils.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { spawn } from 'child_process'
1
+ import { spawn } from 'node:child_process'
2
2
  import yaml from 'js-yaml'
3
- import path from 'path'
3
+ import path from 'node:path'
4
4
  import * as semver from 'semver'
5
5
 
6
6
  import { logger } from './logging.js'
7
- import { tmpdir } from 'os'
8
- import { randomBytes } from 'crypto'
9
- import { unlink, writeFile } from 'fs/promises'
7
+ import { tmpdir } from 'node:os'
8
+ import { randomBytes } from 'node:crypto'
9
+ import { unlink, writeFile } from 'node:fs/promises'
10
10
 
11
11
  /**
12
12
  * Checks if the given string is a fully qualified domain name (FQDN).
@@ -44,9 +44,9 @@ export function splitUrlComponents(urlString: string): {
44
44
  const subpaths = url.pathname.slice(1) ?? null
45
45
 
46
46
  return { scheme, hostname, port, subpaths }
47
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
47
+
48
48
  } catch (error) {
49
- throw new Error('Invalid URL')
49
+ throw new Error('Invalid URL', { cause: error })
50
50
  }
51
51
  }
52
52
 
@@ -266,7 +266,7 @@ export async function checkHelmVersion(): Promise<void> {
266
266
  logger.debug(`Helm version ${helmVersion} is compatible`)
267
267
  } catch (error) {
268
268
  if (error instanceof Error && error.message.includes('ENOENT')) {
269
- throw new Error('Helm is not installed or not found in PATH. Please install Helm version >= 3.18.0 and < 3.18.5')
269
+ throw new Error('Helm is not installed or not found in PATH. Please install Helm version >= 3.18.0 and < 3.18.5', { cause: error })
270
270
  }
271
271
  throw error
272
272
  }
@@ -282,6 +282,7 @@ export async function executeHelmCommand(kubeconfigPayload: string, args: string
282
282
  const kubeconfigPath = pathJoin(tmpdir(), randomBytes(20).toString('hex'))
283
283
  logger.debug(`Write temporary kubeconfig in ${kubeconfigPath}`)
284
284
  await writeFile(kubeconfigPath, kubeconfigPayload, { mode: 0o600 })
285
+ // eslint-disable-next-line no-param-reassign
285
286
  args = args.concat(['--kubeconfig', kubeconfigPath])
286
287
 
287
288
  // Use try-finally to remove the temp kubeconfig at the end
@@ -293,7 +294,7 @@ export async function executeHelmCommand(kubeconfigPayload: string, args: string
293
294
  // \todo output something from Helm result?
294
295
  } catch (error) {
295
296
  const errMessage = error instanceof Error ? error.message : String(error)
296
- throw new Error(`Failed to execute 'helm': ${errMessage}. You need to have Helm v3 installed to deploy a game server with metaplay-auth.`)
297
+ throw new Error(`Failed to execute 'helm': ${errMessage}. You need to have Helm v3 installed to deploy a game server with metaplay-auth.`, { cause: error })
297
298
  }
298
299
 
299
300
  // Throw on Helm non-success exit code
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = "1.9.2"
1
+ export const PACKAGE_VERSION = "1.9.3-next.851"
Binary file