Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/canary-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:

strategy:
matrix:
node-version: [12]
node-version: [18]

steps:
- name: Checkout project
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-and-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [12]
node-version: [18, 20, 22]

steps:
- name: Checkout project
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:12.14.1-alpine3.11
FROM node:lts-alpine3.20
ENV NODE_ENV=production

# install dependencies
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
},
"workspaces": [
"packages/*"
]
],
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
1 change: 1 addition & 0 deletions packages/gitmoji-changelog-cli/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/*
3 changes: 2 additions & 1 deletion packages/gitmoji-changelog-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"description": "Gitmoji Changelog CLI",
"main": "src/index.js",
"engines": {
"node": ">=10"
"node": ">=18"
},
"bin": {
"gitmoji-changelog": "./src/index.js"
Expand Down Expand Up @@ -45,6 +45,7 @@
"semver": "^5.6.0",
"semver-compare": "^1.0.0",
"simple-git": "^1.113.0",
"smol-toml": "^1.3.4",
"toml": "^3.0.0",
"yaml": "^1.10.2",
"yargs": "^17.3.1"
Expand Down
4 changes: 3 additions & 1 deletion packages/gitmoji-changelog-cli/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ async function main(options = {}) {

// eslint-disable-next-line global-require
const loadProjectInfo = require(`./presets/${options.preset}.js`)
projectInfo = await loadProjectInfo()
projectInfo = await loadProjectInfo({
versionCommand: options.versionCommand,
})

if (!projectInfo) {
throw Error(`Cannot retrieve configuration for preset ${options.preset}.`)
Expand Down
1 change: 1 addition & 0 deletions packages/gitmoji-changelog-cli/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ yargs
.option('group-similar-commits', { desc: '[⚗️ - beta] try to group similar commits', default: false })
.option('author', { default: false, desc: 'add the author in changelog lines' })
.option('interactive', { default: false, desc: 'select commits manually', alias: 'i' })
.option('version-command', { desc: 'Command used to determine the package version (for Python packages with dynamic versioning)' })

.help('help')
.epilog(`For more information visit: ${homepage}`)
Expand Down
102 changes: 64 additions & 38 deletions packages/gitmoji-changelog-cli/src/presets/python.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
const toml = require('toml')
const fs = require('fs')
const toml = require('smol-toml')
const ChildProcess = require('child_process')

module.exports = async () => {
try {
const pyprojectPromise = new Promise((resolve, reject) => {
try {
resolve(toml.parse(fs.readFileSync('pyproject.toml', 'utf-8')))
} catch (err) {
reject(err)
}
})

const projectFile = await pyprojectPromise
const name = recursiveKeySearch('name', projectFile)[0]
const version = recursiveKeySearch('version', projectFile)[0]
let description = recursiveKeySearch('description', projectFile)[0]
class DynamicVersionError extends Error { }


module.exports = async (options = {}) => {
try {
const pyproject = toml.parse(fs.readFileSync('pyproject.toml', 'utf-8'))
const meta = pyproject.project || (pyproject.tool && pyproject.tool.poetry)
const dynamicFields = meta.dynamic || []

const name = meta.name
if (!name) {
throw new Error('Could not find name metadata in pyproject.toml')
}

let version = meta.version
const isDynamicVersion = dynamicFields.includes('version')
if (isDynamicVersion) {
if (options.versionCommand) {
version = getDynamicVersion(options.versionCommand)
} else {
throw new DynamicVersionError(
'Dynamic version detected. Please supply a command to obtain it, e.g.: \'gitmoji-changelog --preset python --version-command "python setup.py --version\'"'
)
}
}
if (!version) {
throw new Error('Could not find version metadata in pyproject.toml')
throw new Error('Could not find version metadata (static or dynamic)')
}
if (!description) {
description = ''

let description = meta.description || ''
const isDynamicDescription = dynamicFields.includes('description')
if (isDynamicDescription) {
let readme = meta.readme
if (typeof readme === 'object') {
readme = readme.file
}

description = getDescriptionFromReadme(readme) || ''
}

return {
Expand All @@ -32,37 +49,46 @@ module.exports = async () => {
description,
}
} catch (e) {
if (e instanceof DynamicVersionError) {
throw e
}

return null
}
}


function recursiveKeySearch(key, data) {
// https://codereview.stackexchange.com/a/143914
if (data === null) {
return []
}

if (data !== Object(data)) {
return []
}
function getDynamicVersion(command) {
try {
return ChildProcess.execSync(command, { encoding: 'utf-8' }).trim()
} catch (e) {
const { status, signal, stderr } = e
const exitCode = (status !== null && status !== undefined) ? status : 'unknown'

let results = []
const details = [
'Failed to retrieve package version with external command',
` cmd : ${command}`,
` exit code: ${exitCode}${signal ? ` (signal: ${signal})` : ''}`,
]

if (data.constructor === Array) {
for (let i = 0, len = data.length; i < len; i += 1) {
results = results.concat(recursiveKeySearch(key, data[i]))
if (stderr && String(stderr).trim()) {
details.push(` stderr : ${String(stderr).trim()}`)
}
return results

throw new DynamicVersionError(details.join('\n'), { cause: e })
}
}

for (let i = 0; i < Object.keys(data).length; i += 1) {
const dataKey = Object.keys(data)[i]
if (key === dataKey) {
results.push(data[key])
}
results = results.concat(recursiveKeySearch(key, data[dataKey]))

function getDescriptionFromReadme(readmePath = 'README.md') {
if (!fs.existsSync(readmePath)) {
return ''
}

return results
const content = fs.readFileSync(readmePath, 'utf-8').trim()

const paragraphs = content.split(/\r?\n\r?\n/)
const first = paragraphs.find(p => p.trim().length > 0)

return first ? first.replace(/^#\s*/, '').trim() : null
}
Loading