Skip to content
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

feat: github release integration #67

Merged
merged 20 commits into from
Mar 3, 2023
7 changes: 3 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
{
"extends": [
"eslint-config-unjs"
],
"extends": ["eslint-config-unjs"],
"rules": {
"unicorn/no-null": 0,
"unicorn/prefer-top-level-await": 0,
"unicorn/template-indent": 0
"unicorn/template-indent": 0,
"unicorn/no-process-exit": 0
}
}
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,39 @@ npx changelogen@latest --release
## CLI Usage

```sh
npx changelogen@latest [...args] [<rootDir>]
npx changelogen@latest [...args] [--dir <dir>]
```

**Arguments:**

- `--from`: Start commit reference. When not provided, **latest git tag** will be used as default.
- `--to`: End commit reference. When not provided, **latest commit in HEAD** will be used as default.
- `--rootDir`: Path to git repository. When not provided, **current working directory** will be used as as default.
- `--output`: Changelog file name to create or update. Defaults to `CHANGELOG.md` and resolved relative to rootDir. Use `--no-output` to write to console only.
- `--dir`: Path to git repository. When not provided, **current working directory** will be used as as default.
- `--output`: Changelog file name to create or update. Defaults to `CHANGELOG.md` and resolved relative to dir. Use `--no-output` to write to console only.
- `--bump`: Determine semver change and update version in `package.json`.
- `--release`. Bumps version in `package.json` and creates commit and git tags using local `git`. You can disable commit using `--no-commit` and tag using `--no-tag`.
- `-r`: Release as specific version.

### `changelogen gh release`

Changelogen has built-in functionality to sync with with Github releases!

In order to manually sync a release, you can use `changelogen gh release <versions|all> [--dir] [--token]`. It will parse current `CHANGELOG.md` from current repository (local, then remote) and create or update releases.

To enable this integration, make sure there is a valid `repository` field in `package.json` or `repo` is set in `.changelogenrc`.

By default in unauthenticated mode, changelogen will open a browser link to make manual release. By providing github token, it can be automated.

- Using environment variables or `.env`, use `CHANGELOGEN_TOKENS_GITHUB` or `GITHUB_TOKEN` or `GH_TOKEN`
- Using CLI args, use `--token <token>`
- Using global configuration, put `tokens.github=<token>` inside `~/.changlogenrc`

## Configuration

Configuration is loaded by [unjs/c12](https://github.com/unjs/c12) from cwd. You can use either `changelog.json`, `changelog.{ts,js,mjs,cjs}`, `.changelogrc` or use the `changelog` field in `package.json`.

See [./src/config.ts](./src/config.ts) for available options and defaults.


## 💻 Development

- Clone this repository
Expand All @@ -64,14 +77,12 @@ Made with 💛
Published under [MIT License](./LICENSE).

<!-- Badges -->

[npm-version-src]: https://img.shields.io/npm/v/changelogen?style=flat-square
[npm-version-href]: https://npmjs.com/package/changelogen

[npm-downloads-src]: https://img.shields.io/npm/dm/changelogen?style=flat-square
[npm-downloads-href]: https://npmjs.com/package/changelogen

[github-actions-src]: https://img.shields.io/github/workflow/status/unjs/changelogen/ci/main?style=flat-square
[github-actions-href]: https://github.com/unjs/changelogen/actions?query=workflow%3Aci

[codecov-src]: https://img.shields.io/codecov/c/gh/unjs/changelogen/main?style=flat-square
[codecov-href]: https://codecov.io/gh/unjs/changelogen
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@
},
"dependencies": {
"c12": "^1.1.2",
"colorette": "^2.0.19",
"consola": "^2.15.3",
"convert-gitmoji": "^0.1.3",
"execa": "^7.0.0",
"mri": "^1.2.0",
"node-fetch-native": "^1.0.2",
"ofetch": "^1.0.1",
"open": "^8.4.2",
"pathe": "^1.1.0",
"pkg-types": "^1.0.2",
"scule": "^1.0.0",
"semver": "^7.3.8"
Expand Down
26 changes: 19 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 15 additions & 91 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,28 @@
#!/usr/bin/env node
import { resolve } from "node:path";
import { existsSync, promises as fsp } from "node:fs";
import consola from "consola";
import mri from "mri";
import { execa } from "execa";
import { getGitDiff, parseCommits } from "./git";
import { loadChangelogConfig } from "./config";
import { generateMarkDown } from "./markdown";
import { bumpVersion } from "./semver";

async function main() {
const args = mri(process.argv.splice(2));
const cwd = resolve(args._[0] || "");
process.chdir(cwd);

const config = await loadChangelogConfig(cwd, {
from: args.from,
to: args.to,
output: args.output,
newVersion: args.r,
});

const logger = consola.create({ stdout: process.stderr });
logger.info(`Generating changelog for ${config.from}...${config.to}`);

const rawCommits = await getGitDiff(config.from, config.to);

// Parse commits as conventional commits
const commits = parseCommits(rawCommits, config).filter(
(c) =>
config.types[c.type] &&
!(c.type === "chore" && c.scope === "deps" && !c.isBreaking)
);

// Bump version optionally
if (args.bump || args.release) {
const newVersion = await bumpVersion(commits, config);
if (!newVersion) {
consola.error("Unable to bump version based on changes.");
process.exit(1);
}
config.newVersion = newVersion;
}
const subCommands = {
_default: () => import("./commands/default"),
gh: () => import("./commands/github"),
github: () => import("./commands/github"),
};

// Generate markdown
const markdown = await generateMarkDown(commits, config);
async function main() {
let subCommand = process.argv[2];

// Show changelog in CLI unless bumping or releasing
const displayOnly = !args.bump && !args.release;
if (displayOnly) {
consola.log("\n\n" + markdown + "\n\n");
if (!subCommand || subCommand.startsWith("-")) {
subCommand = "_default";
}

// Update changelog file (only when bumping or releasing or when --output is specified as a file)
if (typeof config.output === "string" && (args.output || !displayOnly)) {
let changelogMD: string;
if (existsSync(config.output)) {
consola.info(`Updating ${config.output}`);
changelogMD = await fsp.readFile(config.output, "utf8");
} else {
consola.info(`Creating ${config.output}`);
changelogMD = "# Changelog\n\n";
}

const lastEntry = changelogMD.match(/^###?\s+.*$/m);

if (lastEntry) {
changelogMD =
changelogMD.slice(0, lastEntry.index) +
markdown +
"\n\n" +
changelogMD.slice(lastEntry.index);
} else {
changelogMD += "\n" + markdown + "\n\n";
}

await fsp.writeFile(config.output, changelogMD);
if (!(subCommand in subCommands)) {
consola.error(`Unknown command ${subCommand}`);
process.exit(1);
}

// Commit and tag changes for release mode
if (args.release) {
if (args.commit !== false) {
const filesToAdd = [config.output, "package.json"].filter(
(f) => f && typeof f === "string"
) as string[];
await execa("git", ["add", ...filesToAdd], { cwd });
await execa(
"git",
["commit", "-m", `chore(release): v${config.newVersion}`],
{ cwd }
);
}
if (args.tag !== false) {
await execa(
"git",
["tag", "-am", "v" + config.newVersion, "v" + config.newVersion],
{ cwd }
);
}
}
await subCommands[subCommand]().then((r) =>
r.default(mri(process.argv.splice(3)))
);
}

main().catch(consola.error);
Loading