contensis-cli 1.0.0-beta.94 → 1.0.0-beta.95

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/services/ContensisDevService.ts"],
4
- "sourcesContent": ["import to from 'await-to-js';\nimport { execFile, spawn } from 'child_process';\nimport inquirer from 'inquirer';\nimport path from 'path';\n\nimport { Role } from 'contensis-management-api/lib/models';\nimport { MigrateRequest } from 'migratortron';\n\nimport ContensisRole from './ContensisRoleService';\nimport { OutputOptionsConstructorArg } from '~/models/CliService';\nimport { EnvContentsToAdd } from '~/models/DevService';\nimport { mapSiteConfigYaml } from '~/mappers/DevRequests-to-RequestHanderSiteConfigYaml';\nimport { mapCIWorkflowContent } from '~/mappers/DevInit-to-CIWorkflow';\nimport {\n deployKeyRole,\n devKeyRole,\n} from '~/mappers/DevInit-to-RolePermissions';\nimport { appRootDir, readFile, writeFile } from '~/providers/file-provider';\nimport { diffFileContent } from '~/util/diff';\nimport { mergeDotEnvFileContents } from '~/util/dotenv';\nimport { findByIdOrName } from '~/util/find';\nimport { GitHelper } from '~/util/git';\nimport { jsonFormatter } from '~/util/json.formatter';\nimport { normaliseLineEndings } from '~/util/os';\nimport { stringifyYaml } from '~/util/yaml';\n\nclass ContensisDev extends ContensisRole {\n constructor(\n args: string[],\n outputOpts?: OutputOptionsConstructorArg,\n contensisOpts: Partial<MigrateRequest> = {}\n ) {\n super(args, outputOpts, contensisOpts);\n }\n\n DevelopmentInit = async (projectHome: string, opts: any) => {\n const { dryRun = false } = opts || {};\n const { currentEnv, currentProject, log, messages } = this;\n const contensis = await this.ConnectContensis();\n\n if (contensis) {\n // Retrieve keys list for env\n const [keysErr, apiKeys] = await contensis.apiKeys.GetKeys();\n if (keysErr) {\n log.error(messages.keys.noList(currentEnv));\n log.error(jsonFormatter(keysErr));\n return;\n }\n const apiKeyExists = (findKey: string) =>\n apiKeys?.find(\n k => k.name.trim().toLowerCase() === findKey?.trim().toLowerCase()\n );\n\n // Retrieve git info\n const git = new GitHelper(projectHome);\n\n // Retrieve ci workflow info\n const workflowFiles = git.workflows;\n\n // Set variables for performing operations and logging etc.\n let ciFileName = git.ciFileName;\n\n const devKeyName = `${git.name} development`;\n const devKeyDescription = `${git.name} [contensis-cli]`;\n let existingDevKey = apiKeyExists(devKeyName);\n\n const deployKeyName = `${git.name} deployment`;\n const deployKeyDescription = `${git.name} deploy [contensis-cli]`;\n\n let existingDeployKey = apiKeyExists(deployKeyName);\n\n const blockId = git.name;\n const errors = [] as AppError[];\n\n // Start render console output\n log.raw('');\n log.success(messages.devinit.intro());\n log.raw('');\n log.raw(\n log.infoText(\n messages.devinit.projectDetails(\n git.name,\n currentEnv,\n currentProject,\n git\n )\n )\n );\n log.raw(\n log.infoText(\n messages.devinit.developmentKey(devKeyName, !!existingDevKey)\n )\n );\n log.raw(\n log.infoText(\n messages.devinit.deploymentKey(deployKeyName, !!existingDeployKey)\n )\n );\n log.raw('');\n\n if (Array.isArray(workflowFiles) && workflowFiles.length > 1) {\n // Choose GitHub workflow file (if multiple)\n ({ ciFileName } = await inquirer.prompt([\n {\n type: 'list',\n message: messages.devinit.ciMultipleChoices(),\n name: 'ciFileName',\n choices: workflowFiles,\n default: workflowFiles.find(f => f.includes('docker')),\n },\n ]));\n log.raw('');\n git.ciFileName = ciFileName;\n }\n\n log.raw(log.infoText(messages.devinit.ciDetails(ciFileName)));\n\n // Look at the workflow file content and make updates\n const mappedWorkflow = mapCIWorkflowContent(this, git);\n\n log.help(messages.devinit.ciIntro(git));\n\n if (!dryRun) {\n // Confirm prompt\n const { confirm } = await inquirer.prompt([\n {\n type: 'confirm',\n message: messages.devinit.confirm(),\n name: 'confirm',\n default: false,\n },\n ]);\n log.raw('');\n if (!confirm) return;\n }\n\n // Access token prompt\n const { accessToken }: { accessToken: string } = await inquirer.prompt([\n {\n type: 'input',\n message: messages.devinit.accessTokenPrompt(),\n name: 'accessToken',\n },\n ]);\n log.raw('');\n\n // Magic happens...\n const checkpoint = (op: string) => {\n if (errors.length) throw errors[0];\n else log.debug(`${op} completed ok`);\n return true;\n };\n\n // Arrange API keys for development and deployment\n const [getRolesErr, roles] = await to(contensis.roles.GetRoles());\n if (!roles && getRolesErr) errors.push(getRolesErr);\n checkpoint(`fetched ${roles?.length} roles`);\n if (dryRun) {\n checkpoint(`skip api key creation (dry-run)`);\n } else {\n existingDevKey = await this.CreateOrUpdateApiKey(\n existingDevKey,\n devKeyName,\n devKeyDescription\n );\n checkpoint('dev key created');\n\n existingDeployKey = await this.CreateOrUpdateApiKey(\n existingDeployKey,\n deployKeyName,\n deployKeyDescription\n );\n checkpoint('deploy key created');\n\n // Ensure dev API key is assigned to a role\n let existingDevRole = findByIdOrName(roles || [], devKeyName, true) as\n | Role\n | undefined;\n existingDevRole = await this.CreateOrUpdateRole(\n existingDevRole,\n devKeyRole(devKeyName, devKeyDescription)\n );\n checkpoint('dev key role assigned');\n log.success(messages.devinit.createDevKey(devKeyName, true));\n\n // Ensure deploy API key is assigned to a role with the right permissions\n let existingDeployRole = findByIdOrName(\n roles || [],\n deployKeyName,\n true\n ) as Role | undefined;\n existingDeployRole = await this.CreateOrUpdateRole(\n existingDeployRole,\n deployKeyRole(deployKeyName, deployKeyDescription)\n );\n\n checkpoint('deploy key role assigned');\n log.success(messages.devinit.createDeployKey(deployKeyName, true));\n checkpoint('api keys done');\n }\n\n // Update or create a file called .env in project home\n const envContentsToAdd: EnvContentsToAdd = {\n ALIAS: currentEnv,\n PROJECT: currentProject,\n };\n if (accessToken) envContentsToAdd['ACCESS_TOKEN'] = accessToken;\n\n const envFilePath = `${projectHome}/.env`;\n const existingEnvFile = readFile(envFilePath);\n const envFileLines = mergeDotEnvFileContents(\n (existingEnvFile || '').split('\\n').filter(l => !!l),\n envContentsToAdd\n );\n const newEnvFileContent = normaliseLineEndings(\n `${envFileLines.join('\\n')}\\n`\n );\n const envDiff = diffFileContent(existingEnvFile || '', newEnvFileContent);\n\n if (dryRun) {\n if (envDiff) {\n log.info(`updating .env file ${envFilePath}: ${envDiff}`);\n log.raw('');\n }\n checkpoint('skip .env file update (dry-run)');\n } else {\n if (envDiff) log.info(`updating .env file ${envFilePath}`);\n writeFile(envFilePath, envFileLines.join('\\n'));\n checkpoint('.env file updated');\n log.success(messages.devinit.writeEnvFile());\n // log.help(messages.devinit.useEnvFileTip());\n }\n\n // Update CI file -- different for GH/GL -- create a sample one with build?\n if (dryRun) {\n if (mappedWorkflow?.diff) {\n log.info(`updating${ciFileName} file: ${mappedWorkflow.diff}`);\n log.raw('');\n }\n checkpoint('skip CI file update (dry-run)');\n //log.object(ciFileLines);\n } else {\n if (mappedWorkflow?.diff) log.info(`updating${ciFileName} file`);\n writeFile(git.ciFilePath, [].join('\\n'));\n log.success(messages.devinit.writeCiFile(`./${ciFileName}`));\n log.info(\n messages.devinit.ciBlockTip(blockId, currentEnv, currentProject)\n );\n checkpoint('CI file updated');\n }\n\n // Echo Deployment API key to console, ask user to add secrets to repo\n log.warning(messages.devinit.addGitSecretsIntro());\n log.help(\n messages.devinit.addGitSecretsHelp(\n git,\n existingDeployKey?.id,\n existingDeployKey?.sharedSecret\n )\n );\n\n if (dryRun) {\n log.success(messages.devinit.dryRun());\n log.help(messages.devinit.noChanges());\n } else {\n log.success(messages.devinit.success());\n log.help(messages.devinit.startProjectTip());\n }\n }\n };\n\n ExecRequestHandler = async (blockIds: string[], overrideArgs?: string[]) => {\n // if no request handler exe\n // download it.\n\n // if update arg, redownload it\n\n const { log } = this;\n // const getPrefixOld = log.getPrefix;\n const exeHome = path.join(appRootDir, 'reqhan');\n const exe = 'Zengenti.Contensis.RequestHandler.LocalDevelopment';\n const exePath = path.join(exeHome, exe);\n const siteConfigPath = path.join(appRootDir, 'site_config.yaml');\n\n const siteConfig = await mapSiteConfigYaml(this);\n writeFile('site_config.yaml', stringifyYaml(siteConfig));\n\n const args = overrideArgs\n ? typeof overrideArgs?.[0] === 'string' &&\n overrideArgs[0].includes(' ', 2)\n ? overrideArgs[0].split(' ')\n : overrideArgs\n : []; // args could be [ '-c .\\\\site_config.yaml' ] or [ '-c', '.\\\\site_config.yaml' ]\n\n // Add required args\n if (!args.find(a => a === '-c')) args.push('-c', siteConfigPath);\n\n // const child = execFile(exePath, args);\n\n const child = spawn(exePath, args, { stdio: 'inherit' });\n\n // log.raw('');\n log.info(`Launching request handler...`);\n if (overrideArgs?.length)\n this.log.warning(\n `Spawning process with supplied args: ${JSON.stringify(\n child.spawnargs,\n null,\n 2\n )}`\n );\n\n let isRunning = false;\n\n // Log child output through event listeners\n child?.stdout?.on('data', data => {\n isRunning = true;\n log.raw(data);\n });\n\n child?.stderr?.on('data', data => {\n log.error(data);\n });\n\n child.on('spawn', () => {\n isRunning = true;\n log.help(\n `You may see a firewall popup requesting network access, it is safe to approve`\n );\n // log.getPrefix = () => Logger.infoText(`[rqh]`);\n });\n\n child.on('exit', code => {\n isRunning = false;\n\n log[code === 0 ? 'success' : 'warning'](\n `Request handler exited with code ${code}\\n`\n );\n });\n\n child.on('error', error => {\n isRunning = false;\n log.error(`Could not launch request handler due to error \\n${error}`);\n });\n\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n // keep the method running until we can return\n while (true === true) {\n if (!isRunning) {\n // log.getPrefix = getPrefixOld; // restore logger state\n return;\n }\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n };\n}\nexport const devCommand = (\n commandArgs: string[],\n outputOpts: OutputOptionsConstructorArg,\n contensisOpts: Partial<MigrateRequest> = {}\n) => {\n return new ContensisDev(['', '', ...commandArgs], outputOpts, contensisOpts);\n};\n\nexport default ContensisDev;\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import to from 'await-to-js';\nimport { execFile, spawn } from 'child_process';\nimport inquirer from 'inquirer';\nimport path from 'path';\n\nimport { Role } from 'contensis-management-api/lib/models';\nimport { MigrateRequest } from 'migratortron';\n\nimport ContensisRole from './ContensisRoleService';\nimport { OutputOptionsConstructorArg } from '~/models/CliService';\nimport { EnvContentsToAdd } from '~/models/DevService';\nimport { mapSiteConfigYaml } from '~/mappers/DevRequests-to-RequestHanderSiteConfigYaml';\nimport { mapCIWorkflowContent } from '~/mappers/DevInit-to-CIWorkflow';\nimport {\n deployKeyRole,\n devKeyRole,\n} from '~/mappers/DevInit-to-RolePermissions';\nimport { appRootDir, readFile, writeFile } from '~/providers/file-provider';\nimport { diffFileContent } from '~/util/diff';\nimport { mergeDotEnvFileContents } from '~/util/dotenv';\nimport { findByIdOrName } from '~/util/find';\nimport { GitHelper } from '~/util/git';\nimport { jsonFormatter } from '~/util/json.formatter';\nimport { normaliseLineEndings, winSlash } from '~/util/os';\nimport { stringifyYaml } from '~/util/yaml';\n\nclass ContensisDev extends ContensisRole {\n constructor(\n args: string[],\n outputOpts?: OutputOptionsConstructorArg,\n contensisOpts: Partial<MigrateRequest> = {}\n ) {\n super(args, outputOpts, contensisOpts);\n }\n\n DevelopmentInit = async (projectHome: string, opts: any) => {\n const { dryRun = false } = opts || {};\n const { currentEnv, currentProject, log, messages } = this;\n const contensis = await this.ConnectContensis();\n\n if (contensis) {\n // Retrieve keys list for env\n const [keysErr, apiKeys] = await contensis.apiKeys.GetKeys();\n if (keysErr) {\n log.error(messages.keys.noList(currentEnv));\n log.error(jsonFormatter(keysErr));\n return;\n }\n const apiKeyExists = (findKey: string) =>\n apiKeys?.find(\n k => k.name.trim().toLowerCase() === findKey?.trim().toLowerCase()\n );\n\n // Retrieve git info\n const git = new GitHelper(projectHome);\n\n // Retrieve ci workflow info\n const workflowFiles = git.workflows;\n\n // Set variables for performing operations and logging etc.\n let ciFileName = git.ciFileName;\n\n const devKeyName = `${git.name} development`;\n const devKeyDescription = `${git.name} [contensis-cli]`;\n let existingDevKey = apiKeyExists(devKeyName);\n\n const deployKeyName = `${git.name} deployment`;\n const deployKeyDescription = `${git.name} deploy [contensis-cli]`;\n\n let existingDeployKey = apiKeyExists(deployKeyName);\n\n const blockId = git.name;\n const errors = [] as AppError[];\n\n // Start render console output\n log.raw('');\n log.success(messages.devinit.intro());\n log.raw('');\n log.raw(\n log.infoText(\n messages.devinit.projectDetails(\n git.name,\n currentEnv,\n currentProject,\n git\n )\n )\n );\n log.raw(\n log.infoText(\n messages.devinit.developmentKey(devKeyName, !!existingDevKey)\n )\n );\n log.raw(\n log.infoText(\n messages.devinit.deploymentKey(deployKeyName, !!existingDeployKey)\n )\n );\n log.raw('');\n\n if (Array.isArray(workflowFiles) && workflowFiles.length > 1) {\n // Choose GitHub workflow file (if multiple)\n ({ ciFileName } = await inquirer.prompt([\n {\n type: 'list',\n prefix: '\u29F0',\n message: messages.devinit.ciMultipleChoices(),\n name: 'ciFileName',\n choices: workflowFiles,\n default: workflowFiles.find(f => f.includes('docker')),\n },\n ]));\n log.raw('');\n git.ciFileName = ciFileName;\n }\n\n log.raw(log.infoText(messages.devinit.ciDetails(ciFileName)));\n\n // Look at the workflow file content and make updates\n const mappedWorkflow = await mapCIWorkflowContent(this, git);\n\n log.help(messages.devinit.ciIntro(git));\n\n if (!dryRun) {\n // Confirm prompt\n const { confirm } = await inquirer.prompt([\n {\n type: 'confirm',\n message: messages.devinit.confirm(),\n name: 'confirm',\n default: false,\n },\n ]);\n log.raw('');\n if (!confirm) return;\n }\n\n // Access token prompt\n const { accessToken }: { accessToken: string } = await inquirer.prompt([\n {\n type: 'input',\n prefix: '\uD83D\uDEE1\uFE0F',\n message: messages.devinit.accessTokenPrompt(),\n name: 'accessToken',\n },\n ]);\n log.raw('');\n\n // Magic happens...\n const checkpoint = (op: string) => {\n if (errors.length) throw errors[0];\n else log.debug(`${op} completed ok`);\n return true;\n };\n\n // Arrange API keys for development and deployment\n const [getRolesErr, roles] = await to(contensis.roles.GetRoles());\n if (!roles && getRolesErr) errors.push(getRolesErr);\n checkpoint(`fetched ${roles?.length} roles`);\n if (dryRun) {\n checkpoint(`skip api key creation (dry-run)`);\n } else {\n existingDevKey = await this.CreateOrUpdateApiKey(\n existingDevKey,\n devKeyName,\n devKeyDescription\n );\n checkpoint('dev key created');\n\n existingDeployKey = await this.CreateOrUpdateApiKey(\n existingDeployKey,\n deployKeyName,\n deployKeyDescription\n );\n checkpoint('deploy key created');\n\n // Ensure dev API key is assigned to a role\n let existingDevRole = findByIdOrName(roles || [], devKeyName, true) as\n | Role\n | undefined;\n existingDevRole = await this.CreateOrUpdateRole(\n existingDevRole,\n devKeyRole(devKeyName, devKeyDescription)\n );\n checkpoint('dev key role assigned');\n log.success(messages.devinit.createDevKey(devKeyName, true));\n\n // Ensure deploy API key is assigned to a role with the right permissions\n let existingDeployRole = findByIdOrName(\n roles || [],\n deployKeyName,\n true\n ) as Role | undefined;\n existingDeployRole = await this.CreateOrUpdateRole(\n existingDeployRole,\n deployKeyRole(deployKeyName, deployKeyDescription)\n );\n\n checkpoint('deploy key role assigned');\n log.success(messages.devinit.createDeployKey(deployKeyName, true));\n checkpoint('api keys done');\n }\n\n // Update or create a file called .env in project home\n const envContentsToAdd: EnvContentsToAdd = {\n ALIAS: currentEnv,\n PROJECT: currentProject,\n };\n if (accessToken) envContentsToAdd['ACCESS_TOKEN'] = accessToken;\n\n const envFilePath = `${projectHome}/.env`;\n const existingEnvFile = readFile(envFilePath);\n const envFileLines = mergeDotEnvFileContents(\n (existingEnvFile || '').split('\\n').filter(l => !!l),\n envContentsToAdd\n );\n const newEnvFileContent = normaliseLineEndings(\n `${envFileLines.join('\\n')}\\n`\n );\n const envDiff = diffFileContent(existingEnvFile || '', newEnvFileContent);\n\n if (dryRun) {\n if (envDiff) {\n log.info(`Updating .env file ${winSlash(envFilePath)}:\\n${envDiff}`);\n log.raw('');\n }\n checkpoint('skip .env file update (dry-run)');\n } else {\n if (envDiff) log.info(`updating .env file ${winSlash(envFilePath)}`);\n writeFile(envFilePath, envFileLines.join('\\n'));\n checkpoint('.env file updated');\n log.success(messages.devinit.writeEnvFile());\n // log.help(messages.devinit.useEnvFileTip());\n }\n\n // Update CI file -- different for GH/GL\n if (mappedWorkflow?.diff) {\n log.info(\n `Updating ${winSlash(ciFileName)} file:\\n${mappedWorkflow.diff}`\n );\n log.raw('');\n }\n if (dryRun) {\n checkpoint('skip CI file update (dry-run)');\n //log.object(ciFileLines);\n } else {\n if (mappedWorkflow?.newWorkflow) {\n if (mappedWorkflow?.diff) {\n writeFile(git.ciFilePath, mappedWorkflow.newWorkflow);\n log.success(messages.devinit.writeCiFile(`./${ciFileName}`));\n log.info(\n messages.devinit.ciBlockTip(blockId, currentEnv, currentProject)\n );\n } else {\n log.info(messages.devinit.ciFileNoChanges(`./${ciFileName}`));\n }\n checkpoint('CI file updated');\n }\n }\n\n // Echo Deployment API key to console, ask user to add secrets to repo\n log.warning(messages.devinit.addGitSecretsIntro());\n log.help(\n messages.devinit.addGitSecretsHelp(\n git,\n existingDeployKey?.id,\n existingDeployKey?.sharedSecret\n )\n );\n\n if (dryRun) {\n log.success(messages.devinit.dryRun());\n log.help(messages.devinit.noChanges());\n } else {\n log.success(messages.devinit.success());\n log.help(messages.devinit.startProjectTip());\n }\n }\n };\n\n ExecRequestHandler = async (blockIds: string[], overrideArgs?: string[]) => {\n // if no request handler exe\n // download it.\n\n // if update arg, redownload it\n\n const { log } = this;\n // const getPrefixOld = log.getPrefix;\n const exeHome = path.join(appRootDir, 'reqhan');\n const exe = 'Zengenti.Contensis.RequestHandler.LocalDevelopment';\n const exePath = path.join(exeHome, exe);\n const siteConfigPath = path.join(appRootDir, 'site_config.yaml');\n\n const siteConfig = await mapSiteConfigYaml(this);\n writeFile('site_config.yaml', stringifyYaml(siteConfig));\n\n const args = overrideArgs\n ? typeof overrideArgs?.[0] === 'string' &&\n overrideArgs[0].includes(' ', 2)\n ? overrideArgs[0].split(' ')\n : overrideArgs\n : []; // args could be [ '-c .\\\\site_config.yaml' ] or [ '-c', '.\\\\site_config.yaml' ]\n\n // Add required args\n if (!args.find(a => a === '-c')) args.push('-c', siteConfigPath);\n\n // const child = execFile(exePath, args);\n\n const child = spawn(exePath, args, { stdio: 'inherit' });\n\n // log.raw('');\n log.info(`Launching request handler...`);\n if (overrideArgs?.length)\n this.log.warning(\n `Spawning process with supplied args: ${JSON.stringify(\n child.spawnargs,\n null,\n 2\n )}`\n );\n\n let isRunning = false;\n\n // Log child output through event listeners\n child?.stdout?.on('data', data => {\n isRunning = true;\n log.raw(data);\n });\n\n child?.stderr?.on('data', data => {\n log.error(data);\n });\n\n child.on('spawn', () => {\n isRunning = true;\n log.help(\n `You may see a firewall popup requesting network access, it is safe to approve`\n );\n // log.getPrefix = () => Logger.infoText(`[rqh]`);\n });\n\n child.on('exit', code => {\n isRunning = false;\n\n log[code === 0 ? 'success' : 'warning'](\n `Request handler exited with code ${code}\\n`\n );\n });\n\n child.on('error', error => {\n isRunning = false;\n log.error(`Could not launch request handler due to error \\n${error}`);\n });\n\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n // keep the method running until we can return\n while (true === true) {\n if (!isRunning) {\n // log.getPrefix = getPrefixOld; // restore logger state\n return;\n }\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n };\n}\nexport const devCommand = (\n commandArgs: string[],\n outputOpts: OutputOptionsConstructorArg,\n contensisOpts: Partial<MigrateRequest> = {}\n) => {\n return new ContensisDev(['', '', ...commandArgs], outputOpts, contensisOpts);\n};\n\nexport default ContensisDev;\n"],
5
+ "mappings": "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",
6
6
  "names": ["ContensisRole", "inquirer", "to", "path"]
