Skip to content

Commit

Permalink
Merge pull request #450 from semantic-release/beta
Browse files Browse the repository at this point in the history
  • Loading branch information
travi committed Jun 2, 2023
2 parents d630b6e + 5932f0b commit c215832
Show file tree
Hide file tree
Showing 20 changed files with 7,355 additions and 20,587 deletions.
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
@@ -0,0 +1,2 @@
# style: prettier
6964913a3286531f1b3aa0a264feee9e84bb2329
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Expand Up @@ -23,7 +23,8 @@ jobs:
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npm clean-install
- run: npm audit signatures
- run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
15 changes: 10 additions & 5 deletions .github/workflows/test.yml
Expand Up @@ -13,8 +13,9 @@ jobs:
strategy:
matrix:
node-version:
- 14.17
- 16
- 18.0.0
- 19
- 20
os:
- ubuntu-latest
- macos-latest
Expand All @@ -27,7 +28,7 @@ jobs:
with:
node-version: "${{ matrix.node-version }}"
cache: npm
- run: npm ci
- run: npm clean-install
- run: "npm run test:ci"
test:
runs-on: ubuntu-latest
Expand All @@ -36,9 +37,13 @@ jobs:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3
with:
node-version: 16
node-version: "lts/*"
cache: npm
- run: npm ci
- run: npm clean-install
- run: npm audit signatures
- name: Ensure dependencies are compatible with the engines range
run: npx ls-engines
- run: npm run lint
# https://github.com/lirantal/lockfile-lint#readme
- name: Scan lockfile for security issues
run: npx lockfile-lint --path package-lock.json
95 changes: 59 additions & 36 deletions README.md

Large diffs are not rendered by default.

48 changes: 24 additions & 24 deletions index.js
@@ -1,20 +1,22 @@
const {isUndefined} = require('lodash');
const parser = require('conventional-commits-parser').sync;
const filter = require('conventional-commits-filter');
const debug = require('debug')('semantic-release:commit-analyzer');
const loadParserConfig = require('./lib/load-parser-config.js');
const loadReleaseRules = require('./lib/load-release-rules.js');
const analyzeCommit = require('./lib/analyze-commit.js');
const compareReleaseTypes = require('./lib/compare-release-types.js');
const RELEASE_TYPES = require('./lib/default-release-types.js');
const DEFAULT_RELEASE_RULES = require('./lib/default-release-rules.js');
import { isUndefined } from "lodash-es";
import { sync as parser } from "conventional-commits-parser";
import filter from "conventional-commits-filter";
import debugFactory from "debug";
import loadParserConfig from "./lib/load-parser-config.js";
import loadReleaseRules from "./lib/load-release-rules.js";
import analyzeCommit from "./lib/analyze-commit.js";
import compareReleaseTypes from "./lib/compare-release-types.js";
import RELEASE_TYPES from "./lib/default-release-types.js";
import DEFAULT_RELEASE_RULES from "./lib/default-release-rules.js";

const debug = debugFactory("semantic-release:commit-analyzer");

