Skip to content

Metadata feature #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,20 @@ To see exactly what data is passed in to the templates, you can generate a JSON
```bash
auto-changelog --template json --output changelog-data.json
```
#### templates metadata

The metadata option set to true (by default false) allows you to get the information from `git config -l` and the information from `package.json`, which can be accessed from the metadata object in the template.

```js
{
"name": "my-awesome-package",
"auto-changelog": {
"metadata": true
}
}
}
```


#### `commit-list` helper

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"dependencies": {
"commander": "^7.2.0",
"handlebars": "^4.7.7",
"lodash": "^4.17.21",
"node-fetch": "^2.6.1",
"parse-github-url": "^1.0.2",
"semver": "^7.3.5"
Expand Down
42 changes: 42 additions & 0 deletions src/metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const merge = require('lodash/merge');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using lodash is a CVE magnet; please use lodash.merge instead.

const { cmd, dotNotationToObject, readJson, niceDate } = require('./utils')

const DIVIDER = '=';

const fetchGitConfig = async () => {
const config = (await cmd(`git config -l`))

return config
.trim()
.split('\n')
.reduce((acum, item) => {
const [key, value] = item.split(DIVIDER)
return merge(acum, dotNotationToObject(key, value))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why lodash and not Object.assign?

}, {})

}

const getMetadata = async (options) => {
if (options.metadata !== true) {
return {}
}

const PACKAGE_FILE = 'package.json'
const config = await fetchGitConfig();

const pkg = await readJson(PACKAGE_FILE)

const allowed = ['name', 'version', 'description', 'changos', 'keywords']

const meta = {};
allowed.forEach(element => {
meta[element] = pkg[element];
});

return JSON.parse(JSON.stringify({ now: niceDate(new Date()), ...meta, ...config }))

}

module.exports = {
getMetadata
}
9 changes: 6 additions & 3 deletions src/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const { fetchRemote } = require('./remote')
const { fetchTags } = require('./tags')
const { parseReleases } = require('./releases')
const { compileTemplate } = require('./template')
const { parseLimit, readFile, readJson, writeFile, fileExists, updateLog, formatBytes } = require('./utils')
const { getMetadata } = require('./metadata')
const { parseLimit, readFile, readJson, writeFile, fileExists, updateLog, formatBytes, niceDate, cmd } = require('./utils')

const DEFAULT_OPTIONS = {
output: 'CHANGELOG.md',
Expand Down Expand Up @@ -98,11 +99,13 @@ const run = async argv => {
const options = await getOptions(argv)
const log = string => options.stdout ? null : updateLog(string)
log('Fetching tags…')
const tags = await fetchTags(options)
const tags = await fetchTags(options, options)
log('Fetching metadata…')
const metadata = await getMetadata(options)
log(`${tags.length} version tags found…`)
const onParsed = ({ title }) => log(`Fetched ${title}…`)
const releases = await parseReleases(tags, options, onParsed)
const changelog = await compileTemplate(releases, options)
const changelog = await compileTemplate(releases, metadata, options)
await write(changelog, options, log)
}

Expand Down
6 changes: 3 additions & 3 deletions src/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const cleanTemplate = template => {
.replace(/\n\n$/, '\n')
}

const compileTemplate = async (releases, options) => {
const compileTemplate = async (releases, metadata, options) => {
const { template, handlebarsSetup } = options
if (handlebarsSetup) {
const path = /^\//.test(handlebarsSetup) ? handlebarsSetup : join(process.cwd(), handlebarsSetup)
Expand All @@ -88,9 +88,9 @@ const compileTemplate = async (releases, options) => {
}
const compile = Handlebars.compile(await getTemplate(template), COMPILE_OPTIONS)
if (template === 'json') {
return compile({ releases, options })
return compile({ releases, metadata, options })
}
return cleanTemplate(compile({ releases, options }))
return cleanTemplate(compile({ releases, metadata, options }))
}

module.exports = {
Expand Down
20 changes: 19 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ const readJson = async (path) => {
return JSON.parse(await readFile(path))
}

const dotNotationToObject = (str, value) => {
const props = str.split('.');
const last = props.pop()

let ref = {}
const object = ref;
props.forEach(prop => {
ref[prop] = {};
ref = ref[prop];
});

ref[last] = value;

return object
}


module.exports = {
updateLog,
formatBytes,
Expand All @@ -112,5 +129,6 @@ module.exports = {
readFile,
writeFile,
fileExists,
readJson
readJson,
dotNotationToObject
}
7 changes: 7 additions & 0 deletions templates/metadata.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# {{metadata.name}} Changelog

Description: {{metadata.description}}

release date: {{metadata.now}}
release owner name: {{metadata.user.name}}
release owner email: {{metadata.user.email}}
10 changes: 10 additions & 0 deletions test/data/metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
now: "20 January 2021",
version: "1.0.0",
name: "my-repository",
description: "description-repository",
user: { name: "John Doe", email: "[email protected]" },
core: {
editor: "nano",
},
};
7 changes: 7 additions & 0 deletions test/data/template-metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# my-repository Changelog

Description: description-repository

release date: 20 January 2021
release owner name: John Doe
release owner email: [email protected]
54 changes: 54 additions & 0 deletions test/metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const { describe, it, beforeEach, afterEach } = require("mocha");
const { expect } = require("chai");

const {
getMetadata,
__Rewire__: mock,
__ResetDependency__: unmock,
} = require("../src/metadata");

describe("metadata", () => {
beforeEach(() => {
mock("niceDate", () => "20 January 2021");
mock("fetchGitConfig", () => {
return {
user: { name: "John Doe", email: "[email protected]" },
core: {
editor: "nano",
},
};
});
mock("readJson", () => {
return {
version: "1.0.0",
name: "my-repository",
description: "description-repository",
};
});
});

afterEach(() => {
unmock("niceDate");
unmock("readJson");
unmock("fetchGitConfig");
});

it("should return data from git config and package json if metadata option is true", async () => {
expect(await getMetadata({ metadata: true })).to.deep.equal({
now: "20 January 2021",
version: "1.0.0",
name: "my-repository",
description: "description-repository",
user: { name: "John Doe", email: "[email protected]" },
core: {
editor: "nano",
},
});
});

it("should return an empty object if metadata option is undefined or false", async () => {
expect(await getMetadata({})).to.deep.equal({});
expect(await getMetadata({metadata:false})).to.deep.equal({});
expect(await getMetadata({metadata: 'true'})).to.deep.equal({});
});
});
22 changes: 15 additions & 7 deletions test/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,56 @@ const { expect } = require('chai')
const { join } = require('path')
const { readFile } = require('../src/utils')
const releases = require('./data/releases')
const metadata = require('./data/metadata')
const { compileTemplate } = require('../src/template')

describe('compileTemplate', () => {
it('compiles using compact template', async () => {
const expected = await readFile(join(__dirname, 'data', 'template-compact.md'))
expect(await compileTemplate(releases, { template: 'compact' })).to.equal(expected)
expect(await compileTemplate(releases, {}, { template: 'compact' })).to.equal(expected)
})

it('compiles using keepachangelog template', async () => {
const expected = await readFile(join(__dirname, 'data', 'template-keepachangelog.md'))
expect(await compileTemplate(releases, { template: 'keepachangelog' })).to.equal(expected)
expect(await compileTemplate(releases, {}, { template: 'keepachangelog' })).to.equal(expected)
})

it('compiles using json template', async () => {
const expected = await readFile(join(__dirname, 'data', 'template-json.json'))
expect(await compileTemplate(releases, { template: 'json' })).to.equal(expected)
expect(await compileTemplate(releases, {}, { template: 'json' })).to.equal(expected)
})

it('compiles using path to template file', async () => {
const path = join(__dirname, 'data', 'template-compact.md')
const expected = await readFile(path)
expect(await compileTemplate(releases, { template: path })).to.equal(expected)
expect(await compileTemplate(releases, {}, { template: path })).to.equal(expected)
})

it('compiles using url path', async () => {
const path = 'https://raw.githubusercontent.com/CookPete/auto-changelog/master/templates/compact.hbs'
const expected = await readFile(join(__dirname, 'data', 'template-compact.md'))
expect(await compileTemplate(releases, { template: path })).to.equal(expected)
expect(await compileTemplate(releases, {}, { template: path })).to.equal(expected)
}).timeout(10000)

it('throws an error when no template found', done => {
compileTemplate(releases, { template: 'not-found' })
compileTemplate(releases, {}, { template: 'not-found' })
.then(() => done('Should throw an error'))
.catch(() => done())
})

it('supports handlebarsSetup option', async () => {
const path = join(__dirname, 'data', 'template-custom-helper.md')
const expected = await readFile(join(__dirname, 'data', 'template-custom-helper-compiled.md'))
expect(await compileTemplate(releases, {
expect(await compileTemplate(releases, {}, {
template: path,
handlebarsSetup: './test/data/handlebars-setup.js'
})).to.equal(expected)
})

it('compiles using metadata template', async () => {
const expected = await readFile(join(__dirname, 'data', 'template-metadata.md'))
expect(await compileTemplate(releases, metadata, {
template: 'metadata',
})).to.equal(expected)
})
})
25 changes: 24 additions & 1 deletion test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const {
fileExists,
readJson,
__Rewire__: mock,
__ResetDependency__: unmock
__ResetDependency__: unmock,
dotNotationToObject
} = require('../src/utils')

describe('updateLog', () => {
Expand Down Expand Up @@ -103,3 +104,25 @@ describe('readJson', () => {
unmock('fs')
})
})

describe('dotNotationToObject', () => {
it('create the object from a string', () => {
const dotString = 'user.name';
const value = 'John Doe'

const expected = {
user: {name: value}
}

expect(dotNotationToObject(dotString, value)).to.deep.equal(expected)

})

it('show error when parameter is not a string value', () => {
const dotString = 32
const value = 'John Doe'

expect(()=> dotNotationToObject(dotString, value)).to.throw()

})
})