7
7
  }
package/dist/util/diff.js CHANGED
@@ -54,18 +54,24 @@ const diffFileContent = (existingContent, newContent) => {
54
54
  ...diffRanges.map((d) => d.startLineNumber.toString().length)
55
55
  );
56
56
  const lnSpaces = Array(lnSpaceLength).join(" ");
57
+ let needsNewLine = false;
57
58
  for (let i = 0; i < diffRanges.length; i++) {
58
59
  const part = diffRanges[i];
59
60
  if (part.added || part.removed) {
60
61
  const colour = part.added ? "green" : part.removed ? "red" : "grey";
61
- if (part.value !== "\n")
62
+ if (part.value !== "\n") {
63
+ if (needsNewLine)
64
+ output.push("\n### --");
62
65
  output.push(
63
66
  `
64
67
  ${part.value.split("\n").map(
65
68
  (ln, idx) => ln.trim() !== "" ? `${part.startLineNumber ? part.startLineNumber + idx : lnSpaces}${part.added ? "+" : part.removed ? "-" : " "} ${import_chalk.default[colour](`${ln}`)}` : ln
66
69
  ).join("\n")}`
67
70
  );
68
- }
71
+ } else
72
+ needsNewLine = true;
73
+ } else
74
+ needsNewLine = true;
69
75
  }
