standard-release / app

Language independent GitHub App for creating GitHub Releases, following the Conventional Commits and SemVer specifications
https://github.com/apps/standard-release
Apache License 2.0
13 stars 2 forks source link

Reimplement from scratch - done (in ~100 lines as a whole) #23

Closed tunnckoCore closed 5 years ago

tunnckoCore commented 6 years ago

Just because of the few updates and new packages that I published from locally.

So, the implementation is near 50 lines, excluding the template and release rendering.

const getConfig = require('probot-config')
const detector = require('detect-next-version') // v3 (in the `next` tag)

module.exports = (robot) => {
  robot.on('push', (context) => {
    const settingsConfig = await getConfig(context, 'new-release.yml')
    const config = Object.assign({}, defaultConfig, settingsConfig)

    if (context.payload.ref !== `refs/heads/${config.defaultBranch}`) {
      return
    }

    let pkgData = null

    try {
      pkgData = await context.github.repos.getContent(context.repo({
        ref: context.payload.ref,
        path: 'package.json'
      }))
    } catch (err) {
      robot.log(err)
      return
    }

    // for ensurance, sometimes.. js can be bad boy.
    if (!pkgData) return

    const pkgJSON = JSON.parse(Buffer.from(pkgData.content, 'base64'))

    const pkgMeta = detector(pkgJSON.name, context.payload.head_commit)

    // If no need for bump, then exit.
    if (!pkgMeta.increment) {
      robot.log('No need for release publishing')
      return
    }

    // pkgMeta is like `{ lastVersion, nextVersion, pkg, increment }`
    robot.log(pkgMeta)

    // So, now:
    // 1. get release template from config (or default one)
    // 2. pass locals (eg. pkgMeta and more) to it
    // 3. render it
    // 4. pass it to context.github.createRelease

    // And we are done.
  })
}
tunnckoCore commented 5 years ago

The continuation

import { parse, plugins } from 'parse-commit-message';
import detector from 'detect-next-version';

const sha = 'ede8c2677df83475009012fef7bb0a4b3e467eb3';
const apiCommits = [
  { sha, message: 'fix: qux baz', author: { name: 'Charlike Mike Reagent ' } },
  { sha, message: 'fix: 123 qq', author: { name: 'Charlike Mike Reagent ' } },
  { sha, message: 'feat: foz', author: { name: 'Charlike Mike Reagent ' } },
];

const author = {
  login: 'tunnckoCore',
  id: 5038030,
};

/* eslint-disable no-param-reassign */
const context = {
  payload: {
    repository: {
      full_name: 'tunnckoCoreLabs/qq5',
    },
    commits: apiCommits,
    author,
  },
};

const commits = context.payload.commits.map((commit) => {
  const cmt = parse(commit.message, plugins);
  cmt.sha = commit.sha;
  cmt.author = context.payload.author;
  cmt.repository = context.payload.repository.full_name;
  return cmt;
});

detector('@tunnckocore/qq5', commits).then(console.log);

// { increment: 'minor',
//   patch:
//    [ { header: [Object],
//        body: null,
//        footer: null,
//        mentions: [],
//        increment: 'patch',
//        isBreaking: false,
//        sha: 'ede8c2677df83475009012fef7bb0a4b3e467eb3',
//        author: [Object],
//        repository: 'tunnckoCoreLabs/qq5' },
//      { header: [Object],
//        body: null,
//        footer: null,
//        mentions: [],
//        increment: 'patch',
//        isBreaking: false,
//        sha: 'ede8c2677df83475009012fef7bb0a4b3e467eb3',
//        author: [Object],
//        repository: 'tunnckoCoreLabs/qq5' } ],
//   minor:
//    [ { header: [Object],
//        body: null,
//        footer: null,
//        mentions: [],
//        increment: 'minor',
//        isBreaking: false,
//        sha: 'ede8c2677df83475009012fef7bb0a4b3e467eb3',
//        author: [Object],
//        repository: 'tunnckoCoreLabs/qq5' } ],
//   pkg:
//    { name: '@tunnckocore/qq5',
//      version: '0.1.0',
//      main: 'index.js',
//      license: 'MIT',
//      publishConfig: { access: 'public', tag: 'latest' } },
//   lastVersion: '0.1.0',
//   nextVersion: '0.2.0' }
tunnckoCore commented 5 years ago

the render to release body

export default function render(locals) {
  const tpl = [];
  const { repository: repo, lastVersion: from, nextVersion: to } = locals;

  /* eslint-disable no-param-reassign */
  locals.compareLink = `https://github.com/${repo}/compare/${from}..v${to}`;

  tpl.push(`## [v${to}](${locals.compareLink}) (${locals.date})`, '');

  ['major', 'minor', 'patch'].forEach((type) => {
    if (locals[type]) {
      let heading = null;

      if (locals.major) {
        heading = '### :exclamation: BREAKING CHANGES! :scream:';
      }
      if (locals.minor) {
        heading = '### :tada: New Features';
      }
      if (locals.patch) {
        heading = '### :bug: Bug Fixes';
      }

      tpl.push(heading, '');

      locals[type].forEach((commit) => {
        const profile = (commit.author && commit.author.login) || '';
        let hash = commit.sha ? commit.sha.slice(0, 7) : null;
        hash = hash ? `(#${hash}) ` : '';

        tpl.push(`- ${commit.header.toString()} ${hash}${profile}`);
      });

      const excludeSignOff = (zz) => {
        const val = zz
          .split('\n')
          .filter((x) => !x.startsWith('Signed-off-by'));
        return val;
      };

      locals[type].forEach((commit) => {
        if (commit.body) {
          tpl.push('', excludeSignOff(commit.body));
        }
        if (commit.footer) {
          tpl.push('', excludeSignOff(commit.footer));
        }
        if (commit.mentions && commit.mentions.length > 0) {
          tpl.push('', commit.mentions.join(' '));
        }
      });

      tpl.push('', '');
    }
  });

  const link = `[\`v${locals.from}...v${locals.to}\`](${locals.compareLink})`;

  return tpl
    .concat('', link)
    .filter((x) => x !== null && x !== ' ')
    .join('\n')
    .trim();
}