Skip to content

Commit

Permalink
✨ Added python preset (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
c4th4r5y5 authored Jun 9, 2023
1 parent 584d065 commit 305a371
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/gitmoji-changelog-cli/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ yargs
}, execute('update'))

.option('format', { default: 'markdown', desc: 'changelog format (markdown, json)' })
.option('preset', { default: 'node', desc: 'define preset mode', choices: ['node', 'generic', 'maven', 'cargo', 'helm'] })
.option('preset', { default: 'node', desc: 'define preset mode', choices: ['node', 'generic', 'maven', 'cargo', 'helm', 'python'] })
.option('output', { desc: 'output changelog file' })
.option('group-similar-commits', { desc: '[⚗️ - beta] try to group similar commits', default: false })
.option('author', { default: false, desc: 'add the author in changelog lines' })
Expand Down
68 changes: 68 additions & 0 deletions packages/gitmoji-changelog-cli/src/presets/python.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const toml = require('toml')
const fs = require('fs')

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]

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

return {
name,
version,
description,
}
} catch (e) {
return null
}
}


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

if (data !== Object(data)) {
return []
}

let results = []

if (data.constructor === Array) {
for (let i = 0, len = data.length; i < len; i += 1) {
results = results.concat(recursiveKeySearch(key, data[i]))
}
return results
}

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]))
}

return results
}
137 changes: 137 additions & 0 deletions packages/gitmoji-changelog-cli/src/presets/python.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const fs = require('fs')

const loadProjectInfo = require('./python.js')

describe('getPackageInfo', () => {
it('should extract metadata from a pyproject.toml made by poetry', async () =>{
// Note the TOML section is distinct for poetry
fs.readFileSync.mockReturnValue(`
[tool.poetry]
name = "poetry-package-name"
version = "0.1.0"
description = "Description of the poetry package"
`)

const result = await loadProjectInfo()

expect(result).toEqual({
name: 'poetry-package-name',
version: '0.1.0',
description: 'Description of the poetry package',
})
})

it('should extract metadata from the PEP621 example pyproject.toml', async () =>{
// [project] is the usual TOML section for the metadata
fs.readFileSync.mockReturnValue(`
[project]
name = "spam"
version = "2020.0.0"
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
requires-python = ">=3.8"
license = {file = "LICENSE.txt"}
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
authors = [
{email = "[email protected]"},
{name = "Tzu-Ping Chung"}
]
maintainers = [
{name = "Brett Cannon", email = "[email protected]"}
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python"
]
dependencies = [
"httpx",
"gidgethub[httpx]>4.0.0",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'"
]
[project.optional-dependencies]
test = [
"pytest < 5.0.0",
"pytest-cov[all]"
]
[project.urls]
homepage = "example.com"
documentation = "readthedocs.org"
repository = "github.com"
changelog = "github.com/me/spam/blob/master/CHANGELOG.md"
[project.scripts]
spam-cli = "spam:main_cli"
[project.gui-scripts]
spam-gui = "spam:main_gui"
[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"
`)

const result = await loadProjectInfo()

expect(result).toEqual({
name: 'spam',
version: '2020.0.0',
description: 'Lovely Spam! Wonderful Spam!',
})
})

it('should extract metadata despite a missing description', async () =>{
// The description metadata is optional.
fs.readFileSync.mockReturnValue(`
[project]
name = "no-description"
version = "0.0.1"
readme = "README.rst"
`)

const result = await loadProjectInfo()

expect(result).toEqual({
name: 'no-description',
version: '0.0.1',
description: '',
})
})

it('should use the first metadata value found from the top', async () =>{
// Only the first occurance of the expected key names are taken.
fs.readFileSync.mockReturnValue(`
[other.section]
somebody = "once told me the"
world = "is gonna roll me"
[project]
name = "project-1"
version = "0.0.1"
description = "Project 1 Description"
[tool.poetry]
name = "project-2"
version = "0.0.2"
description = "Project 2 Description"
[tool.something.else]
name = "project-3"
version = "0.0.3"
description = "Project 3 Description"
`)

const result = await loadProjectInfo()

expect(result).toEqual({
name: 'project-1',
version: '0.0.1',
description: 'Project 1 Description',
})
})
})


jest.mock('fs')
10 changes: 10 additions & 0 deletions packages/gitmoji-changelog-documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ The helm preset looks for 3 properties in your `Chart.yaml`:
- version
- description

#### Python

The python preset looks for 3 properties in your `pyproject.toml`:

- name
- version
- description

(The value taken is the first one found in your `pyproject.toml` that matches the expected key name given above.)

### Add a preset

A preset need to export a function. When called this function must return three mandatory information about the project in which the cli has been called. The name of the project, a short description of it and its current version.
Expand Down

0 comments on commit 305a371

Please sign in to comment.