semantic-release-gitmoji icon indicating copy to clipboard operation
semantic-release-gitmoji copied to clipboard

[feature request] generate changelog

Open Clumsy-Coder opened this issue 4 years ago • 9 comments

It'd be nice to generate changelog as well along with release notes

kinda like https://github.com/semantic-release/changelog

Clumsy-Coder avatar Aug 14 '20 01:08 Clumsy-Coder

kinda like https://github.com/semantic-release/changelog

That is actually where changelog should be generated, no matter what commit message conventions you are following. Have you ever try using @semantic-release/changelog along with semantic-release-gitmoji?

i.e.

{
  "plugins": [
    "semantic-release-gitmoji",
    "@semantic-release/changelog"
  ]
}

FYI,

There are steps that semantic release will perform in the release pipeline. https://github.com/semantic-release/semantic-release#release-steps

Especially Generate notes and Prepare are steps that semantic-release-gitmoji and @semantic-release/changelog are applied respectively.

The responsibility of semantic-release-gitmoji is to generate notes from gitmoji commits for the whole semantic release system in Generate notes step. After the step other plugins can further make use of the generated notes. For example, @semantic-release/github publishes the release notes to github releases in Publish step.

momocow avatar Aug 14 '20 02:08 momocow

I tried it with @semantic-release/changelog. All I got was the header with the version 1.0.0. nothing after that.

# v1.0.0 (2020-08-14)

NOTE: i created the commits using commitizen and cz-emoji

some of the commits I have are

:rotating_light: (app) Remove import <StatusBar /> component
:fire: (app) Remove <StatusBar /> component from being used
:pencil: (readme) add codecov markdown badge
:see_no_evil: (test-results) ignore test-results/ folder
:heavy_plus_sign: (npm) add enzyme npm package
:heavy_plus_sign: (npm) add enzyme-to-json npm package
:wrench: (jest) temporarily disable setupFiles properties
:heavy_plus_sign: (npm) add ts-jest npm package
:heavy_plus_sign: (npm) add jest-junit npm package
:heavy_plus_sign: (npm) add jest-html-reporters npm package
:wrench: (jest) Add jest config file
:pencil: (readme) Add androidBuild github-action markdown badge
:art: (github-action) Add new lines to make androidBuild more readable

Here's my config.

.releaserc.js

const { promisify } = require('util');
const dateFormat = require('dateformat');
const readFileAsync = promisify(require('fs').readFile);
const path = require('path');

const commitRules = require('./semantic-release/commitRules.js');

const TEMPLATE_DIR = 'node_modules/semantic-release-gitmoji/lib/assets/templates/';
const template = readFileAsync(path.join(TEMPLATE_DIR, 'default-template.hbs'));
const commitTemplate = readFileAsync(path.join(TEMPLATE_DIR, 'commit-template.hbs'));

const MAJOR = 'major';
const MINOR = 'minor';
const PATCH = 'patch';

module.exports = {
  branches: ['master'],
  plugins: [
    [
      'semantic-release-gitmoji',
      {
        releaseRules: {
          major: commitRules.filter(({ release }) => release === MAJOR).map(({ emoji }) => emoji),
          minor: commitRules.filter(({ release }) => release === MINOR).map(({ emoji }) => emoji),
          patch: commitRules.filter(({ release }) => release === PATCH).map(({ emoji }) => emoji),
        },
        releaseNotes: {
          template,
          partials: { commitTemplate },
          helpers: {
            datetime: function (format = 'UTC:yyyy-mm-dd') {
              return dateFormat(new Date(), format);
            },
          },
          issueResolution: {
            template: '{baseUrl}/{owner}/{repo}/issues/{ref}',
            baseUrl: 'https://github.com',
            source: 'github.com',
          },
        },
      },
    ],
    '@semantic-release/changelog',
    '@semantic-release/github',
    [
      '@semantic-release/git',
      {
        assets: ['CHANGELOG.md'],
      },
    ],
  ],
  tagFormat: '${version}',
};


.semantic-release/commitRules.js

const MAJOR = 'major';
const MINOR = 'minor';
const PATCH = 'patch';

