|
1 | 1 | #!/usr/bin/env node
|
2 |
| -import { exec as child_exec } from "child_process" |
3 |
| -import util from "util" |
4 |
| -import path from "path" |
5 |
| -import packageJson from "../package.json" |
| 2 | +import { exec as child_exec } from 'child_process' |
| 3 | +import util from 'util' |
| 4 | +import path from 'path' |
| 5 | +import packageJson from '../package.json' |
| 6 | +import { simpleGit } from 'simple-git' |
| 7 | +import { Command } from 'commander' |
| 8 | +import { bumpCalculator, bumpMapping, BumpType, isValidTag } from './utils' |
6 | 9 |
|
7 | 10 | const exec = util.promisify(child_exec)
|
8 | 11 |
|
9 | 12 | const scriptDir = path.dirname(__filename)
|
10 | 13 | const scriptPath = path.resolve(`${scriptDir}/../../scripts/pack-nextjs.sh`)
|
11 | 14 | const handlerPath = path.resolve(`${scriptDir}/../server-handler/index.js`)
|
12 | 15 |
|
13 |
| -import { Command } from "commander" |
14 | 16 | const program = new Command()
|
15 | 17 |
|
16 |
| -program.name(packageJson.name).description(packageJson.description).version(packageJson.version) |
| 18 | +program |
| 19 | + // |
| 20 | + .name(packageJson.name) |
| 21 | + .description(packageJson.description) |
| 22 | + .version(packageJson.version) |
17 | 23 |
|
18 | 24 | program
|
19 |
| - .command("pack") |
20 |
| - .description("Package standalone Next12 build into Lambda compatible ZIPs.") |
21 |
| - .option("--output", "folder where to save output", "next.out") |
22 |
| - .option("--publicFolder", "folder where public assets are located", "public") |
23 |
| - .option("--handler", "custom handler to deal with ApiGw events", handlerPath) |
24 |
| - .option("--grepBy", "keyword to identify configuration inside server.js", "webpack") |
25 |
| - .action(async (str, options) => { |
26 |
| - // @TODO: Ensure path exists. |
27 |
| - // @TODO: Ensure.next folder exists with standalone folder inside. |
28 |
| - |
29 |
| - // @TODO: Transform into code, move away from script. |
30 |
| - // Also, pass parameters and options. |
31 |
| - console.log("Starting packaging of your NextJS project!") |
32 |
| - |
33 |
| - await exec(`chmod +x ${scriptPath} && ${scriptPath}`) |
34 |
| - .then(({ stdout }) => console.log(stdout)) |
35 |
| - .catch(console.error) |
36 |
| - |
37 |
| - console.log("Your NextJS project was succefully prepared for Lambda.") |
38 |
| - }) |
| 25 | + .command('pack') |
| 26 | + .description('Package standalone Next12 build into Lambda compatible ZIPs.') |
| 27 | + .option('--output', 'folder where to save output', 'next.out') |
| 28 | + .option('--publicFolder', 'folder where public assets are located', 'public') |
| 29 | + .option('--handler', 'custom handler to deal with ApiGw events', handlerPath) |
| 30 | + .option('--grepBy', 'keyword to identify configuration inside server.js', 'webpack') |
| 31 | + .action(async (str, options) => { |
| 32 | + // @TODO: Ensure path exists. |
| 33 | + // @TODO: Ensure.next folder exists with standalone folder inside. |
| 34 | + |
| 35 | + // @TODO: Transform into code, move away from script. |
| 36 | + // Also, pass parameters and options. |
| 37 | + console.log('Starting packaging of your NextJS project!') |
| 38 | + |
| 39 | + await exec(`chmod +x ${scriptPath} && ${scriptPath}`) |
| 40 | + .then(({ stdout }) => console.log(stdout)) |
| 41 | + .catch(console.error) |
| 42 | + |
| 43 | + console.log('Your NextJS project was succefully prepared for Lambda.') |
| 44 | + }) |
39 | 45 |
|
40 | 46 | program
|
41 |
| - .command("guess") |
42 |
| - .description("Get commits from last tag and guess bump version based on SemVer/Chakra keywords.") |
43 |
| - .action(async (str, options) => { |
44 |
| - // @TODO: Implement git commits parsing. |
45 |
| - // Use ref, docs, bugfix, fix, feature, feat, etc. |
46 |
| - // Also consider parsing commit body for things such as "Breaking change" |
47 |
| - }) |
48 |
| - |
49 |
| -program.parse() |
| 47 | + .command('guess') |
| 48 | + .description('Calculate next version based on last version and commit message.') |
| 49 | + .argument('<commitMessage>', 'Commit message to use for guessing bump.') |
| 50 | + .argument('<latestVersion>', 'Your existing app version which should be used for calculation of next version.') |
| 51 | + .option('-t, --tagPrefix <prefix>', 'Prefix version with string of your choice', 'v') |
| 52 | + .action(async (args, options) => { |
| 53 | + const { commitMessage, latestVersion } = args |
| 54 | + const { tagPrefix } = options |
| 55 | + |
| 56 | + if (!isValidTag(latestVersion, tagPrefix)) { |
| 57 | + throw new Error(`Invalid version found - ${latestVersion}!`) |
| 58 | + } |
| 59 | + |
| 60 | + const match = bumpMapping.find(({ test }) => commitMessage.match(test)) |
| 61 | + if (!match) { |
| 62 | + throw new Error('No mapping for for suplied commit message.') |
| 63 | + } |
| 64 | + |
| 65 | + const nextTag = bumpCalculator(latestVersion.replace(tagPrefix, ''), match?.bump) |
| 66 | + const nextTagWithPrefix = tagPrefix + nextTag |
| 67 | + |
| 68 | + console.log(nextTagWithPrefix) |
| 69 | + }) |
| 70 | + |
| 71 | +program |
| 72 | + .command('shipit') |
| 73 | + .description('Get last tag, calculate bump version for all commits that happened and create release branch.') |
| 74 | + .option('--failOnMissingCommit', 'In case commit has not happened since last tag (aka. we are on latest tag) fail.', Boolean, true) |
| 75 | + .option('-f, --forceBump', 'In case no compatible commits found, use patch as fallback and ensure bump happens.', Boolean, true) |
| 76 | + .option('-a, --autoPush', 'This will automatically create release branch and tag commit in master.', Boolean, true) |
| 77 | + .option('-t, --tagPrefix <prefix>', 'Prefix version with string of your choice', 'v') |
| 78 | + .option('-r, --releaseBranchPrefix <prefix>', 'Prefix for release branch fork.', 'release/') |
| 79 | + .action(async (options) => { |
| 80 | + const { tagPrefix, failOnMissingCommit, releaseBranchPrefix, forceBump } = options |
| 81 | + |
| 82 | + const git = simpleGit() |
| 83 | + const tags = await git.tags() |
| 84 | + const log = await git.log() |
| 85 | + const [remote] = await git.getRemotes() |
| 86 | + |
| 87 | + const latestCommit = log.latest?.hash |
| 88 | + const latestTag = tags.latest ?? '0.0.0' |
| 89 | + |
| 90 | + if (!isValidTag(latestTag, tagPrefix)) { |
| 91 | + throw new Error(`Invalid tag found - ${latestTag}!`) |
| 92 | + } |
| 93 | + |
| 94 | + if (!latestCommit) { |
| 95 | + throw new Error('Latest commit was not found!') |
| 96 | + } |
| 97 | + |
| 98 | + const commits = await git.log({ |
| 99 | + from: tags.latest, |
| 100 | + to: latestCommit, |
| 101 | + }) |
| 102 | + |
| 103 | + if (commits.total < 1 && failOnMissingCommit) { |
| 104 | + throw new Error('No new commits since last tag.') |
| 105 | + } |
| 106 | + |
| 107 | + const bumps = [] |
| 108 | + |
| 109 | + commits.all.forEach(({ message, body }) => { |
| 110 | + const match = bumpMapping.find(({ test, scanBody }) => (scanBody ? body : message).match(test)) |
| 111 | + if (!match) { |
| 112 | + console.warn(`Invalid commit, cannot match bump - ${message}!`) |
| 113 | + } else { |
| 114 | + bumps.push(match?.bump) |
| 115 | + } |
| 116 | + }) |
| 117 | + |
| 118 | + // Bump minor in case nothing is found. |
| 119 | + if (bumps.length < 1 && forceBump) { |
| 120 | + bumps.push(BumpType.Patch) |
| 121 | + } |
| 122 | + |
| 123 | + const nextTag = bumps.reduce((acc, curr) => bumpCalculator(acc, curr), latestTag.replace(tagPrefix, '')) |
| 124 | + const nextTagWithPrefix = tagPrefix + nextTag |
| 125 | + const releaseBranch = `${releaseBranchPrefix}${nextTagWithPrefix}` |
| 126 | + |
| 127 | + console.log(`Next version is - ${nextTagWithPrefix}!`) |
| 128 | + |
| 129 | + // Create tag and push it to master. |
| 130 | + // @Note: CI/CD should not be listening for tags in master, it should listen to release branch. |
| 131 | + await git.addTag(nextTagWithPrefix) |
| 132 | + await git.pushTags() |
| 133 | + |
| 134 | + await git.push(remote.name, releaseBranch) |
| 135 | + |
| 136 | + console.log(`Successfuly tagged and created new branch - ${releaseBranch}`) |
| 137 | + }) |
| 138 | + |
| 139 | +program.parse(process.argv) |
0 commit comments