70
76
  return output.join("");
71
77
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/util/diff.ts"],
4
- "sourcesContent": ["import chalk from 'chalk';\nimport { Change, diffLines } from 'diff';\nimport { normaliseLineEndings } from './os';\n\nexport const diffLogStrings = (updates: string, previous: string) => {\n const lastFewLines = previous.split('\\n').slice(-10);\n const incomingLines = updates.split('\\n');\n\n // Find the line indices in the incoming lines\n // of the last few lines previously rendered\n const incomingLineIndices = [];\n for (const lastRenderedLine of lastFewLines) {\n if (lastRenderedLine.length > 10)\n incomingLineIndices.push(incomingLines.lastIndexOf(lastRenderedLine));\n }\n\n // Get the new lines from the next position on from the last of the already shown lines\n const differentFromPos = Math.max(...incomingLineIndices) + 1 || 0;\n // Return just the incoming lines from the position we matched\n return incomingLines.slice(differentFromPos).join('\\n');\n};\n\nexport const diffFileContent = (\n existingContent: string,\n newContent: string\n) => {\n const existingContentNormalised = normaliseLineEndings(existingContent, '\\n');\n const newContentNormalised = normaliseLineEndings(newContent, '\\n');\n\n const diff = diffLines(existingContentNormalised, newContentNormalised, {\n newlineIsToken: true,\n });\n const diffRanges = addDiffPositionInfo(diff);\n\n // Create formatted output for console\n const output: string[] = [];\n const lnSpaceLength = Math.max(\n ...diffRanges.map(d => d.startLineNumber.toString().length)\n );\n\n const lnSpaces = Array(lnSpaceLength).join(' ');\n\n for (let i = 0; i < diffRanges.length; i++) {\n const part = diffRanges[i];\n if (part.added || part.removed) {\n const colour = part.added ? 'green' : part.removed ? 'red' : 'grey';\n\n if (part.value !== '\\n')\n output.push(\n `\\n${part.value\n .split('\\n')\n .map((ln, idx) =>\n ln.trim() !== ''\n ? `${\n part.startLineNumber ? part.startLineNumber + idx : lnSpaces\n }${part.added ? '+' : part.removed ? '-' : ' '} ${chalk[\n colour\n ](`${ln}`)}`\n : ln\n )\n .join('\\n')}`\n );\n }\n }\n\n return output.join('');\n // return retOutput.endsWith('\\n') ? retOutput : `${retOutput}\\n`;\n};\n\nconst addDiffPositionInfo = (diff: Change[]) => {\n const diffRanges: (Change & {\n startLineNumber: number;\n startColumn: number;\n endLineNumber: number;\n endColumn: number;\n })[] = [];\n\n let lineNumber = 0;\n let column = 0;\n for (let partIndex = 0; partIndex < diff.length; partIndex++) {\n const part = diff[partIndex];\n\n // // Skip any parts that aren't in `after`\n // if (part.removed === true) {\n // continue;\n // }\n\n const startLineNumber = lineNumber;\n const startColumn = column;\n\n // Split the part into lines. Loop throug these lines to find\n // the line no. and column at the end of this part.\n const substring = part.value;\n const lines = substring.split('\\n');\n lines.forEach((line, lineIndex) => {\n // The first `line` is actually just a continuation of the last line\n if (lineIndex === 0) {\n column += line.length;\n // All other lines come after a line break.\n } else if (lineIndex > 0) {\n lineNumber += 1;\n column = line.length;\n }\n });\n\n // Save a range for all of the parts with position info added\n if (part.added === true || part.removed === true) {\n diffRanges.push({\n startLineNumber: startLineNumber + 1,\n startColumn: startColumn,\n endLineNumber: lineNumber,\n endColumn: column,\n ...part,\n });\n }\n }\n return diffRanges;\n};\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,kBAAkC;AAClC,gBAAqC;AAE9B,MAAM,iBAAiB,CAAC,SAAiB,aAAqB;AACnE,QAAM,eAAe,SAAS,MAAM,IAAI,EAAE,MAAM,GAAG;AACnD,QAAM,gBAAgB,QAAQ,MAAM,IAAI;AAIxC,QAAM,sBAAsB,CAAC;AAC7B,aAAW,oBAAoB,cAAc;AAC3C,QAAI,iBAAiB,SAAS;AAC5B,0BAAoB,KAAK,cAAc,YAAY,gBAAgB,CAAC;AAAA,EACxE;AAGA,QAAM,mBAAmB,KAAK,IAAI,GAAG,mBAAmB,IAAI,KAAK;AAEjE,SAAO,cAAc,MAAM,gBAAgB,EAAE,KAAK,IAAI;AACxD;AAEO,MAAM,kBAAkB,CAC7B,iBACA,eACG;AACH,QAAM,gCAA4B,gCAAqB,iBAAiB,IAAI;AAC5E,QAAM,2BAAuB,gCAAqB,YAAY,IAAI;AAElE,QAAM,WAAO,uBAAU,2BAA2B,sBAAsB;AAAA,IACtE,gBAAgB;AAAA,EAClB,CAAC;AACD,QAAM,aAAa,oBAAoB,IAAI;AAG3C,QAAM,SAAmB,CAAC;AAC1B,QAAM,gBAAgB,KAAK;AAAA,IACzB,GAAG,WAAW,IAAI,OAAK,EAAE,gBAAgB,SAAS,EAAE,MAAM;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,aAAa,EAAE,KAAK,GAAG;AAE9C,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,OAAO,WAAW;AACxB,QAAI,KAAK,SAAS,KAAK,SAAS;AAC9B,YAAM,SAAS,KAAK,QAAQ,UAAU,KAAK,UAAU,QAAQ;AAE7D,UAAI,KAAK,UAAU;AACjB,eAAO;AAAA,UACL;AAAA,EAAK,KAAK,MACP,MAAM,IAAI,EACV;AAAA,YAAI,CAAC,IAAI,QACR,GAAG,KAAK,MAAM,KACV,GACE,KAAK,kBAAkB,KAAK,kBAAkB,MAAM,WACnD,KAAK,QAAQ,MAAM,KAAK,UAAU,MAAM,OAAO,aAAAA,QAChD,QACA,GAAG,IAAI,MACT;AAAA,UACN,EACC,KAAK,IAAI;AAAA,QACd;AAAA,IACJ;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,EAAE;AAEvB;AAEA,MAAM,sBAAsB,CAAC,SAAmB;AAC9C,QAAM,aAKC,CAAC;AAER,MAAI,aAAa;AACjB,MAAI,SAAS;AACb,WAAS,YAAY,GAAG,YAAY,KAAK,QAAQ,aAAa;AAC5D,UAAM,OAAO,KAAK;AAOlB,UAAM,kBAAkB;AACxB,UAAM,cAAc;AAIpB,UAAM,YAAY,KAAK;AACvB,UAAM,QAAQ,UAAU,MAAM,IAAI;AAClC,UAAM,QAAQ,CAAC,MAAM,cAAc;AAEjC,UAAI,cAAc,GAAG;AACnB,kBAAU,KAAK;AAAA,MAEjB,WAAW,YAAY,GAAG;AACxB,sBAAc;AACd,iBAAS,KAAK;AAAA,MAChB;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,UAAU,QAAQ,KAAK,YAAY,MAAM;AAChD,iBAAW,KAAK;AAAA,QACd,iBAAiB,kBAAkB;AAAA,QACnC;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,QACX,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["import chalk from 'chalk';\nimport { Change, diffLines } from 'diff';\nimport { normaliseLineEndings } from './os';\n\nexport const diffLogStrings = (updates: string, previous: string) => {\n const lastFewLines = previous.split('\\n').slice(-10);\n const incomingLines = updates.split('\\n');\n\n // Find the line indices in the incoming lines\n // of the last few lines previously rendered\n const incomingLineIndices = [];\n for (const lastRenderedLine of lastFewLines) {\n if (lastRenderedLine.length > 10)\n incomingLineIndices.push(incomingLines.lastIndexOf(lastRenderedLine));\n }\n\n // Get the new lines from the next position on from the last of the already shown lines\n const differentFromPos = Math.max(...incomingLineIndices) + 1 || 0;\n // Return just the incoming lines from the position we matched\n return incomingLines.slice(differentFromPos).join('\\n');\n};\n\nexport const diffFileContent = (\n existingContent: string,\n newContent: string\n) => {\n const existingContentNormalised = normaliseLineEndings(existingContent, '\\n');\n const newContentNormalised = normaliseLineEndings(newContent, '\\n');\n\n const diff = diffLines(existingContentNormalised, newContentNormalised, {\n newlineIsToken: true,\n });\n const diffRanges = addDiffPositionInfo(diff);\n\n // Create formatted output for console\n const output: string[] = [];\n const lnSpaceLength = Math.max(\n ...diffRanges.map(d => d.startLineNumber.toString().length)\n );\n\n const lnSpaces = Array(lnSpaceLength).join(' ');\n\n let needsNewLine = false;\n for (let i = 0; i < diffRanges.length; i++) {\n const part = diffRanges[i];\n if (part.added || part.removed) {\n const colour = part.added ? 'green' : part.removed ? 'red' : 'grey';\n\n if (part.value !== '\\n') {\n if (needsNewLine) output.push('\\n### --');\n output.push(\n `\\n${part.value\n .split('\\n')\n .map((ln, idx) =>\n ln.trim() !== ''\n ? `${\n part.startLineNumber ? part.startLineNumber + idx : lnSpaces\n }${part.added ? '+' : part.removed ? '-' : ' '} ${chalk[\n colour\n ](`${ln}`)}`\n : ln\n )\n .join('\\n')}`\n );\n } else needsNewLine = true;\n } else needsNewLine = true;\n }\n\n return output.join('');\n};\n\nconst addDiffPositionInfo = (diff: Change[]) => {\n const diffRanges: (Change & {\n startLineNumber: number;\n startColumn: number;\n endLineNumber: number;\n endColumn: number;\n })[] = [];\n\n let lineNumber = 0;\n let column = 0;\n for (let partIndex = 0; partIndex < diff.length; partIndex++) {\n const part = diff[partIndex];\n\n // // Skip any parts that aren't in `after`\n // if (part.removed === true) {\n // continue;\n // }\n\n const startLineNumber = lineNumber;\n const startColumn = column;\n\n // Split the part into lines. Loop throug these lines to find\n // the line no. and column at the end of this part.\n const substring = part.value;\n const lines = substring.split('\\n');\n lines.forEach((line, lineIndex) => {\n // The first `line` is actually just a continuation of the last line\n if (lineIndex === 0) {\n column += line.length;\n // All other lines come after a line break.\n } else if (lineIndex > 0) {\n lineNumber += 1;\n column = line.length;\n }\n });\n\n // Save a range for all of the parts with position info added\n if (part.added === true || part.removed === true) {\n diffRanges.push({\n startLineNumber: startLineNumber + 1,\n startColumn: startColumn,\n endLineNumber: lineNumber,\n endColumn: column,\n ...part,\n });\n }\n }\n return diffRanges;\n};\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,kBAAkC;AAClC,gBAAqC;AAE9B,MAAM,iBAAiB,CAAC,SAAiB,aAAqB;AACnE,QAAM,eAAe,SAAS,MAAM,IAAI,EAAE,MAAM,GAAG;AACnD,QAAM,gBAAgB,QAAQ,MAAM,IAAI;AAIxC,QAAM,sBAAsB,CAAC;AAC7B,aAAW,oBAAoB,cAAc;AAC3C,QAAI,iBAAiB,SAAS;AAC5B,0BAAoB,KAAK,cAAc,YAAY,gBAAgB,CAAC;AAAA,EACxE;AAGA,QAAM,mBAAmB,KAAK,IAAI,GAAG,mBAAmB,IAAI,KAAK;AAEjE,SAAO,cAAc,MAAM,gBAAgB,EAAE,KAAK,IAAI;AACxD;AAEO,MAAM,kBAAkB,CAC7B,iBACA,eACG;AACH,QAAM,gCAA4B,gCAAqB,iBAAiB,IAAI;AAC5E,QAAM,2BAAuB,gCAAqB,YAAY,IAAI;AAElE,QAAM,WAAO,uBAAU,2BAA2B,sBAAsB;AAAA,IACtE,gBAAgB;AAAA,EAClB,CAAC;AACD,QAAM,aAAa,oBAAoB,IAAI;AAG3C,QAAM,SAAmB,CAAC;AAC1B,QAAM,gBAAgB,KAAK;AAAA,IACzB,GAAG,WAAW,IAAI,OAAK,EAAE,gBAAgB,SAAS,EAAE,MAAM;AAAA,EAC5D;AAEA,QAAM,WAAW,MAAM,aAAa,EAAE,KAAK,GAAG;AAE9C,MAAI,eAAe;AACnB,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,OAAO,WAAW;AACxB,QAAI,KAAK,SAAS,KAAK,SAAS;AAC9B,YAAM,SAAS,KAAK,QAAQ,UAAU,KAAK,UAAU,QAAQ;AAE7D,UAAI,KAAK,UAAU,MAAM;AACvB,YAAI;AAAc,iBAAO,KAAK,UAAU;AACxC,eAAO;AAAA,UACL;AAAA,EAAK,KAAK,MACP,MAAM,IAAI,EACV;AAAA,YAAI,CAAC,IAAI,QACR,GAAG,KAAK,MAAM,KACV,GACE,KAAK,kBAAkB,KAAK,kBAAkB,MAAM,WACnD,KAAK,QAAQ,MAAM,KAAK,UAAU,MAAM,OAAO,aAAAA,QAChD,QACA,GAAG,IAAI,MACT;AAAA,UACN,EACC,KAAK,IAAI;AAAA,QACd;AAAA,MACF;AAAO,uBAAe;AAAA,IACxB;AAAO,qBAAe;AAAA,EACxB;AAEA,SAAO,OAAO,KAAK,EAAE;AACvB;AAEA,MAAM,sBAAsB,CAAC,SAAmB;AAC9C,QAAM,aAKC,CAAC;AAER,MAAI,aAAa;AACjB,MAAI,SAAS;AACb,WAAS,YAAY,GAAG,YAAY,KAAK,QAAQ,aAAa;AAC5D,UAAM,OAAO,KAAK;AAOlB,UAAM,kBAAkB;AACxB,UAAM,cAAc;AAIpB,UAAM,YAAY,KAAK;AACvB,UAAM,QAAQ,UAAU,MAAM,IAAI;AAClC,UAAM,QAAQ,CAAC,MAAM,cAAc;AAEjC,UAAI,cAAc,GAAG;AACnB,kBAAU,KAAK;AAAA,MAEjB,WAAW,YAAY,GAAG;AACxB,sBAAc;AACd,iBAAS,KAAK;AAAA,MAChB;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,UAAU,QAAQ,KAAK,YAAY,MAAM;AAChD,iBAAW,KAAK;AAAA,QACd,iBAAiB,kBAAkB;AAAA,QACnC;AAAA,QACA,eAAe;AAAA,QACf,WAAW;AAAA,QACX,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;",
6
6
  "names": ["chalk"]
7
7
  }
package/dist/version.js CHANGED
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  LIB_VERSION: () => LIB_VERSION
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const LIB_VERSION = "1.0.0-beta.94";
24
+ const LIB_VERSION = "1.0.0-beta.95";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  LIB_VERSION
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["export const LIB_VERSION = \"1.0.0-beta.94\";\n"],
4
+ "sourcesContent": ["export const LIB_VERSION = \"1.0.0-beta.95\";\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,cAAc;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contensis-cli",
3
- "version": "1.0.0-beta.94",
3
+ "version": "1.0.0-beta.95",
4
4
  "description": "A fully featured Contensis command line interface with a shell UI provides simple and intuitive ways to manage or profile your content in any NodeJS terminal.",
5
5
  "repository": "https://github.com/contensis/node-cli",
6
6
  "homepage": "https://github.com/contensis/node-cli/tree/main/packages/contensis-cli#readme",
@@ -456,8 +456,24 @@ We will ask you to add secrets/variables to your git repository to give your wor
456
456
  `Add push-block job to CI file: ${Logger.highlightText(filename)}\n`,
457
457
  ciMultipleChoices: () =>
458
458
  `Multiple GitHub workflow files found\n${Logger.infoText(
459
- `Tell us which GitHub workflow builds the container image after each push:`
459
+ `Tell us which GitHub workflow builds a container image after each push:`
460
460
  )}`,
461
+ ciMultipleBuildJobChoices: () =>
462
+ `Multiple build jobs found in workflow\n${Logger.infoText(
463
+ `Choose the build job that produces a fresh container image to push to a block:`
464
+ )}`,
465
+ ciMultipleJobChoices: () =>
466
+ `Other jobs found in workflow\n${Logger.infoText(
467
+ `Choose the job that produces a fresh container image we can push to a block:`
468
+ )}`,
469
+ ciMultipleAppImageVarChoices: () =>
470
+ `Do one of these variables point to your tagged app image?\n${Logger.infoText(
471
+ `we have included a default choice - ensure your build image is tagged exactly the same as this`
472
+ )}`,
473
+ ciEnterOwnAppImagePrompt: () =>
474
+ `Tell us the registry uri your app image is tagged and pushed with (āŽ accept default) \n${Logger.infoText(
475
+ `Tip: GitHub context variables available\nhttps://docs.github.com/en/actions/learn-github-actions/variables#using-contexts-to-access-variable-values`
476
+ )}\n`,
461
477
  confirm: () =>
462
478
  `Confirm these details are correct so we can make changes to your project`,
463
479
  accessTokenPrompt: () =>
@@ -479,6 +495,10 @@ We will ask you to add secrets/variables to your git repository to give your wor
479
495
  `You should alter existing project code that connects a Contensis client to use the variables from this file`,
480
496
  writeCiFile: (ciFilePath: string) =>
481
497
  `Updated CI file ${Logger.standardText(winSlash(ciFilePath))}`,
498
+ ciFileNoChanges: (ciFilePath: string) =>
499
+ `No updates needed for CI file ${Logger.standardText(
500
+ winSlash(ciFilePath)
501
+ )}`,
482
502
  ciBlockTip: (blockId: string, env: string, projectId: string) =>
483
503
  `A job is included to deploy your built container image to ${Logger.standardText(
484
504
  projectId
@@ -512,7 +532,7 @@ We will ask you to add secrets/variables to your git repository to give your wor
512
532
  dryRun: () =>
513
533
  `Contensis developer environment initialisation dry run completed`,
514
534
  noChanges: () =>
515
- `No changes were made to your project - run the command again without the --dry-run flag to update your project with these changes`,
535
+ `No changes were made to your project, run the command again without the --dry-run flag to update your project with these changes`,
516
536
  startProjectTip: () =>
517
537
  `Start up your project in the normal way for development`,
518
538
  },
@@ -1,4 +1,9 @@
1
+ import inquirer from 'inquirer';
1
2
  import { JSONPath, JSONPathOptions } from 'jsonpath-plus';
3
+ import {
4
+ GitHubActionPushBlockJob,
5
+ GitHubActionPushBlockJobStep,
6
+ } from '~/models/DevService';
2
7
  import { readFile } from '~/providers/file-provider';
3
8
  import ContensisDev from '~/services/ContensisDevService';
4
9
  import { diffFileContent } from '~/util/diff';
@@ -13,17 +18,17 @@ type MappedWorkflowOutput = {
13
18
  diff: string;
14
19
  };
15
20
 
16
- export const mapCIWorkflowContent = (
21
+ export const mapCIWorkflowContent = async (
17
22
  cli: ContensisDev,
18
23
  git: GitHelper
19
- ): MappedWorkflowOutput | undefined => {
24
+ ): Promise<MappedWorkflowOutput | undefined> => {
20
25
  // get existing workflow file
21
26
  const workflowFile = readFile(git.ciFilePath);
22
27
  if (!workflowFile) return undefined;
23
28
 
24
29
  const blockId = git.name;
25
30
  if (git.type === 'github') {
26
- const addGitHubActionJobStep = {
31
+ const addGitHubActionJobStep: GitHubActionPushBlockJobStep = {
27
32
  name: 'Push block to Contensis',
28
33
  id: 'push-block',
29
34
  uses: 'contensis/block-push@v1',
@@ -39,7 +44,7 @@ export const mapCIWorkflowContent = (
39
44
 
40
45
  // parse yaml to js
41
46
  const workflowDoc = parseYamlDocument(workflowFile);
42
- const workflow = workflowDoc.toJS();
47
+ const workflowJS = workflowDoc.toJS();
43
48
  const setWorkflowElement = (path: string | any[], value: any) => {
44
49
  const findPath =
45
50
  typeof path === 'string' && path.includes('.')
@@ -56,26 +61,44 @@ export const mapCIWorkflowContent = (
56
61
  }
57
62
  };
58
63
  const findExistingJobSteps = (
64
+ path: string,
59
65
  resultType: JSONPathOptions['resultType']
60
66
  ) => {
61
- // look for line in job
62
- // jobs.x.steps[uses: contensis/block-push]
63
- const path =
64
- git.type === 'github'
65
- ? '$.jobs..steps.*[?(@property === "uses" && @.match(/^contensis\\/block-push/i))]^'
66
- : // TODO: add jsonpath for gitlab file
67
- '';
68
-
69
67
  const existingJobStep = JSONPath({
70
68
  path,
71
- json: workflow,
69
+ json: workflowJS,
72
70
  resultType: resultType,
73
71
  });
74
72
 
75
73
  return existingJobStep;
76
74
  };
77
75
 
78
- const existingJobStep = findExistingJobSteps('all');
76
+ // look for line in job
77
+ // jobs.x.steps[uses: contensis/block-push]
78
+ const existingJobStep = findExistingJobSteps(
79
+ git.type === 'github'
80
+ ? '$.jobs..steps.*[?(@property === "uses" && @.match(/^contensis\\/block-push/i))]^'
81
+ : // TODO: add jsonpath for gitlab file
82
+ '',
83
+ 'all'
84
+ );
85
+
86
+ const addAppImageUri = async () => {
87
+ // Look in document level "env" vars
88
+ const appImageUri = await determineAppImageUri(
89
+ cli,
90
+ Object.entries(workflowJS.env || {})
91
+ );
92
+
93
+ if (appImageUri.addVar)
94
+ setWorkflowElement(`env.${appImageUri.var}`, appImageUri.uri);
95
+ // workflowDoc.addIn(['env'], { [appImageUri.var]: appImageUri.uri });
96
+
97
+ if (appImageUri.var)
98
+ addGitHubActionJobStep.with[
99
+ 'image-uri'
100
+ ] = `\${{ env.${appImageUri.var} }}`;
101
+ };
79
102
 
80
103
  // update job step
81
104
  if (existingJobStep.length) {
@@ -101,6 +124,11 @@ export const mapCIWorkflowContent = (
101
124
  setWorkflowElement(`${stepPath}.with.alias`, cli.currentEnv);
102
125
  setWorkflowElement(`${stepPath}.with.project-id`, cli.currentProject);
103
126
  setWorkflowElement(`${stepPath}.with.block-id`, blockId);
127
+
128
+ // This is likely not needed when updating an existing push-block job step
129
+ // we are assuming this is a forked/copied workflow with an already working image-uri reference
130
+ // await addAppImageUri();
131
+
104
132
  setWorkflowElement(
105
133
  `${stepPath}.with.client-id`,
106
134
  '${{ secrets.CONTENSIS_CLIENT_ID }}'
@@ -111,20 +139,89 @@ export const mapCIWorkflowContent = (
111
139
  );
112
140
  } else {
113
141
  // create job with push step
142
+
143
+ // is there already a job with a property name containing "build"?
144
+ const existingBuildJobStep = findExistingJobSteps(
145
+ git.type === 'github'
146
+ ? '$.jobs[?(@property.match(/build/i))]'
147
+ : // TODO: add jsonpath for gitlab file
148
+ '',
149
+ 'all'
150
+ ) as JSONPathOptions[]; // This isn't the correct type for this object
151
+
152
+ let needs: string | undefined;
153
+ // There are multiple jobs called *build*
154
+ if (existingBuildJobStep.length > 1) {
155
+ // prompt which build job we should depend on before pushing the block
156
+ const choices = existingBuildJobStep.map(s => s.parentProperty);
157
+ choices.push(new inquirer.Separator() as any);
158
+ choices.push('none');
159
+
160
+ ({ needs } = await inquirer.prompt([
161
+ {
162
+ type: 'list',
163
+ prefix: 'āŒ›',
164
+ message: cli.messages.devinit.ciMultipleBuildJobChoices(),
165
+ name: 'needs',
166
+ choices,
167
+ default: choices.find(
168
+ s => typeof s === 'string' && s.includes('docker')
169
+ ),
170
+ },
171
+ ]));
172
+ cli.log.raw('');
173
+ } else if (existingBuildJobStep.length === 1)
174
+ // Exactly one job step found containing a property name of *build*
175
+ // we'll assume that is the one the push-block job depends on
176
+ needs = existingBuildJobStep[0].parentProperty;
177
+
178
+ if (existingBuildJobStep.length === 0 || needs === 'none') {
179
+ // No existing build step found or chosen, offer all job steps in prompt
180
+ const choices = Object.keys(workflowJS.jobs);
181
+ choices.push(new inquirer.Separator() as any);
182
+ choices.push('none');
183
+
184
+ ({ needs } = await inquirer.prompt([
185
+ {
186
+ type: 'list',
187
+ prefix: 'āŒ›',
188
+ message: cli.messages.devinit.ciMultipleJobChoices(),
189
+ name: 'needs',
190
+ choices,
191
+ default: choices.find(
192
+ j => typeof j === 'string' && j.includes('docker')
193
+ ),
194
+ },
195
+ ]));
196
+ if (needs === 'none') needs = undefined;
197
+ cli.log.raw('');
198
+ }
199
+
200
+ // Does a series of checks and prompts to determine the correct image-uri
201
+ // for the app container build
202
+ await addAppImageUri();
203
+
204
+ const newJob: GitHubActionPushBlockJob = {
205
+ name: 'Deploy container image to Contensis',
206
+ 'runs-on': 'ubuntu-latest',
207
+ needs,
208
+ steps: [addGitHubActionJobStep],
209
+ };
210
+
211
+ // Add the new "job" to the Yaml Document
114
212
  workflowDoc.addIn(['jobs'], {
115
213
  key: 'deploy',
116
- value: {
117
- name: 'Push image to Contensis',
118
- 'runs-on': 'ubuntu-latest',
119
- steps: [addGitHubActionJobStep],
120
- },
214
+ value: newJob,
121
215
  });
122
216
  }
123
217
 
124
218
  // Workflow validation provided by @action-validator/core package
125
219
  const workflowIsValid = validateWorkflowYaml(workflowDoc.toString());
126
220
 
221
+ // We could expand validation to check for having a build step to wait for
222
+ // or if a valid image-uri attribute is set
127
223
  if (workflowIsValid === true) {
224
+ cli.log.success(`GitHub workflow YAML is valid`);
128
225
  cli.log.debug(
129
226
  `New file content to write to ${git.ciFilePath}\n\n${workflowDoc}`
130
227
  );
@@ -151,3 +248,89 @@ export const mapCIWorkflowContent = (
151
248
  };
152
249
  }
153
250
  };
251
+
252
+ const determineAppImageUri = async (
253
+ cli: ContensisDev,
254
+ vars: [string, string][]
255
+ ) => {
256
+ // Determine container image-uri via variables and/or prompts
257
+
258
+ // Find vars including the word "image"
259
+ const imageVars = vars.filter(([varname, value]) =>
260
+ varname.toLowerCase().includes('image')
261
+ );
262
+ // Find vars named "image" that include the word "app"
263
+ const appImageVars = imageVars.filter(
264
+ ([varname, value]) =>
265
+ varname.toLowerCase().includes('app_') ||
266
+ varname.toLowerCase().includes('build_') ||
267
+ value?.toLowerCase().includes('/app')
268
+ );
269
+
270
+ const appImageUriGuess = appImageVars?.[0];
271
+
272
+ let appImageUri: string | undefined;
273
+ let appImageVar: string | undefined;
274
+
275
+ if (appImageUriGuess) {
276
+ cli.log.success(
277
+ `Found variable ${cli.log.standardText(
278
+ appImageUriGuess[0]
279
+ )} we'll use for pulling the block image from: ${cli.log.infoText(
280
+ appImageUriGuess[1]
281
+ )}`
282
+ );
283
+ appImageVar = appImageUriGuess[0];
284
+ } else {
285
+ // Could not find a suitable var to use for block image-uri
286
+ // prompt for an app image uri
287
+ const choices = vars.map(v => v[0]);
288
+ const enterOwnMsg = 'enter my own / use default';
289
+ const defaultUri =
290
+ 'ghcr.io/${{ github.repository }}/${{ github.ref_name }}/app:build-${{ github.run_number }}';
291
+ choices.push(new inquirer.Separator() as any);
292
+ choices.push(enterOwnMsg);
293
+ choices.push(defaultUri);
294
+
295
+ ({ appImageVar, appImageUri } = await inquirer.prompt([
296
+ // First question determines if an existing env variable
297
+ // already containes the tagged app image uri
298
+ {
299
+ type: 'list',
300
+ prefix: '🐳',
301
+ message: cli.messages.devinit.ciMultipleAppImageVarChoices(),
302
+ name: 'appImageVar',
303
+ choices,
304
+ default: choices.find(
305
+ v => typeof v === 'string' && v.toLowerCase().includes('image')
306
+ ),
307
+ },
308
+ // Subsequent prompt allows input of an image-uri if needed
309
+ {
310
+ type: 'input',
311
+ when(answers) {
312
+ return [enterOwnMsg, defaultUri].includes(answers.appImageVar);
313
+ },
314
+ prefix: `\n \nšŸ”—`,
315
+ message: cli.messages.devinit.ciEnterOwnAppImagePrompt(),
316
+ name: 'appImageUri',
317
+ default: defaultUri,
318
+ },
319
+ ]));
320
+ cli.log.raw('');
321
+
322
+ // this indicates a uri has been added and we will add a new var
323
+ // to the workflow to encourage users to update the docker tag part
324
+ // of their build workflow to use the same var as the push-block job/step
325
+ if ([enterOwnMsg, defaultUri].includes(appImageVar || ''))
326
+ appImageVar = undefined;
327
+ }
328
+
329
+ if (appImageVar) appImageUri = `\${{ env.${appImageVar} }}`;
330
+
331
+ return {
332
+ addVar: !appImageVar,
333
+ uri: appImageUri,
334
+ var: appImageVar || 'BUILD_IMAGE',
335
+ };
336
+ };
@@ -2,4 +2,25 @@ export type EnvContentsToAdd = {
2
2
  ALIAS: string;
3
3
  PROJECT: string;
4
4
  ACCESS_TOKEN?: string;
5
- }
5
+ };
6
+
7
+ export type GitHubActionPushBlockJobStep = {
8
+ name: string;
9
+ id: 'push-block';
10
+ uses: string;
11
+ with: {
12
+ 'block-id': string;
13
+ alias: string;
14
+ 'project-id': string;
15
+ 'client-id': string;
16
+ 'shared-secret': string;
17
+ 'image-uri'?: string;
18
+ };
19
+ };
20
+
21
+ export type GitHubActionPushBlockJob = {
22
+ name: string;
23
+ 'runs-on': string;
24
+ needs?: string;
25
+ steps: GitHubActionPushBlockJobStep[];
26
+ };
@@ -1,2 +1,3 @@
1
1
  declare module 'giturl';
2
2
  declare module 'inquirer-command-prompt';
3
+ declare module 'printable-characters';
@@ -21,7 +21,7 @@ import { mergeDotEnvFileContents } from '~/util/dotenv';
21
21
  import { findByIdOrName } from '~/util/find';
22
22
  import { GitHelper } from '~/util/git';
23
23
  import { jsonFormatter } from '~/util/json.formatter';
24
- import { normaliseLineEndings } from '~/util/os';
24
+ import { normaliseLineEndings, winSlash } from '~/util/os';
25
25
  import { stringifyYaml } from '~/util/yaml';
26
26
 
27
27
  class ContensisDev extends ContensisRole {
@@ -103,6 +103,7 @@ class ContensisDev extends ContensisRole {
103
103
  ({ ciFileName } = await inquirer.prompt([
104
104
  {
105
105
  type: 'list',
106
+ prefix: 'ā§°',
106
107
  message: messages.devinit.ciMultipleChoices(),
107
108
  name: 'ciFileName',
108
109
  choices: workflowFiles,
@@ -116,7 +117,7 @@ class ContensisDev extends ContensisRole {
116
117
  log.raw(log.infoText(messages.devinit.ciDetails(ciFileName)));
117
118
 
118
119
  // Look at the workflow file content and make updates
119
- const mappedWorkflow = mapCIWorkflowContent(this, git);
120
+ const mappedWorkflow = await mapCIWorkflowContent(this, git);
120
121
 
121
122
  log.help(messages.devinit.ciIntro(git));
122
123
 
@@ -138,6 +139,7 @@ class ContensisDev extends ContensisRole {
138
139
  const { accessToken }: { accessToken: string } = await inquirer.prompt([
139
140
  {
140
141
  type: 'input',
142
+ prefix: 'šŸ›”ļø',
141
143
  message: messages.devinit.accessTokenPrompt(),
142
144
  name: 'accessToken',
143
145
  },
@@ -219,34 +221,41 @@ class ContensisDev extends ContensisRole {
219
221
 
220
222
  if (dryRun) {
221
223
  if (envDiff) {
222
- log.info(`updating .env file ${envFilePath}: ${envDiff}`);
224
+ log.info(`Updating .env file ${winSlash(envFilePath)}:\n${envDiff}`);
223
225
  log.raw('');
224
226
  }
225
227
  checkpoint('skip .env file update (dry-run)');
226
228
  } else {
227
- if (envDiff) log.info(`updating .env file ${envFilePath}`);
229
+ if (envDiff) log.info(`updating .env file ${winSlash(envFilePath)}`);
228
230
  writeFile(envFilePath, envFileLines.join('\n'));
229
231
  checkpoint('.env file updated');
230
232
  log.success(messages.devinit.writeEnvFile());
231
233
  // log.help(messages.devinit.useEnvFileTip());
232
234
  }
233
235
 
234
- // Update CI file -- different for GH/GL -- create a sample one with build?
236
+ // Update CI file -- different for GH/GL
237
+ if (mappedWorkflow?.diff) {
238
+ log.info(
239
+ `Updating ${winSlash(ciFileName)} file:\n${mappedWorkflow.diff}`
240
+ );
241
+ log.raw('');
242
+ }
235
243
  if (dryRun) {
236
- if (mappedWorkflow?.diff) {
237
- log.info(`updating${ciFileName} file: ${mappedWorkflow.diff}`);
238
- log.raw('');
239
- }
240
244
  checkpoint('skip CI file update (dry-run)');
241
245
  //log.object(ciFileLines);
242
246
  } else {
243
- if (mappedWorkflow?.diff) log.info(`updating${ciFileName} file`);
244
- writeFile(git.ciFilePath, [].join('\n'));
245
- log.success(messages.devinit.writeCiFile(`./${ciFileName}`));
246
- log.info(
247
- messages.devinit.ciBlockTip(blockId, currentEnv, currentProject)
248
- );
249
- checkpoint('CI file updated');
247
+ if (mappedWorkflow?.newWorkflow) {
248
+ if (mappedWorkflow?.diff) {
249
+ writeFile(git.ciFilePath, mappedWorkflow.newWorkflow);
250
+ log.success(messages.devinit.writeCiFile(`./${ciFileName}`));
251
+ log.info(
252
+ messages.devinit.ciBlockTip(blockId, currentEnv, currentProject)
253
+ );
254
+ } else {
255
+ log.info(messages.devinit.ciFileNoChanges(`./${ciFileName}`));
256
+ }
257
+ checkpoint('CI file updated');
258
+ }
250
259
  }
251
260
 
252
261
  // Echo Deployment API key to console, ask user to add secrets to repo