module.exports = [
  { breaking: true, release: MAJOR },
  { revert: true, release: PATCH },
  {
    // type: 'style',
    emoji: ':art:',
    // description: 'Improving structure / format of the code.',
    release: PATCH,
  },
  {
    // type: 'perf',
    emoji: ':zap:',
    // description: 'Improving performance.',
    release: PATCH,
  },
  {
    // type: 'prune',
    emoji: ':fire:',
    // description: 'Removing code or files.',
    release: PATCH,
  },
  {
    // type: 'fix',
    emoji: ':bug:',
    // description: 'Fixing a bug.',
    release: PATCH,
  },
  {
    // type: 'quickfix',
    emoji: ':ambulance:',
    // description: 'Critical hotfix.',
    release: MINOR,
  },
  {
    // type: 'feature',
    emoji: ':sparkles:',
    // description: 'Introducing new features.',
    release: MINOR,
  },
  {
    // type: 'docs',
    emoji: ':pencil:',
    // description: 'Writing docs.',
    release: false,
  },
  {
    // type: 'deploy',
    emoji: ':rocket:',
    // description: 'Deploying stuff.',
    release: PATCH,
  },
  {
    // type: 'ui',
    emoji: ':lipstick:',
    // description: 'Updating the UI and style files.',
    release: PATCH,
  },
  {
    // type: 'init',
    emoji: ':tada:',
    // description: 'Initial commit.',
    release: PATCH,
  },
  {
    // type: 'test',
    emoji: ':white_check_mark:',
    // description: 'Adding tests.',
    release: false,
  },
  {
    // type: 'security',
    emoji: ':lock:',
    // description: 'Fixing security issues.',
    release: MINOR,
  },
  {
    // type: 'osx',
    emoji: ':apple:',
    // description: 'Fixing something on macOS.',
    release: PATCH,
  },
  {
    // type: 'linux',
    emoji: ':penguin:',
    // description: 'Fixing something on Linux.',
    release: PATCH,
  },
  {
    // type: 'windows',
    emoji: ':checkered_flag:',
    // description: 'Fixing something on Windows.',
    release: PATCH,
  },
  {
    // type: 'android',
    emoji: ':robot:',
    // description: 'Fixing something on Android.',
    release: PATCH,
  },
  {
    // type: 'ios',
    emoji: ':green_apple:',
    // description: 'Fixing something on iOS.',
    release: PATCH,
  },
  {
    // type: 'release',
    emoji: ':bookmark:',
    // description: 'Releasing / Version tags.',
    release: PATCH,
  },
  {
    // type: 'lint',
    emoji: ':rotating_light:',
    // description: 'Removing linter warnings.',
    release: PATCH,
  },
  {
    // type: 'wip',
    emoji: ':construction:',
    // description: 'Work in progress.',
    release: false, // don't create a release
  },
  {
    // type: 'fix-ci',
    emoji: ':green_heart:',
    // description: 'Fixing CI Build.',
    release: PATCH,
  },
  {
    // type: 'downgrade',
    emoji: ':arrow_down:',
    // description: ' Downgrading dependencies.',
    release: PATCH,
  },
  {
    // type: 'upgrade',
    emoji: ':arrow_up:',
    // description: ' Upgrading dependencies.',
    release: PATCH,
  },
  {
    // type: 'pushpin',
    emoji: ':pushpin:',
    // description: 'Pinning dependencies to specific versions.',
    release: false,
  },
  {
    // type: 'ci',
    emoji: ':construction_worker:',
    // description: 'Adding CI build system.',
    release: PATCH,
  },
  {
    // type: 'analytics',
    emoji: ':chart_with_upwards_trend:',
    // description: 'Adding analytics or tracking code.',
    release: PATCH,
  },
  {
    // type: 'refactoring',
    emoji: ':recycle:',
    // description: ' Refactoring code.',
    release: MINOR,
  },
  {
    // type: 'docker',
    emoji: ':whale:',
    // description: 'Work about Docker.',
    release: PATCH,
  },
  {
    // type: 'dep-add',
    emoji: ':heavy_plus_sign:',
    // description: 'Adding a dependency.',
    release: PATCH,
  },
  {
    // type: 'dep-rm',
    emoji: ':heavy_minus_sign:',
    // description: 'Removing a dependency.',
    release: PATCH,
  },
  {
    // type: 'config',
    emoji: ':wrench:',
    // description: 'Changing configuration files.',
    release: PATCH,
  },
  {
    // type: 'i18n',
    emoji: ':globe_with_meridians:',
    // description: 'Internationalization and localization.',
    release: PATCH,
  },
  {
    // type: 'typo',
    emoji: ':pencil2:',
    // description: ' Fixing typos.',
    release: PATCH,
  },
  {
    // type: 'poo',
    emoji: ':poop:',
    // description: 'Writing bad code that needs to be improved.',
    release: PATCH,
  },
  {
    // type: 'revert',
    emoji: ':rewind:',
    // description: 'Reverting changes.',
    release: PATCH,
  },
  {
    // type: 'merge',
    emoji: ':twisted_rightwards_arrows:',
    // description: 'Merging branches.',
    release: PATCH,
  },
  {
    // type: 'dep-up',
    emoji: ':package:',
    // description: 'Updating compiled files or packages.',
    release: PATCH,
  },
  {
    // type: 'compat',
    emoji: ':alien:',
    // description: 'Updating code due to external API changes.',
    release: PATCH,
  },
  {
    // type: 'mv',
    emoji: ':truck:',
    // description: 'Moving or renaming files.',
    release: PATCH,
  },
  {
    // type: 'license',
    emoji: ':page_facing_up:',
    // description: 'Adding or updating license.',
    release: false,
  },
  {
    // type: 'breaking',
    emoji: ':boom:',
    // description: 'Introducing breaking changes.',
    release: MAJOR,
  },
  {
    // type: 'assets',
    emoji: ':bento:',
    // description: 'Adding or updating assets.',
    release: PATCH,
  },
  {
    // type: 'review',
    emoji: ':ok_hand:',
    // description: 'Updating code due to code review changes.',
    release: PATCH,
  },
  {
    // type: 'access',
    emoji: ':wheelchair:',
    // description: 'Improving accessibility.',
    release: PATCH,
  },
  {
    // type: 'docs-code',
    emoji: ':bulb:',
    // description: 'Documenting source code.',
    release: PATCH,
  },
  {
    // type: 'beer',
    emoji: ':beers:',
    // description: 'Writing code drunkenly.',
    release: PATCH,
  },
  {
    // type: 'texts',
    emoji: ':speech_balloon:',
    // description: 'Updating text and literals.',
    release: PATCH,
  },
  {
    // type: 'db',
    emoji: ':card_file_box:',
    // description: ' Performing database related changes.',
    release: PATCH,
  },
  {
    // type: 'log-add',
    emoji: ':loud_sound:',
    // description: 'Adding logs.',
    release: false,
  },
  {
    // type: 'log-rm',
    emoji: ':mute:',
    // description: 'Removing logs.',
    release: false,
  },
  {
    // type: 'contrib-add',
    emoji: ':busts_in_silhouette:',
    // description: 'Adding contributor(s).',
    release: false,
  },
  {
    // type: 'ux',
    emoji: ':children_crossing:',
    // description: 'Improving user experience / usability.',
    release: PATCH,
  },
  {
    // type: 'arch',
    emoji: ':building_construction:',
    // description: ' Making architectural changes.',
    release: PATCH,
  },
  {
    // type: 'iphone',
    emoji: ':iphone:',
    // description: 'Working on responsive design.',
    release: PATCH,
  },
  {
    // type: 'clown-face',
    emoji: ':clown_face:',
    // description: 'Mocking things.',
    release: false,
  },
  {
    // type: 'egg',
    emoji: ':egg:',
    // description: 'Adding an easter egg.',
    release: PATCH,
  },
  {
    // type: 'see-no-evil',
    emoji: ':see_no_evil:',
    // description: 'Adding or updating a .gitignore file',
    release: false,
  },
  {
    // type: 'camera-flash',
    emoji: ':camera_flash:',
    // description: 'Adding or updating snapshots',
    release: false,
  },
  {
    // type: 'experiment',
    emoji: ':alembic:',
    // description: ' Experimenting new things',
    release: false,
  },
  {
    // type: 'seo',
    emoji: ':mag:',
    // description: 'Improving SEO',
    release: PATCH,
  },
  {
    // type: 'k8s',
    emoji: ':wheel_of_dharma:',
    // description: ' Work about Kubernetes',
    release: PATCH,
  },
  {
    // type: 'types',
    emoji: ':label:',
    // description: ' Adding or updating types (Flow, TypeScript)',
    release: PATCH,
  },
  {
    // type: 'seed',
    emoji: ':seedling:',
    // description: 'Adding or updating seed files',
    release: PATCH,
  },
  {
    // type: 'flags',
    emoji: ':triangular_flag_on_post:',
    // description: 'Adding, updating, or removing feature flags',
    release: PATCH,
  },
  {
    // type: 'animation',
    emoji: ':dizzy:',
    // description: 'Adding or updating animations and transitions',
    release: PATCH,
  },
];


