@nexical/cli 0.11.20 → 0.11.23
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/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/deploy/providers/railway.js +12 -2
- package/dist/src/deploy/providers/railway.js.map +1 -1
- package/package.json +1 -1
- package/src/deploy/providers/railway.ts +14 -2
- package/test/unit/deploy/providers/railway.test.ts +4 -2
- package/tsconfig.json +1 -1
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI, findProjectRoot } from '@nexical/cli-core';\nimport { fileURLToPath } from 'node:url';\nimport { discoverCommandDirectories } from './src/utils/discovery.js';\nimport pkg from './package.json';\nimport path from 'node:path';\nimport { filterCommandDirectories } from './src/utils/filter.js';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst commandName = 'nexical';\nconst projectRoot = (await findProjectRoot(commandName, process.cwd())) || process.cwd();\nconst coreCommandsDir = path.resolve(__dirname, './src/commands');\nconst additionalCommands = discoverCommandDirectories(projectRoot);\n\nconst filteredAdditional = filterCommandDirectories(additionalCommands, coreCommandsDir);\n\nconst app = new CLI({\n version: pkg.version,\n commandName: commandName,\n searchDirectories: [...new Set([coreCommandsDir, ...filteredAdditional])],\n});\napp.start();\n","{\n \"name\": \"@nexical/cli\",\n \"version\": \"0.11.
|
|
1
|
+
{"version":3,"sources":["../index.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\nimport { CLI, findProjectRoot } from '@nexical/cli-core';\nimport { fileURLToPath } from 'node:url';\nimport { discoverCommandDirectories } from './src/utils/discovery.js';\nimport pkg from './package.json';\nimport path from 'node:path';\nimport { filterCommandDirectories } from './src/utils/filter.js';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nconst commandName = 'nexical';\nconst projectRoot = (await findProjectRoot(commandName, process.cwd())) || process.cwd();\nconst coreCommandsDir = path.resolve(__dirname, './src/commands');\nconst additionalCommands = discoverCommandDirectories(projectRoot);\n\nconst filteredAdditional = filterCommandDirectories(additionalCommands, coreCommandsDir);\n\nconst app = new CLI({\n version: pkg.version,\n commandName: commandName,\n searchDirectories: [...new Set([coreCommandsDir, ...filteredAdditional])],\n});\napp.start();\n","{\n \"name\": \"@nexical/cli\",\n \"version\": \"0.11.23\",\n \"license\": \"Apache-2.0\",\n \"type\": \"module\",\n \"bin\": {\n \"nexical\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"npm run test:unit && npm run test:integration && npm run test:e2e\",\n \"test:unit\": \"vitest run --config vitest.config.ts --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.ts\",\n \"test:e2e\": \"npm run build && vitest run --config vitest.e2e.config.ts\",\n \"test:watch\": \"vitest\",\n \"format\": \"prettier --write .\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"prepare\": \"husky\"\n },\n \"lint-staged\": {\n \"**/*\": [\n \"prettier --write --ignore-unknown\"\n ],\n \"**/*.{js,jsx,ts,tsx,astro}\": [\n \"eslint --fix\"\n ]\n },\n \"dependencies\": {\n \"@nexical/ai\": \"^0.1.5\",\n \"@nexical/cli-core\": \"^0.1.16\",\n \"dotenv\": \"^17.3.1\",\n \"fast-glob\": \"^3.3.3\",\n \"glob\": \"^13.0.5\",\n \"jiti\": \"^2.6.1\",\n \"minimist\": \"^1.2.8\",\n \"yaml\": \"^2.8.2\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/minimist\": \"^1.2.5\",\n \"@types/node\": \"^25.3.0\",\n \"@types/nunjucks\": \"^3.2.6\",\n \"@vitest/coverage-v8\": \"^4.0.18\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-astro\": \"^1.6.0\",\n \"eslint-plugin-jsx-a11y\": \"^6.10.2\",\n \"eslint-plugin-react\": \"^7.37.5\",\n \"eslint-plugin-react-hooks\": \"^7.0.1\",\n \"execa\": \"^9.6.1\",\n \"fs-extra\": \"^11.3.3\",\n \"globals\": \"^17.3.0\",\n \"husky\": \"^9.1.7\",\n \"lint-staged\": \"^16.2.7\",\n \"prettier\": \"^3.8.1\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.9.3\",\n \"typescript-eslint\": \"^8.56.0\",\n \"vitest\": \"^4.0.18\"\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAAA;AACA,SAAS,KAAK,uBAAuB;AACrC,SAAS,qBAAqB;;;ACF9B;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,KAAO;AAAA,IACL,SAAW;AAAA,EACb;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,KAAO;AAAA,IACP,OAAS;AAAA,IACT,MAAQ;AAAA,IACR,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,QAAU;AAAA,IACV,MAAQ;AAAA,IACR,YAAY;AAAA,IACZ,SAAW;AAAA,EACb;AAAA,EACA,eAAe;AAAA,IACb,QAAQ;AAAA,MACN;AAAA,IACF;AAAA,IACA,8BAA8B;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAgB;AAAA,IACd,eAAe;AAAA,IACf,qBAAqB;AAAA,IACrB,QAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,UAAY;AAAA,IACZ,MAAQ;AAAA,EACV;AAAA,EACA,iBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,QAAU;AAAA,IACV,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,6BAA6B;AAAA,IAC7B,OAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAW;AAAA,IACX,OAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,YAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,QAAU;AAAA,EACZ;AACF;;;AD5DA,OAAO,UAAU;AAGjB,IAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAE7D,IAAM,cAAc;AACpB,IAAM,cAAe,MAAM,gBAAgB,aAAa,QAAQ,IAAI,CAAC,KAAM,QAAQ,IAAI;AACvF,IAAM,kBAAkB,KAAK,QAAQ,WAAW,gBAAgB;AAChE,IAAM,qBAAqB,2BAA2B,WAAW;AAEjE,IAAM,qBAAqB,yBAAyB,oBAAoB,eAAe;AAEvF,IAAM,MAAM,IAAI,IAAI;AAAA,EAClB,SAAS,gBAAI;AAAA,EACb;AAAA,EACA,mBAAmB,CAAC,GAAG,oBAAI,IAAI,CAAC,iBAAiB,GAAG,kBAAkB,CAAC,CAAC;AAC1E,CAAC;AACD,IAAI,MAAM;","names":[]}
|
|
@@ -99,14 +99,24 @@ var RailwayProvider = class {
|
|
|
99
99
|
return secrets;
|
|
100
100
|
}
|
|
101
101
|
async getVariables(context) {
|
|
102
|
-
|
|
102
|
+
const env = context.options.env || "production";
|
|
103
|
+
const baseProjectName = context.config.deploy?.backend?.projectName;
|
|
104
|
+
if (!baseProjectName) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"Railway project name not found in nexical.yaml. Please configure 'deploy.backend.projectName'."
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const projectName = env === "production" ? baseProjectName : `${baseProjectName}-${env}`;
|
|
110
|
+
return {
|
|
111
|
+
RAILWAY_PROJECT_NAME: projectName
|
|
112
|
+
};
|
|
103
113
|
}
|
|
104
114
|
getCIConfig() {
|
|
105
115
|
return {
|
|
106
116
|
secrets: ["RAILWAY_API_TOKEN"],
|
|
107
117
|
variables: [],
|
|
108
118
|
installSteps: ["npm install -g @railway/cli"],
|
|
109
|
-
deploySteps: ["railway up --detach"]
|
|
119
|
+
deploySteps: ["railway up --detach --project=${{ vars.RAILWAY_PROJECT_NAME }}"]
|
|
110
120
|
};
|
|
111
121
|
}
|
|
112
122
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/deploy/providers/railway.ts"],"sourcesContent":["import path from 'node:path';\nimport { logger } from '@nexical/cli-core';\nimport { DeploymentProvider, DeploymentContext, CIConfig } from '../types';\nimport { execAsync } from '../utils';\n\nexport class RailwayProvider implements DeploymentProvider {\n name = 'railway';\n type = 'backend' as const;\n\n async provision(context: DeploymentContext): Promise<void> {\n const backendDir = path.join(context.cwd, 'apps/backend');\n const env = (context.options.env as string) || 'production';\n const baseProjectName = context.config.deploy?.backend?.projectName;\n\n if (!baseProjectName) {\n throw new Error(\n \"Railway project name not found in nexical.yaml. Please configure 'deploy.backend.projectName'.\",\n );\n }\n\n const railwayName = env === 'production' ? baseProjectName : `${baseProjectName}-${env}`;\n\n logger.info('Configuring Railway...');\n\n if (context.options.dryRun) {\n logger.info(`[Dry Run] Would check Railway status and init project \"${railwayName}\".`);\n return;\n }\n\n try {\n // We consciously DO NOT pass any RAILWAY_API_TOKEN to the subprocess.\n // The user may have an invalid token in their .env file (which process.env inherits).\n // We want to force the Railway CLI to use the locally logged-in user's credentials.\n const env = { ...process.env };\n delete env.RAILWAY_API_TOKEN;\n delete env.RAILWAY_TOKEN;\n\n logger.info('Using local Railway CLI credentials (environment variables stripped).');\n\n // Check status to see if we are linked to a project\n try {\n await execAsync('railway status', { cwd: backendDir, env });\n } catch (error: unknown) {\n const errMsg = error instanceof Error ? error.message : String(error);\n const stderr = (error as { stderr?: string }).stderr || '';\n const stdout = (error as { stdout?: string }).stdout || '';\n const fullError = `${errMsg} ${stderr} ${stdout}`;\n\n // If status fails, likely project doesn't exist locally or we aren't linked.\n if (\n fullError.includes('Project not found') ||\n fullError.includes('No project') ||\n fullError.includes('Project is deleted')\n ) {\n if (fullError.includes('Project is deleted')) {\n logger.info('[Railway] Project is deleted. Unlinking...');\n // If it's deleted, we might need to unlink first to clean up local config\n await execAsync('railway unlink', { cwd: backendDir }).catch(() => {});\n }\n const initCmd = `railway init --name ${railwayName}`;\n logger.info(`No active Railway project linked. Initializing with: ${initCmd}`);\n await execAsync(initCmd, { cwd: backendDir, env });\n } else if (\n fullError.includes('Invalid RAILWAY_API_TOKEN') ||\n fullError.includes('Unauthorized')\n ) {\n throw new Error('Railway authentication failed during status check.');\n } else {\n // Some other error (e.g. timeout), warn and try to proceed\n logger.warn(`Railway status check failed: ${errMsg}. Proceeding.`);\n }\n }\n\n logger.info(`Adding PostgreSQL service if missing for \"${railwayName}\"...`);\n const { stdout: status } = await execAsync('railway status', { cwd: backendDir, env }).catch(\n () => ({ stdout: '' }),\n );\n if (!status.includes('postgres')) {\n try {\n await execAsync('railway add --database postgres', { cwd: backendDir, env });\n } catch {\n logger.warn('Failed to auto-add PostgreSQL.');\n }\n }\n } catch (e: unknown) {\n // Rethrow explicit auth errors, otherwise warn\n const errMsg = e instanceof Error ? e.message : String(e);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stderr = (e as any).stderr || '';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stdout = (e as any).stdout || '';\n\n if (errMsg.includes('Railway authentication failed')) throw e;\n\n logger.error(`Railway setup failed with error: ${errMsg}`);\n if (stderr) logger.error(`[Railway stderr]: ${stderr}`);\n if (stdout) logger.info(`[Railway stdout]: ${stdout}`);\n\n logger.warn(\n 'Railway setup encountered an issue. Ensure you are logged in or have a valid token.',\n );\n }\n }\n\n private resolveToken(context: DeploymentContext): string | undefined {\n const options = context.config.deploy?.backend?.options || {};\n const tokenEnvVar = typeof options.tokenEnvVar === 'string' ? options.tokenEnvVar : undefined;\n return (\n process.env.RAILWAY_API_TOKEN?.trim() ||\n (tokenEnvVar ? process.env[tokenEnvVar]?.trim() : undefined) ||\n process.env.RAILWAY_TOKEN?.trim()\n );\n }\n\n async getSecrets(context: DeploymentContext): Promise<Record<string, string>> {\n const token = this.resolveToken(context);\n const secrets: Record<string, string> = {};\n\n if (!token) {\n // Strict check: Error if token is missing\n throw new Error(\n `Railway Token not found. Please provide it via:\\n` +\n `1. Setting RAILWAY_API_TOKEN in .env (Recommended)\\n` +\n `2. Configuring 'deploy.backend.options.tokenEnvVar' in nexical.yaml\\n` +\n `3. Setting RAILWAY_TOKEN in .env`,\n );\n }\n\n secrets['RAILWAY_API_TOKEN'] = token;\n return secrets;\n }\n\n async getVariables(context: DeploymentContext): Promise<Record<string, string>> {\n return {};\n }\n\n getCIConfig(): CIConfig {\n return {\n secrets: ['RAILWAY_API_TOKEN'],\n variables: [],\n installSteps: ['npm install -g @railway/cli'],\n deploySteps: ['railway up --detach'],\n };\n }\n}\n"],"mappings":"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
|
|
1
|
+
{"version":3,"sources":["../../../../src/deploy/providers/railway.ts"],"sourcesContent":["import path from 'node:path';\nimport { logger } from '@nexical/cli-core';\nimport { DeploymentProvider, DeploymentContext, CIConfig } from '../types';\nimport { execAsync } from '../utils';\n\nexport class RailwayProvider implements DeploymentProvider {\n name = 'railway';\n type = 'backend' as const;\n\n async provision(context: DeploymentContext): Promise<void> {\n const backendDir = path.join(context.cwd, 'apps/backend');\n const env = (context.options.env as string) || 'production';\n const baseProjectName = context.config.deploy?.backend?.projectName;\n\n if (!baseProjectName) {\n throw new Error(\n \"Railway project name not found in nexical.yaml. Please configure 'deploy.backend.projectName'.\",\n );\n }\n\n const railwayName = env === 'production' ? baseProjectName : `${baseProjectName}-${env}`;\n\n logger.info('Configuring Railway...');\n\n if (context.options.dryRun) {\n logger.info(`[Dry Run] Would check Railway status and init project \"${railwayName}\".`);\n return;\n }\n\n try {\n // We consciously DO NOT pass any RAILWAY_API_TOKEN to the subprocess.\n // The user may have an invalid token in their .env file (which process.env inherits).\n // We want to force the Railway CLI to use the locally logged-in user's credentials.\n const env = { ...process.env };\n delete env.RAILWAY_API_TOKEN;\n delete env.RAILWAY_TOKEN;\n\n logger.info('Using local Railway CLI credentials (environment variables stripped).');\n\n // Check status to see if we are linked to a project\n try {\n await execAsync('railway status', { cwd: backendDir, env });\n } catch (error: unknown) {\n const errMsg = error instanceof Error ? error.message : String(error);\n const stderr = (error as { stderr?: string }).stderr || '';\n const stdout = (error as { stdout?: string }).stdout || '';\n const fullError = `${errMsg} ${stderr} ${stdout}`;\n\n // If status fails, likely project doesn't exist locally or we aren't linked.\n if (\n fullError.includes('Project not found') ||\n fullError.includes('No project') ||\n fullError.includes('Project is deleted')\n ) {\n if (fullError.includes('Project is deleted')) {\n logger.info('[Railway] Project is deleted. Unlinking...');\n // If it's deleted, we might need to unlink first to clean up local config\n await execAsync('railway unlink', { cwd: backendDir }).catch(() => {});\n }\n const initCmd = `railway init --name ${railwayName}`;\n logger.info(`No active Railway project linked. Initializing with: ${initCmd}`);\n await execAsync(initCmd, { cwd: backendDir, env });\n } else if (\n fullError.includes('Invalid RAILWAY_API_TOKEN') ||\n fullError.includes('Unauthorized')\n ) {\n throw new Error('Railway authentication failed during status check.');\n } else {\n // Some other error (e.g. timeout), warn and try to proceed\n logger.warn(`Railway status check failed: ${errMsg}. Proceeding.`);\n }\n }\n\n logger.info(`Adding PostgreSQL service if missing for \"${railwayName}\"...`);\n const { stdout: status } = await execAsync('railway status', { cwd: backendDir, env }).catch(\n () => ({ stdout: '' }),\n );\n if (!status.includes('postgres')) {\n try {\n await execAsync('railway add --database postgres', { cwd: backendDir, env });\n } catch {\n logger.warn('Failed to auto-add PostgreSQL.');\n }\n }\n } catch (e: unknown) {\n // Rethrow explicit auth errors, otherwise warn\n const errMsg = e instanceof Error ? e.message : String(e);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stderr = (e as any).stderr || '';\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const stdout = (e as any).stdout || '';\n\n if (errMsg.includes('Railway authentication failed')) throw e;\n\n logger.error(`Railway setup failed with error: ${errMsg}`);\n if (stderr) logger.error(`[Railway stderr]: ${stderr}`);\n if (stdout) logger.info(`[Railway stdout]: ${stdout}`);\n\n logger.warn(\n 'Railway setup encountered an issue. Ensure you are logged in or have a valid token.',\n );\n }\n }\n\n private resolveToken(context: DeploymentContext): string | undefined {\n const options = context.config.deploy?.backend?.options || {};\n const tokenEnvVar = typeof options.tokenEnvVar === 'string' ? options.tokenEnvVar : undefined;\n return (\n process.env.RAILWAY_API_TOKEN?.trim() ||\n (tokenEnvVar ? process.env[tokenEnvVar]?.trim() : undefined) ||\n process.env.RAILWAY_TOKEN?.trim()\n );\n }\n\n async getSecrets(context: DeploymentContext): Promise<Record<string, string>> {\n const token = this.resolveToken(context);\n const secrets: Record<string, string> = {};\n\n if (!token) {\n // Strict check: Error if token is missing\n throw new Error(\n `Railway Token not found. Please provide it via:\\n` +\n `1. Setting RAILWAY_API_TOKEN in .env (Recommended)\\n` +\n `2. Configuring 'deploy.backend.options.tokenEnvVar' in nexical.yaml\\n` +\n `3. Setting RAILWAY_TOKEN in .env`,\n );\n }\n\n secrets['RAILWAY_API_TOKEN'] = token;\n return secrets;\n }\n\n async getVariables(context: DeploymentContext): Promise<Record<string, string>> {\n const env = (context.options.env as string) || 'production';\n const baseProjectName = context.config.deploy?.backend?.projectName;\n\n if (!baseProjectName) {\n throw new Error(\n \"Railway project name not found in nexical.yaml. Please configure 'deploy.backend.projectName'.\",\n );\n }\n\n const projectName = env === 'production' ? baseProjectName : `${baseProjectName}-${env}`;\n return {\n RAILWAY_PROJECT_NAME: projectName,\n };\n }\n\n getCIConfig(): CIConfig {\n return {\n secrets: ['RAILWAY_API_TOKEN'],\n variables: [],\n installSteps: ['npm install -g @railway/cli'],\n deploySteps: ['railway up --detach --project=${{ vars.RAILWAY_PROJECT_NAME }}'],\n };\n }\n}\n"],"mappings":"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","names":["env"]}
|
package/package.json
CHANGED
|
@@ -132,7 +132,19 @@ export class RailwayProvider implements DeploymentProvider {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
async getVariables(context: DeploymentContext): Promise<Record<string, string>> {
|
|
135
|
-
|
|
135
|
+
const env = (context.options.env as string) || 'production';
|
|
136
|
+
const baseProjectName = context.config.deploy?.backend?.projectName;
|
|
137
|
+
|
|
138
|
+
if (!baseProjectName) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
"Railway project name not found in nexical.yaml. Please configure 'deploy.backend.projectName'.",
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const projectName = env === 'production' ? baseProjectName : `${baseProjectName}-${env}`;
|
|
145
|
+
return {
|
|
146
|
+
RAILWAY_PROJECT_NAME: projectName,
|
|
147
|
+
};
|
|
136
148
|
}
|
|
137
149
|
|
|
138
150
|
getCIConfig(): CIConfig {
|
|
@@ -140,7 +152,7 @@ export class RailwayProvider implements DeploymentProvider {
|
|
|
140
152
|
secrets: ['RAILWAY_API_TOKEN'],
|
|
141
153
|
variables: [],
|
|
142
154
|
installSteps: ['npm install -g @railway/cli'],
|
|
143
|
-
deploySteps: ['railway up --detach'],
|
|
155
|
+
deploySteps: ['railway up --detach --project=${{ vars.RAILWAY_PROJECT_NAME }}'],
|
|
144
156
|
};
|
|
145
157
|
}
|
|
146
158
|
}
|
|
@@ -315,8 +315,10 @@ describe('RailwayProvider', () => {
|
|
|
315
315
|
});
|
|
316
316
|
|
|
317
317
|
describe('getVariables', () => {
|
|
318
|
-
it('should return
|
|
319
|
-
expect(await provider.getVariables(mockContext)).toEqual({
|
|
318
|
+
it('should return project name', async () => {
|
|
319
|
+
expect(await provider.getVariables(mockContext)).toEqual({
|
|
320
|
+
RAILWAY_PROJECT_NAME: 'my-proj',
|
|
321
|
+
});
|
|
320
322
|
});
|
|
321
323
|
});
|
|
322
324
|
|
package/tsconfig.json
CHANGED