/**
* Determine the type of release to create based on a list of commits.
*
* @param {Object} pluginConfig The plugin configuration.
* @param {String} pluginConfig.preset conventional-changelog preset ('angular', 'atom', 'codemirror', 'ember', 'eslint', 'express', 'jquery', 'jscs', 'jshint')
* @param {String} pluginConfig.config Requirable npm package with a custom conventional-changelog preset
* @param {String} pluginConfig.config Requireable npm package with a custom conventional-changelog preset
* @param {String|Array} pluginConfig.releaseRules A `String` to load an external module or an `Array` of rules.
* @param {Object} pluginConfig.parserOpts Additional `conventional-changelog-parser` options that will overwrite ones loaded by `preset` or `config`.
* @param {Object} context The semantic-release context.
Expand All @@ -23,43 +25,43 @@ const DEFAULT_RELEASE_RULES = require('./lib/default-release-rules.js');
*
* @returns {String|null} the type of release to create based on the list of commits or `null` if no release has to be done.
*/
async function analyzeCommits(pluginConfig, context) {
const {commits, logger} = context;
export async function analyzeCommits(pluginConfig, context) {
const { commits, logger } = context;
const releaseRules = loadReleaseRules(pluginConfig, context);
const config = await loadParserConfig(pluginConfig, context);
let releaseType = null;

filter(
commits
.filter(({message, hash}) => {
.filter(({ message, hash }) => {
if (!message.trim()) {
debug('Skip commit %s with empty message', hash);
debug("Skip commit %s with empty message", hash);
return false;
}

return true;
})
.map(({message, ...commitProps}) => ({rawMsg: message, message, ...commitProps, ...parser(message, config)}))
).every(({rawMsg, ...commit}) => {
.map(({ message, ...commitProps }) => ({ rawMsg: message, message, ...commitProps, ...parser(message, config) }))
).every(({ rawMsg, ...commit }) => {
logger.log(`Analyzing commit: %s`, rawMsg);
let commitReleaseType;

// Determine release type based on custom releaseRules
if (releaseRules) {
debug('Analyzing with custom rules');
debug("Analyzing with custom rules");
commitReleaseType = analyzeCommit(releaseRules, commit);
}

// If no custom releaseRules or none matched the commit, try with default releaseRules
if (isUndefined(commitReleaseType)) {
debug('Analyzing with default rules');
debug("Analyzing with default rules");
commitReleaseType = analyzeCommit(DEFAULT_RELEASE_RULES, commit);
}

if (commitReleaseType) {
logger.log('The release type for the commit is %s', commitReleaseType);
logger.log("The release type for the commit is %s", commitReleaseType);
} else {
logger.log('The commit should not trigger a release');
logger.log("The commit should not trigger a release");
}

// Set releaseType if commit's release type is higher
Expand All @@ -74,9 +76,7 @@ async function analyzeCommits(pluginConfig, context) {

return true;
});
logger.log('Analysis of %s commits complete: %s release', commits.length, releaseType || 'no');
logger.log("Analysis of %s commits complete: %s release", commits.length, releaseType || "no");

return releaseType;
}

module.exports = {analyzeCommits};
21 changes: 11 additions & 10 deletions lib/analyze-commit.js
@@ -1,22 +1,23 @@
const {isMatchWith, isString} = require('lodash');
const micromatch = require('micromatch');
const debug = require('debug')('semantic-release:commit-analyzer');
const RELEASE_TYPES = require('./default-release-types.js');
const compareReleaseTypes = require('./compare-release-types.js');
import { isMatchWith, isString } from "lodash-es";
import micromatch from "micromatch";
import debugFactory from "debug";
import RELEASE_TYPES from "./default-release-types.js";
import compareReleaseTypes from "./compare-release-types.js";

const debug = debugFactory("semantic-release:commit-analyzer");
/**
* Find all the rules matching and return the highest release type of the matching rules.
*
* @param {Array} releaseRules the rules to match the commit against.
* @param {Commit} commit a parsed commit.
* @return {string} the highest release type of the matching rules or `undefined` if no rule match the commit.
*/
module.exports = (releaseRules, commit) => {
export default (releaseRules, commit) => {
let releaseType;

releaseRules
.filter(
({breaking, revert, release, ...rule}) =>
({ breaking, revert, release, ...rule }) =>
// If the rule is not `breaking` or the commit doesn't have a breaking change note
(!breaking || (commit.notes && commit.notes.length > 0)) &&
// If the rule is not `revert` or the commit is not a revert
Expand All @@ -29,14 +30,14 @@ module.exports = (releaseRules, commit) => {
.every((match) => {
if (compareReleaseTypes(releaseType, match.release)) {
releaseType = match.release;
debug('The rule %o match commit with release type %o', match, releaseType);
debug("The rule %o match commit with release type %o", match, releaseType);
if (releaseType === RELEASE_TYPES[0]) {
debug('Release type %o is the highest possible. Stop analysis.', releaseType);
debug("Release type %o is the highest possible. Stop analysis.", releaseType);
return false;
}
} else {
debug(
'The rule %o match commit with release type %o but the higher release type %o has already been found for this commit',
"The rule %o match commit with release type %o but the higher release type %o has already been found for this commit",
match,
match.release,
releaseType
Expand Down
4 changes: 2 additions & 2 deletions lib/compare-release-types.js
@@ -1,4 +1,4 @@
const RELEASE_TYPES = require('./default-release-types.js');
import RELEASE_TYPES from "./default-release-types.js";

/**
* Test if a realease type is of higher level than a given one.
Expand All @@ -7,5 +7,5 @@ const RELEASE_TYPES = require('./default-release-types.js');
* @param {string} releaseType the release type to compare with.
* @return {Boolean} true if `releaseType` is higher than `currentReleaseType`.
*/
module.exports = (currentReleaseType, releaseType) =>
export default (currentReleaseType, releaseType) =>
!currentReleaseType || RELEASE_TYPES.indexOf(releaseType) < RELEASE_TYPES.indexOf(currentReleaseType);
44 changes: 22 additions & 22 deletions lib/default-release-rules.js
Expand Up @@ -3,32 +3,32 @@
*
* @type {Array}
*/
module.exports = [
{breaking: true, release: 'major'},
{revert: true, release: 'patch'},
export default [
{ breaking: true, release: "major" },
{ revert: true, release: "patch" },
// Angular
{type: 'feat', release: 'minor'},
{type: 'fix', release: 'patch'},
{type: 'perf', release: 'patch'},
{ type: "feat", release: "minor" },
{ type: "fix", release: "patch" },
{ type: "perf", release: "patch" },
// Atom
{emoji: ':racehorse:', release: 'patch'},
{emoji: ':bug:', release: 'patch'},
{emoji: ':penguin:', release: 'patch'},
{emoji: ':apple:', release: 'patch'},
{emoji: ':checkered_flag:', release: 'patch'},
{ emoji: ":racehorse:", release: "patch" },
{ emoji: ":bug:", release: "patch" },
{ emoji: ":penguin:", release: "patch" },
{ emoji: ":apple:", release: "patch" },
{ emoji: ":checkered_flag:", release: "patch" },
// Ember
{tag: 'BUGFIX', release: 'patch'},
{tag: 'FEATURE', release: 'minor'},
{tag: 'SECURITY', release: 'patch'},
{ tag: "BUGFIX", release: "patch" },
{ tag: "FEATURE", release: "minor" },
{ tag: "SECURITY", release: "patch" },
// ESLint
{tag: 'Breaking', release: 'major'},
{tag: 'Fix', release: 'patch'},
{tag: 'Update', release: 'minor'},
{tag: 'New', release: 'minor'},
{ tag: "Breaking", release: "major" },
{ tag: "Fix", release: "patch" },
{ tag: "Update", release: "minor" },
{ tag: "New", release: "minor" },
// Express
{component: 'perf', release: 'patch'},
{component: 'deps', release: 'patch'},
{ component: "perf", release: "patch" },
{ component: "deps", release: "patch" },
// JSHint
{type: 'FEAT', release: 'minor'},
{type: 'FIX', release: 'patch'},
{ type: "FEAT", release: "minor" },
{ type: "FIX", release: "patch" },
];
2 changes: 1 addition & 1 deletion lib/default-release-types.js
Expand Up @@ -3,4 +3,4 @@
*
* @type {Array}
*/
module.exports = ['major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease'];
export default ["major", "premajor", "minor", "preminor", "patch", "prepatch", "prerelease"];
21 changes: 12 additions & 9 deletions lib/load-parser-config.js
@@ -1,21 +1,24 @@
const {promisify} = require('util');
const {isPlainObject} = require('lodash');
const importFrom = require('import-from');
const conventionalChangelogAngular = require('conventional-changelog-angular');
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { isPlainObject } from "lodash-es";
import importFrom from "import-from";
import conventionalChangelogAngular from "conventional-changelog-angular";

/**
* Load `conventional-changelog-parser` options. Handle presets that return either a `Promise<Array>` or a `Promise<Function>`.
*
* @param {Object} pluginConfig The plugin configuration.
* @param {Object} pluginConfig.preset conventional-changelog preset ('angular', 'atom', 'codemirror', 'ember', 'eslint', 'express', 'jquery', 'jscs', 'jshint')
* @param {String} pluginConfig.config Requirable npm package with a custom conventional-changelog preset
* @param {Object} pluginConfig.parserOpts Additionnal `conventional-changelog-parser` options that will overwrite ones loaded by `preset` or `config`.
* @param {String} pluginConfig.config Requireable npm package with a custom conventional-changelog preset
* @param {Object} pluginConfig.parserOpts Additional `conventional-changelog-parser` options that will overwrite ones loaded by `preset` or `config`.
* @param {Object} context The semantic-release context.
* @param {String} context.cwd The current working directory.
* @return {Promise<Object>} a `Promise` that resolve to the `conventional-changelog-parser` options.
*/
module.exports = async ({preset, config, parserOpts, presetConfig}, {cwd}) => {
export default async ({ preset, config, parserOpts, presetConfig }, { cwd }) => {
let loadedConfig;
const __dirname = dirname(fileURLToPath(import.meta.url));

if (preset) {
const presetPackage = `conventional-changelog-${preset.toLowerCase()}`;
Expand All @@ -26,11 +29,11 @@ module.exports = async ({preset, config, parserOpts, presetConfig}, {cwd}) => {
loadedConfig = conventionalChangelogAngular;
}

loadedConfig = await (typeof loadedConfig === 'function'
loadedConfig = await (typeof loadedConfig === "function"
? isPlainObject(presetConfig)
? loadedConfig(presetConfig)
: promisify(loadedConfig)()
: loadedConfig);

return {...loadedConfig.parserOpts, ...parserOpts};
return { ...loadedConfig.parserOpts, ...parserOpts };
};
13 changes: 8 additions & 5 deletions lib/load-release-rules.js
@@ -1,6 +1,8 @@
const {isUndefined} = require('lodash');
const importFrom = require('import-from');
const RELEASE_TYPES = require('./default-release-types.js');
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { isUndefined } from "lodash-es";
import importFrom from "import-from";
import RELEASE_TYPES from "./default-release-types.js";

/**
* Load and validate the `releaseRules` rules.
Expand All @@ -15,12 +17,13 @@ const RELEASE_TYPES = require('./default-release-types.js');
*
* @return {Array} the loaded and validated `releaseRules`.
*/
module.exports = ({releaseRules}, {cwd}) => {
export default ({ releaseRules }, { cwd }) => {
let loadedReleaseRules;
const __dirname = dirname(fileURLToPath(import.meta.url));

if (releaseRules) {
loadedReleaseRules =
typeof releaseRules === 'string'
typeof releaseRules === "string"
? importFrom.silent(__dirname, releaseRules) || importFrom(cwd, releaseRules)
: releaseRules;

Expand Down

0 comments on commit c215832

Please sign in to comment.