Clumsy-Coder avatar Aug 14 '20 14:08 Clumsy-Coder

Sorry for late response. I will have a try to reproduce by this weekend.

momocow avatar Aug 25 '20 12:08 momocow

@Clumsy-Coder Hi, I might get the issue for your configuration.

Please take a look at the default template first.

https://github.com/momocow/semantic-release-gitmoji/blob/b5f20fd1e4a77d44f2af944c1c2a1a6f73228c79/lib/assets/templates/default-template.hbs#L8-L41

The template only renders a set of predefined gitmoji commits, i.e. commits whose emojis are one of :sparkles:, :bug:, :ambulance:, :lock: and :boom:.

You can try to provide your own template with all your gitmojis.


In the default template gitmojis are explicitly rendered through #with due to the limitation of handlebars which cannot iterate over objects and the plugin has no knowledge about the shape of the commits object. (Keys of the object are emoji names that appears in related commits)

To do dynamic commit rendering with the default template, the commits object requires refactor for handlebars to iterate it easily.

This may be included in the future improvement but in the current version you need a custom template to make your configuration work.

momocow avatar Aug 31 '20 10:08 momocow

I see, so for now you have to provide your own template file. Which is basically copy paste for each gitmoji used with their own Titles.

I'd be great to have this done automatically in the future.

