diff --git a/src/api/actor.api.ts b/src/api/actor.api.ts index e9bed48..79b3908 100644 --- a/src/api/actor.api.ts +++ b/src/api/actor.api.ts @@ -5,14 +5,15 @@ import {green, red, yellow} from 'kleur'; import {getToken} from '../configs/cli.config'; import {readEmulatorConfig} from '../configs/emulator.config'; import {ENV} from '../env'; +import {EnvToken} from '../env.token'; import {noConfigFile} from '../utils/cli.config.utils'; -import {getProcessToken, isHeadless} from '../utils/process.utils'; +import {isNotHeadless} from '../utils/process.utils'; import {initAgent} from './agent.api'; export const actorParameters = async (): Promise< Omit & Required> > => { - const configNotFound = !isHeadless() && noConfigFile(); + const configNotFound = (await isNotHeadless()) && noConfigFile(); if (configNotFound) { await missingConfigInfo({errorType: 'not-configured'}); @@ -20,7 +21,7 @@ export const actorParameters = async (): Promise< process.exit(1); } - const token = getProcessToken() ?? (await getToken()); + const token = (await EnvToken.getInstance()).token ?? (await getToken()); if (isNullish(token)) { await missingConfigInfo({errorType: 'not-logged-in'}); diff --git a/src/env.token.ts b/src/env.token.ts new file mode 100644 index 0000000..1af2aa6 --- /dev/null +++ b/src/env.token.ts @@ -0,0 +1,53 @@ +import {nonNullish} from '@dfinity/utils'; +import type {JsonnableEd25519KeyIdentity} from '@icp-sdk/core/identity'; + +export class EnvToken { + static #instance: EnvToken | undefined; + + readonly #token: JsonnableEd25519KeyIdentity | undefined; + + private constructor(readonly keyIdentity: JsonnableEd25519KeyIdentity | undefined) { + this.#token = keyIdentity; + } + + static async getInstance(): Promise { + if (EnvToken.#instance === undefined) { + // eslint-disable-next-line require-atomic-updates + EnvToken.#instance = await EnvToken.#init(); + } + + return EnvToken.#instance; + } + + // eslint-disable-next-line @typescript-eslint/require-await + static async #init(): Promise { + const envToken = process.env.JUNO_TOKEN; + + if (envToken !== undefined) { + const token = this.#parseToken({envToken}); + return new EnvToken(token); + } + + return new EnvToken(undefined); + } + + static #parseToken({envToken}: {envToken: string}): JsonnableEd25519KeyIdentity | undefined { + try { + const {token} = JSON.parse(atob(envToken)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return token; + } catch (err: unknown) { + throw new Error('Cannot parse token provided as an environment variable.', { + cause: err + }); + } + } + + get token(): JsonnableEd25519KeyIdentity | undefined { + return this.#token; + } + + isDefined = (): boolean => { + return nonNullish(this.#token); + }; +} diff --git a/src/services/assert.services.ts b/src/services/assert.services.ts index 5a2393f..c834ce2 100644 --- a/src/services/assert.services.ts +++ b/src/services/assert.services.ts @@ -48,7 +48,7 @@ export const assertSatelliteMemorySize = async () => { heap )} MB, which exceeds the asserted limit of ${formatNumber(maxMemorySize)} MB.`; - if (isHeadless()) { + if (await isHeadless()) { console.log(yellow(errorText)); process.exit(1); } diff --git a/src/services/config/apply.services.ts b/src/services/config/apply.services.ts index 9bea5b1..15f5eb2 100644 --- a/src/services/config/apply.services.ts +++ b/src/services/config/apply.services.ts @@ -583,7 +583,7 @@ const prepareConfig = async ({ return filterIdenticalConfig(extendWithVersions()); } - if (isHeadless()) { + if (await isHeadless()) { console.log( yellow('Non-interactive mode detected. Re-run with --force to overwrite without checks.') ); diff --git a/src/services/emulator/_runner.services.ts b/src/services/emulator/_runner.services.ts index 81948f2..1185149 100644 --- a/src/services/emulator/_runner.services.ts +++ b/src/services/emulator/_runner.services.ts @@ -170,7 +170,7 @@ const startEmulator = async ({config: extendedConfig}: {config: CliEmulatorConfi // -i: Keep STDIN open even if not attached. Equivalent to `--interactive`. await execute({ command: runner, - args: ['start', '-a', ...(isHeadless() ? [] : ['-i']), containerName] + args: ['start', '-a', ...((await isHeadless()) ? [] : ['-i']), containerName] }); return; } @@ -219,7 +219,7 @@ const startEmulator = async ({config: extendedConfig}: {config: CliEmulatorConfi command: runner, args: [ 'run', - ...(isHeadless() ? [] : ['-it']), + ...((await isHeadless()) ? [] : ['-it']), '--name', containerName, '-p', diff --git a/src/services/links.services.ts b/src/services/links.services.ts index e0d269f..dc9f379 100644 --- a/src/services/links.services.ts +++ b/src/services/links.services.ts @@ -16,7 +16,7 @@ export const links = async () => { // If a developer is using a JUNO_TOKEN to execute command(s), the custom domain will not be queried or printed. // This is particularly useful in CI environments where access keys might be limited, // and only ADMIN keys can list custom domains. - const domains = isHeadless() ? [] : await listCustomDomains({satellite}); + const domains = (await isHeadless()) ? [] : await listCustomDomains({satellite}); console.log(`\n🛠️ ${terminalLink(adminUrl)}`); diff --git a/src/services/modules/upgrade/upgrade-assert.services.ts b/src/services/modules/upgrade/upgrade-assert.services.ts index 22ca2c5..381db4e 100644 --- a/src/services/modules/upgrade/upgrade-assert.services.ts +++ b/src/services/modules/upgrade/upgrade-assert.services.ts @@ -45,7 +45,7 @@ export const assertSatelliteBuildType = async ({ const warning = satelliteType === 'extended' && (wasmType === 'stock' || isNullish(wasmType)); - if (isHeadless() && warning) { + if ((await isHeadless()) && warning) { throw new Error( 'Your satellite uses serverless functions. Reverting to the stock version would remove your custom features! ' ); diff --git a/src/services/modules/upgrade/upgrade.services.ts b/src/services/modules/upgrade/upgrade.services.ts index 0cbecdf..d3078a1 100644 --- a/src/services/modules/upgrade/upgrade.services.ts +++ b/src/services/modules/upgrade/upgrade.services.ts @@ -34,7 +34,7 @@ const executeUpgradeWasm = async ({ }: {assetKey: AssetKey; moduleId: string} & UpgradeWasm) => { await assert?.({wasmModule: wasm}); - if (isNotHeadless()) { + if (await isNotHeadless()) { await assertUpgradeHash({hash, reset, assetKey, moduleId}); } diff --git a/src/stores/settings.store.ts b/src/stores/settings.store.ts index d282045..77d6382 100644 --- a/src/stores/settings.store.ts +++ b/src/stores/settings.store.ts @@ -18,7 +18,7 @@ class SettingsConfigStore { static async init(): Promise { const store = new SettingsConfigStore(getSettingsConfig()); - if (isHeadless()) { + if (await isHeadless()) { return store; } diff --git a/src/utils/process.utils.ts b/src/utils/process.utils.ts index 21a82e7..451876e 100644 --- a/src/utils/process.utils.ts +++ b/src/utils/process.utils.ts @@ -1,28 +1,10 @@ -import type {JsonnableEd25519KeyIdentity} from '@icp-sdk/core/identity'; import {hasArgs} from '@junobuild/cli-tools'; +import {EnvToken} from '../env.token'; -export const getProcessToken = (): JsonnableEd25519KeyIdentity | undefined => { - const envToken = process.env.JUNO_TOKEN; +export const isHeadless = async (): Promise => { + const {isDefined} = await EnvToken.getInstance(); - if (envToken === undefined) { - return undefined; - } - - try { - const {token} = JSON.parse(atob(envToken)); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return token; - } catch (err: unknown) { - throw new Error('Cannot parse token provided as an environment variable.', { - cause: err - }); - } -}; - -const isProcessToken = (): boolean => getProcessToken() !== undefined; - -export const isHeadless = (): boolean => { - if (isProcessToken()) { + if (isDefined()) { return true; } @@ -30,4 +12,4 @@ export const isHeadless = (): boolean => { return hasArgs({args, options: ['--headless']}); }; -export const isNotHeadless = (): boolean => !isHeadless(); +export const isNotHeadless = async (): Promise => !(await isHeadless()); diff --git a/src/utils/prompt.utils.ts b/src/utils/prompt.utils.ts index d3649d6..9d13aa4 100644 --- a/src/utils/prompt.utils.ts +++ b/src/utils/prompt.utils.ts @@ -50,7 +50,7 @@ export const confirmAndExit = async (message: string) => { }; export const confirmAndExitUnlessHeadlessAndDev = async (message: string) => { - if (isHeadless() && DEV) { + if ((await isHeadless()) && DEV) { return; }