Clumsy-Coder avatar Sep 09 '20 16:09 Clumsy-Coder

I'd be great to have this done automatically in the future.

I'll keep this issue open until the feature comes out.

momocow avatar Sep 12 '20 15:09 momocow

Hey @momocow, A PR that introduces semver attributes to gitmojis has been merged: https://github.com/carloscuesta/gitmoji/pull/692 That will help with setting default releaseRules. I'll be glad to open a PR, just want to check with you first.


And what do you think of grouping the commits in the template by their semantic level (major, minor, patch) instead of grouping them by emojis.

The final template can look like this:

{{#if compareUrl}}
# [v{{nextRelease.version}}]({{compareUrl}}) ({{datetime "UTC:yyyy-mm-dd"}})
{{else}}
# v{{nextRelease.version}} ({{datetime "UTC:yyyy-mm-dd"}})
{{/if}}

{{#with commits}}
{{#if major}}
### 💥 Breaking Changes
{{#each major}}
- {{> commitTemplate}}
{{/each}}
{{/if}}

{{#if minor}}
### ✨ New Features
{{#each minor}}
- {{> commitTemplate}}
{{/each}}
{{/if}}

{{#if patch}}
### Others
{{#each patch}}
- {{gitmoji}} {{> commitTemplate}}
{{/each}}
{{/if}}
{{/with}}

ilyasmez avatar Feb 04 '21 13:02 ilyasmez

@ilyasmez It looks awesome, which really simplifies the complexity of release notes templates!

PRs are welcome ;)

momocow avatar Mar 10 '21 01:03 momocow

As a gitmoji fan, I'd also love to see this PR-ed and merged 🚀

Happy to help testing 🤗

dgilperez avatar Jul 22 '21 15:07 dgilperez

Hey their,

I managed to find a workaround for this issues. If you prefer to group commit by their rules and use of mention Gitmojis Semver https://github.com/carloscuesta/gitmoji/pull/692, you could do as follow.

You will need to setup rules in an object to be able to use them in an handblerbars helpers which will reorganise commits by type of release instead of gitmoji, as shown below:

// in ".releaserc.js" or "release.config.js"

const {promisify} = require("util");
const dateFormat = require("dateformat");
const readFileAsync = promisify(require("fs").readFile);
const path = require("path");

// Use of Gitmojis to create rules
const gitmojis = require("gitmojis").gitmojis;

const TEMPLATE_DIR = "./.semantic-release/templates/";
const template = readFileAsync(path.join(TEMPLATE_DIR, "default-template.hbs"));
const commitTemplate = readFileAsync(path.join(TEMPLATE_DIR, "commit-template.hbs"));

const MAJOR = "major";
const MINOR = "minor";
const PATCH = "patch";
// Store rules based on Gitmoji semver key in a object for later
const RULES = {
  major: gitmojis
    .filter(({semver}) => semver === MAJOR)
    .map(({emoji}) => emoji),
  minor: gitmojis
    .filter(({semver}) => semver === MINOR)
    .map(({emoji}) => emoji),
  patch: gitmojis
    .filter(({semver}) => semver === PATCH)
    .map(({emoji}) => emoji),
  // Not needed, just if you wish to render commit 
  // with gitmojis without semver.
  others: gitmojis
    .filter(({semver}) => semver === null)
    .map(({emoji}) => emoji),
};

module.exports = {
  branches: ["main"],
  plugins: [
    [
      "semantic-release-gitmoji",
      {
        releaseRules: RULES,
        releaseNotes: {
          template,
          partials: {commitTemplate},
          helpers: {
            datetime: function (format = "UTC:yyyy-mm-dd") {
              return dateFormat(new Date(), format);
            },
            // Handlebars helper to reorganise commits, not optimal though
            commitlist: function (commits, options) {
              let commitlist = {};
              let currRule = "not_defined";
              const rules = RULES;
              for (const iGitmoji in commits) {
                currRule = "not_defined";
                for (const iRule in rules) {
                  if (rules[iRule].includes(iGitmoji)) {
                    if (
                      !Object.prototype.hasOwnProperty.call(commitlist, iRule)
                    ) {
                      commitlist[iRule] = [];
                    }
                    currRule = iRule;
                    break;
                  }
                }
                if (
                  currRule == "not_defined" &&
                  !Object.prototype.hasOwnProperty.call(commitlist, currRule)
                ) {
                  commitlist[currRule] = [];
                }
                for (
                  let idxCommit = 0;
                  idxCommit < commits[iGitmoji].length;
                  idxCommit++
                ) {
                  commitlist[currRule].push(commits[iGitmoji][idxCommit]);
                }
              }
              options.data.root["commits"] = commitlist;
            },
          },
          issueResolution: {
            template: "{baseUrl}/{owner}/{repo}/issues/{ref}",
            baseUrl: "https://github.com",
          },
        },
      },
    ],
    // Generate changelog
    [
      "@semantic-release/changelog",
      {
        changelogFile: "CHANGELOG.md",
        changelogTitle: "# CHANGELOG",
      },
    ],
    // Commit changelog
    [
      "@semantic-release/git",
      {
        assets: ["CHANGELOG.md"],
        message: "🔖 ${nextRelease.version} [skip-ci]\n\n${nextRelease.notes}",
      },
    ],
  ],
  tagFormat: "${version}",
};

Them, you will just need to provide a custom handlebars template such as the example below:

{{#if compareUrl}}
## [v{{nextRelease.version}}]({{compareUrl}}) ({{datetime "UTC:yyyy-mm-dd"}})
{{else}}
## v{{nextRelease.version}} ({{datetime "UTC:yyyy-mm-dd"}})
{{/if}}

{{commitlist commits}}
{{#with commits}}
{{#if major}}
### Major
{{#each major}}
  * {{> commitTemplate}}
{{/each}}
{{/if}}


{{#if minor}}
### Minor
{{#each minor}}
  * {{> commitTemplate}}
{{/each}}
{{/if}}


{{#if patch}}
### Patch
{{#each patch}}
  * {{> commitTemplate}}
{{/each}}
{{/if}}
{{/with}}

rdeville avatar Dec 29 '22 07:12 rdeville

Fixed by #52 as v1.6.0.

Thanks for the PR by @rdeville

momocow avatar Jan 12 '23 15:01 momocow