mirror of
https://github.com/changesets/action
synced 2026-10-26 16:39:56 +08:00
Fixed an issue with ignored package being included in PR body incorrectly (#38)
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`version creates simple PR 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"base": "some-branch",
|
||||
"body": "This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to some-branch, this PR will be updated.
|
||||
|
||||
# Releases
|
||||
## simple-project-pkg-a@1.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Awesome feature
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [undefined]
|
||||
- simple-project-pkg-b@1.1.0
|
||||
|
||||
## simple-project-pkg-b@1.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Awesome feature
|
||||
",
|
||||
"head": "changeset-release/some-branch",
|
||||
"owner": "changesets",
|
||||
"repo": "action",
|
||||
"title": "Version Packages",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`version doesn't include ignored package that got a dependency update in the PR body 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"base": "some-branch",
|
||||
"body": "This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to some-branch, this PR will be updated.
|
||||
|
||||
# Releases
|
||||
## ignored-package-pkg-b@1.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Awesome feature
|
||||
",
|
||||
"head": "changeset-release/some-branch",
|
||||
"owner": "changesets",
|
||||
"repo": "action",
|
||||
"title": "Version Packages",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`version only includes bumped packages in the PR body 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"base": "some-branch",
|
||||
"body": "This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to some-branch, this PR will be updated.
|
||||
|
||||
# Releases
|
||||
## simple-project-pkg-a@1.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Awesome feature
|
||||
",
|
||||
"head": "changeset-release/some-branch",
|
||||
"owner": "changesets",
|
||||
"repo": "action",
|
||||
"title": "Version Packages",
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -0,0 +1,45 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`it sorts the things right 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"highestLevel": 3,
|
||||
"name": "c",
|
||||
"private": false,
|
||||
},
|
||||
Object {
|
||||
"highestLevel": 1,
|
||||
"name": "b",
|
||||
"private": false,
|
||||
},
|
||||
Object {
|
||||
"highestLevel": 3,
|
||||
"name": "a",
|
||||
"private": true,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`it works 1`] = `
|
||||
"### Major Changes
|
||||
|
||||
- [2164a779](https://github.com/keystonejs/keystone-5/commit/2164a779):
|
||||
|
||||
- Replace jade with pug because Jade was renamed to Pug, and \`jade\` package is outdated
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [81dc0be5](https://github.com/keystonejs/keystone-5/commit/81dc0be5):
|
||||
|
||||
- Update dependencies
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`it works 2`] = `
|
||||
"### Patch Changes
|
||||
|
||||
- [19fe6c1b](https://github.com/keystonejs/keystone-5/commit/19fe6c1b):
|
||||
|
||||
Move frontmatter in docs into comments
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,61 @@
|
||||
import { exec } from "@actions/exec";
|
||||
import { execWithOutput } from "./utils";
|
||||
|
||||
export const setupUser = async () => {
|
||||
await exec("git", [
|
||||
"config",
|
||||
"--global",
|
||||
"user.name",
|
||||
`"github-actions[bot]"`,
|
||||
]);
|
||||
await exec("git", [
|
||||
"config",
|
||||
"--global",
|
||||
"user.email",
|
||||
`"github-actions[bot]@users.noreply.github.com"`,
|
||||
]);
|
||||
};
|
||||
|
||||
export const pullBranch = async (branch: string) => {
|
||||
await exec("git", ["pull", "origin", branch]);
|
||||
};
|
||||
|
||||
export const push = async (
|
||||
branch: string,
|
||||
{ force, includeTags }: { force?: boolean; includeTags?: boolean } = {}
|
||||
) => {
|
||||
await exec(
|
||||
"git",
|
||||
[
|
||||
"push",
|
||||
"origin",
|
||||
`HEAD:${branch}`,
|
||||
includeTags && "--tags",
|
||||
force && "--force",
|
||||
].filter<string>(Boolean as any)
|
||||
);
|
||||
};
|
||||
|
||||
export const switchToMaybeExistingBranch = async (branch: string) => {
|
||||
let { stderr } = await execWithOutput("git", ["checkout", branch], {
|
||||
ignoreReturnCode: true,
|
||||
});
|
||||
let isCreatingBranch = !stderr
|
||||
.toString()
|
||||
.includes(`Switched to a new branch '${branch}'`);
|
||||
if (isCreatingBranch) {
|
||||
await exec("git", ["checkout", "-b", branch]);
|
||||
}
|
||||
};
|
||||
|
||||
export const reset = async (
|
||||
pathSpec: string,
|
||||
mode: "hard" | "soft" | "mixed" = "hard"
|
||||
) => {
|
||||
await exec("git", ["reset", `--${mode}`, pathSpec]);
|
||||
};
|
||||
|
||||
export const commitAll = async (message: string) => {
|
||||
await exec("git", ["add", "."]);
|
||||
await exec("git", ["commit", "-m", message]);
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
import * as core from "@actions/core";
|
||||
import fs from "fs-extra";
|
||||
import * as gitUtils from "./gitUtils";
|
||||
import { runPublish, runVersion } from "./run";
|
||||
import readChangesetState from "./readChangesetState";
|
||||
|
||||
const getOptionalInput = (name: string) => core.getInput(name) || undefined;
|
||||
|
||||
(async () => {
|
||||
let githubToken = process.env.GITHUB_TOKEN;
|
||||
|
||||
if (!githubToken) {
|
||||
core.setFailed("Please add the GITHUB_TOKEN to the changesets action");
|
||||
return;
|
||||
}
|
||||
|
||||
core.setOutput("published", "false");
|
||||
core.setOutput("publishedPackages", "[]");
|
||||
|
||||
console.log("setting git user");
|
||||
await gitUtils.setupUser();
|
||||
|
||||
console.log("setting GitHub credentials");
|
||||
await fs.writeFile(
|
||||
`${process.env.HOME}/.netrc`,
|
||||
`machine github.com\nlogin github-actions[bot]\npassword ${githubToken}`
|
||||
);
|
||||
|
||||
let { changesets } = await readChangesetState();
|
||||
|
||||
let publishScript = core.getInput("publish");
|
||||
let hasChangesets = changesets.length !== 0;
|
||||
let hasPublishScript = !!publishScript;
|
||||
|
||||
switch (true) {
|
||||
case !hasChangesets && !hasPublishScript:
|
||||
console.log("No changesets found");
|
||||
return;
|
||||
case !hasChangesets && hasPublishScript: {
|
||||
console.log(
|
||||
"No changesets found, attempting to publish any unpublished packages to npm"
|
||||
);
|
||||
|
||||
let npmrcPath = `${process.env.HOME}/.npmrc`;
|
||||
if (fs.existsSync(npmrcPath)) {
|
||||
console.log("Found existing .npmrc file");
|
||||
} else {
|
||||
console.log("No .npmrc file found, creating one");
|
||||
fs.writeFileSync(
|
||||
npmrcPath,
|
||||
`//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}`
|
||||
);
|
||||
}
|
||||
|
||||
const result = await runPublish({
|
||||
script: publishScript,
|
||||
githubToken,
|
||||
});
|
||||
|
||||
if (result.published) {
|
||||
core.setOutput("published", "true");
|
||||
core.setOutput(
|
||||
"publishedPackages",
|
||||
JSON.stringify(result.publishedPackages)
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case hasChangesets:
|
||||
await runVersion({
|
||||
script: getOptionalInput("version"),
|
||||
githubToken,
|
||||
prTitle: getOptionalInput("title"),
|
||||
commitMessage: getOptionalInput("commit"),
|
||||
hasPublishScript,
|
||||
});
|
||||
return;
|
||||
}
|
||||
})().catch((err) => {
|
||||
console.error(err);
|
||||
core.setFailed(err.message);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { PreState, NewChangeset } from "@changesets/types";
|
||||
import { readPreState } from "@changesets/pre";
|
||||
import readChangesets from "@changesets/read";
|
||||
|
||||
export type ChangesetState = {
|
||||
preState: PreState | undefined;
|
||||
changesets: NewChangeset[];
|
||||
};
|
||||
|
||||
export default async function readChangesetState(
|
||||
cwd: string = process.cwd()
|
||||
): Promise<ChangesetState> {
|
||||
let preState = await readPreState(cwd);
|
||||
let isInPreMode = preState !== undefined && preState.mode === "pre";
|
||||
|
||||
let changesets = await readChangesets(cwd);
|
||||
|
||||
if (isInPreMode) {
|
||||
let changesetsToFilter = new Set(preState.changesets);
|
||||
changesets = changesets.filter((x) => !changesetsToFilter.has(x.id));
|
||||
}
|
||||
|
||||
return {
|
||||
preState: isInPreMode ? preState : undefined,
|
||||
changesets,
|
||||
};
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
import fixturez from "fixturez";
|
||||
import * as github from "@actions/github";
|
||||
import fs from "fs-extra";
|
||||
import path from "path";
|
||||
import writeChangeset from "@changesets/write";
|
||||
import { Changeset } from "@changesets/types";
|
||||
import { runVersion } from "./run";
|
||||
|
||||
jest.mock("@actions/github", () => ({
|
||||
context: {
|
||||
repo: {
|
||||
owner: "changesets",
|
||||
repo: "action",
|
||||
},
|
||||
ref: "refs/heads/some-branch",
|
||||
sha: "xeac7",
|
||||
},
|
||||
GitHub: jest.fn(),
|
||||
}));
|
||||
jest.mock("./gitUtils");
|
||||
|
||||
let mockedGithubMethods = {
|
||||
search: {
|
||||
issuesAndPullRequests: jest.fn(),
|
||||
},
|
||||
pulls: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
repos: {
|
||||
createRelease: jest.fn(),
|
||||
},
|
||||
};
|
||||
(github.GitHub as any).mockImplementation(() => mockedGithubMethods);
|
||||
|
||||
let f = fixturez(__dirname);
|
||||
|
||||
const linkNodeModules = async (cwd: string) => {
|
||||
await fs.symlink(
|
||||
path.join(__dirname, "..", "node_modules"),
|
||||
path.join(cwd, "node_modules")
|
||||
);
|
||||
};
|
||||
const writeChangesets = (changesets: Changeset[], cwd: string) => {
|
||||
return Promise.all(changesets.map((commit) => writeChangeset(commit, cwd)));
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("version", () => {
|
||||
it("creates simple PR", async () => {
|
||||
let cwd = f.copy("simple-project");
|
||||
linkNodeModules(cwd);
|
||||
|
||||
mockedGithubMethods.search.issuesAndPullRequests.mockImplementationOnce(
|
||||
() => ({ data: { items: [] } })
|
||||
);
|
||||
|
||||
await writeChangesets(
|
||||
[
|
||||
{
|
||||
releases: [
|
||||
{
|
||||
name: "simple-project-pkg-a",
|
||||
type: "minor",
|
||||
},
|
||||
{
|
||||
name: "simple-project-pkg-b",
|
||||
type: "minor",
|
||||
},
|
||||
],
|
||||
summary: "Awesome feature",
|
||||
},
|
||||
],
|
||||
cwd
|
||||
);
|
||||
|
||||
await runVersion({
|
||||
githubToken: "@@GITHUB_TOKEN",
|
||||
cwd,
|
||||
});
|
||||
|
||||
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("only includes bumped packages in the PR body", async () => {
|
||||
let cwd = f.copy("simple-project");
|
||||
linkNodeModules(cwd);
|
||||
|
||||
mockedGithubMethods.search.issuesAndPullRequests.mockImplementationOnce(
|
||||
() => ({ data: { items: [] } })
|
||||
);
|
||||
|
||||
await writeChangesets(
|
||||
[
|
||||
{
|
||||
releases: [
|
||||
{
|
||||
name: "simple-project-pkg-a",
|
||||
type: "minor",
|
||||
},
|
||||
],
|
||||
summary: "Awesome feature",
|
||||
},
|
||||
],
|
||||
cwd
|
||||
);
|
||||
|
||||
await runVersion({
|
||||
githubToken: "@@GITHUB_TOKEN",
|
||||
cwd,
|
||||
});
|
||||
|
||||
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("doesn't include ignored package that got a dependency update in the PR body", async () => {
|
||||
let cwd = f.copy("ignored-package");
|
||||
linkNodeModules(cwd);
|
||||
|
||||
mockedGithubMethods.search.issuesAndPullRequests.mockImplementationOnce(
|
||||
() => ({ data: { items: [] } })
|
||||
);
|
||||
|
||||
await writeChangesets(
|
||||
[
|
||||
{
|
||||
releases: [
|
||||
{
|
||||
name: "ignored-package-pkg-b",
|
||||
type: "minor",
|
||||
},
|
||||
],
|
||||
summary: "Awesome feature",
|
||||
},
|
||||
],
|
||||
cwd
|
||||
);
|
||||
|
||||
await runVersion({
|
||||
githubToken: "@@GITHUB_TOKEN",
|
||||
cwd,
|
||||
});
|
||||
|
||||
expect(mockedGithubMethods.pulls.create.mock.calls[0]).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
import { exec } from "@actions/exec";
|
||||
import * as github from "@actions/github";
|
||||
import fs from "fs-extra";
|
||||
import { getPackages, Package } from "@manypkg/get-packages";
|
||||
import path from "path";
|
||||
import * as semver from "semver";
|
||||
import {
|
||||
getChangelogEntry,
|
||||
execWithOutput,
|
||||
getChangedPackages,
|
||||
sortTheThings,
|
||||
getVersionsByDirectory,
|
||||
} from "./utils";
|
||||
import * as gitUtils from "./gitUtils";
|
||||
import readChangesetState from "./readChangesetState";
|
||||
|
||||
const createRelease = async (
|
||||
octokit: github.GitHub,
|
||||
{ pkg, tagName }: { pkg: Package; tagName: string }
|
||||
) => {
|
||||
try {
|
||||
let changelogFileName = path.join(pkg.dir, "CHANGELOG.md");
|
||||
|
||||
let changelog = await fs.readFile(changelogFileName, "utf8");
|
||||
|
||||
let changelogEntry = getChangelogEntry(changelog, pkg.packageJson.version);
|
||||
if (!changelogEntry) {
|
||||
// we can find a changelog but not the entry for this version
|
||||
// if this is true, something has probably gone wrong
|
||||
throw new Error(
|
||||
`Could not find changelog entry for ${pkg.packageJson.name}@${pkg.packageJson.version}`
|
||||
);
|
||||
}
|
||||
|
||||
await octokit.repos.createRelease({
|
||||
tag_name: tagName,
|
||||
body: changelogEntry.content,
|
||||
prerelease: pkg.packageJson.version.includes("-"),
|
||||
...github.context.repo,
|
||||
});
|
||||
} catch (err) {
|
||||
// if we can't find a changelog, the user has probably disabled changelogs
|
||||
if (err.code !== "ENOENT") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type PublishOptions = {
|
||||
script: string;
|
||||
githubToken: string;
|
||||
cwd?: string;
|
||||
};
|
||||
|
||||
type PublishedPackage = { name: string; version: string };
|
||||
|
||||
type PublishResult =
|
||||
| {
|
||||
published: true;
|
||||
publishedPackages: PublishedPackage[];
|
||||
}
|
||||
| {
|
||||
published: false;
|
||||
};
|
||||
|
||||
export async function runPublish({
|
||||
script,
|
||||
githubToken,
|
||||
cwd = process.cwd(),
|
||||
}: PublishOptions): Promise<PublishResult> {
|
||||
let branch = github.context.ref.replace("refs/heads/", "");
|
||||
let octokit = new github.GitHub(githubToken);
|
||||
let [publishCommand, ...publishArgs] = script.split(/\s+/);
|
||||
|
||||
let changesetPublishOutput = await execWithOutput(
|
||||
publishCommand,
|
||||
publishArgs,
|
||||
{ cwd }
|
||||
);
|
||||
|
||||
await gitUtils.pullBranch(branch);
|
||||
await gitUtils.push(branch, { includeTags: true });
|
||||
|
||||
let { packages, tool } = await getPackages(cwd);
|
||||
let releasedPackages: Package[] = [];
|
||||
|
||||
if (tool !== "root") {
|
||||
let newTagRegex = /New tag:\s+(@[^/]+\/[^@]+|[^/]+)@([^\s]+)/;
|
||||
let packagesByName = new Map(packages.map((x) => [x.packageJson.name, x]));
|
||||
|
||||
for (let line of changesetPublishOutput.stdout.split("\n")) {
|
||||
let match = line.match(newTagRegex);
|
||||
if (match === null) {
|
||||
continue;
|
||||
}
|
||||
let pkgName = match[1];
|
||||
let pkg = packagesByName.get(pkgName);
|
||||
if (pkg === undefined) {
|
||||
throw new Error(
|
||||
`Package "${pkgName}" not found.` +
|
||||
"This is probably a bug in the action, please open an issue"
|
||||
);
|
||||
}
|
||||
releasedPackages.push(pkg);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
releasedPackages.map((pkg) =>
|
||||
createRelease(octokit, {
|
||||
pkg,
|
||||
tagName: `${pkg.packageJson.name}@${pkg.packageJson.version}`,
|
||||
})
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (packages.length === 0) {
|
||||
throw new Error(
|
||||
`No package found.` +
|
||||
"This is probably a bug in the action, please open an issue"
|
||||
);
|
||||
}
|
||||
const pkg = packages[0];
|
||||
releasedPackages.push(pkg);
|
||||
await createRelease(octokit, {
|
||||
pkg,
|
||||
tagName: `v${pkg.packageJson.version}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (releasedPackages.length) {
|
||||
return {
|
||||
published: true,
|
||||
publishedPackages: releasedPackages.map((pkg) => ({
|
||||
name: pkg.packageJson.name,
|
||||
version: pkg.packageJson.version,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
return { published: false };
|
||||
}
|
||||
|
||||
type VersionOptions = {
|
||||
script?: string;
|
||||
githubToken: string;
|
||||
cwd?: string;
|
||||
prTitle?: string;
|
||||
commitMessage?: string;
|
||||
hasPublishScript?: boolean;
|
||||
};
|
||||
|
||||
export async function runVersion({
|
||||
script,
|
||||
githubToken,
|
||||
cwd = process.cwd(),
|
||||
prTitle = "Version Packages",
|
||||
commitMessage = "Version Packages",
|
||||
hasPublishScript = false,
|
||||
}: VersionOptions) {
|
||||
let repo = `${github.context.repo.owner}/${github.context.repo.repo}`;
|
||||
let branch = github.context.ref.replace("refs/heads/", "");
|
||||
let versionBranch = `changeset-release/${branch}`;
|
||||
let octokit = new github.GitHub(githubToken);
|
||||
let { preState } = await readChangesetState(cwd);
|
||||
|
||||
await gitUtils.switchToMaybeExistingBranch(versionBranch);
|
||||
await gitUtils.reset(github.context.sha);
|
||||
|
||||
let versionsByDirectory = await getVersionsByDirectory(cwd);
|
||||
|
||||
if (script) {
|
||||
let [versionCommand, ...versionArgs] = script.split(/\s+/);
|
||||
await exec(versionCommand, versionArgs, { cwd });
|
||||
} else {
|
||||
// technically
|
||||
let changesetsCliPkgJson = await require(path.join(
|
||||
cwd,
|
||||
"node_modules",
|
||||
"@changesets",
|
||||
"cli",
|
||||
"package.json"
|
||||
));
|
||||
let cmd = semver.lt(changesetsCliPkgJson.version, "2.0.0")
|
||||
? "bump"
|
||||
: "version";
|
||||
await exec("node", ["./node_modules/@changesets/cli/bin.js", cmd], { cwd });
|
||||
}
|
||||
|
||||
let searchQuery = `repo:${repo}+state:open+head:${versionBranch}+base:${branch}`;
|
||||
let searchResultPromise = octokit.search.issuesAndPullRequests({
|
||||
q: searchQuery,
|
||||
});
|
||||
let changedPackages = await getChangedPackages(cwd, versionsByDirectory);
|
||||
|
||||
let prBodyPromise = (async () => {
|
||||
return (
|
||||
`This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and ${
|
||||
hasPublishScript
|
||||
? `the packages will be published to npm automatically`
|
||||
: `publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing)`
|
||||
}. If you're not ready to do a release yet, that's fine, whenever you add more changesets to ${branch}, this PR will be updated.
|
||||
${
|
||||
!!preState
|
||||
? `
|
||||
⚠️⚠️⚠️⚠️⚠️⚠️
|
||||
|
||||
\`${branch}\` is currently in **pre mode** so this branch has prereleases rather than normal releases. If you want to exit prereleases, run \`changeset pre exit\` on \`${branch}\`.
|
||||
|
||||
⚠️⚠️⚠️⚠️⚠️⚠️
|
||||
`
|
||||
: ""
|
||||
}
|
||||
# Releases
|
||||
` +
|
||||
(
|
||||
await Promise.all(
|
||||
changedPackages.map(async (pkg) => {
|
||||
let changelogContents = await fs.readFile(
|
||||
path.join(pkg.dir, "CHANGELOG.md"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
let entry = getChangelogEntry(
|
||||
changelogContents,
|
||||
pkg.packageJson.version
|
||||
);
|
||||
return {
|
||||
highestLevel: entry.highestLevel,
|
||||
private: !!pkg.packageJson.private,
|
||||
content:
|
||||
`## ${pkg.packageJson.name}@${pkg.packageJson.version}\n\n` +
|
||||
entry.content,
|
||||
};
|
||||
})
|
||||
)
|
||||
)
|
||||
.filter((x) => x)
|
||||
.sort(sortTheThings)
|
||||
.map((x) => x.content)
|
||||
.join("\n ")
|
||||
);
|
||||
})();
|
||||
|
||||
const finalPrTitle = `${prTitle}${!!preState ? ` (${preState.tag})` : ""}`;
|
||||
const finalCommitMessage = `${commitMessage}${
|
||||
!!preState ? ` (${preState.tag})` : ""
|
||||
}`;
|
||||
|
||||
await gitUtils.commitAll(finalCommitMessage);
|
||||
await gitUtils.push(versionBranch, { force: true });
|
||||
|
||||
let searchResult = await searchResultPromise;
|
||||
console.log(JSON.stringify(searchResult.data, null, 2));
|
||||
if (searchResult.data.items.length === 0) {
|
||||
console.log("creating pull request");
|
||||
await octokit.pulls.create({
|
||||
base: branch,
|
||||
head: versionBranch,
|
||||
title: finalPrTitle,
|
||||
body: await prBodyPromise,
|
||||
...github.context.repo,
|
||||
});
|
||||
} else {
|
||||
octokit.pulls.update({
|
||||
pull_number: searchResult.data.items[0].number,
|
||||
title: finalPrTitle,
|
||||
body: await prBodyPromise,
|
||||
...github.context.repo,
|
||||
});
|
||||
console.log("pull request found");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { getChangelogEntry, BumpLevels, sortTheThings } from "./utils";
|
||||
|
||||
let changelog = `# @keystone-alpha/email
|
||||
|
||||
## 3.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [19fe6c1b](https://github.com/keystonejs/keystone-5/commit/19fe6c1b):
|
||||
|
||||
Move frontmatter in docs into comments
|
||||
|
||||
## 3.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- [2164a779](https://github.com/keystonejs/keystone-5/commit/2164a779):
|
||||
|
||||
- Replace jade with pug because Jade was renamed to Pug, and \`jade\` package is outdated
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [81dc0be5](https://github.com/keystonejs/keystone-5/commit/81dc0be5):
|
||||
|
||||
- Update dependencies
|
||||
|
||||
## 2.0.0
|
||||
|
||||
- [patch][b69fb9b7](https://github.com/keystonejs/keystone-5/commit/b69fb9b7):
|
||||
|
||||
- Update dev devependencies
|
||||
|
||||
- [major][f97e4ecf](https://github.com/keystonejs/keystone-5/commit/f97e4ecf):
|
||||
|
||||
- Export { emailSender } as the API, rather than a default export
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- [patch][7417ea3a](https://github.com/keystonejs/keystone-5/commit/7417ea3a):
|
||||
|
||||
- Update patch-level dependencies
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- [patch][1f0bc236](https://github.com/keystonejs/keystone-5/commit/1f0bc236):
|
||||
|
||||
- Update the package.json author field to "The Keystone Development Team"
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- [major] 8b6734ae:
|
||||
|
||||
- This is the first release of keystone-alpha (previously voussoir).
|
||||
All packages in the \`@voussoir\` namespace are now available in the \`@keystone-alpha\` namespace, starting at version \`1.0.0\`.
|
||||
To upgrade your project you must update any \`@voussoir/<foo>\` dependencies in \`package.json\` to point to \`@keystone-alpha/<foo>: "^1.0.0"\` and update any \`require\`/\`import\` statements in your code.
|
||||
|
||||
# @voussoir/email
|
||||
|
||||
## 0.0.2
|
||||
|
||||
- [patch] 113e16d4:
|
||||
|
||||
- Remove unused dependencies
|
||||
|
||||
- [patch] 625c1a6d:
|
||||
|
||||
- Update mjml-dependency
|
||||
`;
|
||||
|
||||
test("it works", () => {
|
||||
let entry = getChangelogEntry(changelog, "3.0.0");
|
||||
expect(entry.content).toMatchSnapshot();
|
||||
expect(entry.highestLevel).toBe(BumpLevels.major);
|
||||
});
|
||||
|
||||
test("it works", () => {
|
||||
let entry = getChangelogEntry(changelog, "3.0.1");
|
||||
expect(entry.content).toMatchSnapshot();
|
||||
expect(entry.highestLevel).toBe(BumpLevels.patch);
|
||||
});
|
||||
|
||||
test("it sorts the things right", () => {
|
||||
let things = [
|
||||
{
|
||||
name: "a",
|
||||
highestLevel: BumpLevels.major,
|
||||
private: true,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
highestLevel: BumpLevels.patch,
|
||||
private: false,
|
||||
},
|
||||
{
|
||||
name: "c",
|
||||
highestLevel: BumpLevels.major,
|
||||
private: false,
|
||||
},
|
||||
];
|
||||
expect(things.sort(sortTheThings)).toMatchSnapshot();
|
||||
});
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
import unified from "unified";
|
||||
import remarkParse from "remark-parse";
|
||||
import remarkStringify from "remark-stringify";
|
||||
// @ts-ignore
|
||||
import mdastToString from "mdast-util-to-string";
|
||||
import { exec } from "@actions/exec";
|
||||
import { getPackages, Package } from "@manypkg/get-packages";
|
||||
|
||||
export const BumpLevels = {
|
||||
dep: 0,
|
||||
patch: 1,
|
||||
minor: 2,
|
||||
major: 3,
|
||||
} as const;
|
||||
|
||||
export async function getVersionsByDirectory(cwd: string) {
|
||||
let { packages } = await getPackages(cwd);
|
||||
return new Map(packages.map((x) => [x.dir, x.packageJson.version]));
|
||||
}
|
||||
|
||||
export async function getChangedPackages(
|
||||
cwd: string,
|
||||
previousVersions: Map<string, string>
|
||||
) {
|
||||
let { packages } = await getPackages(cwd);
|
||||
let changedPackages = new Set<Package>();
|
||||
|
||||
for (let pkg of packages) {
|
||||
const previousVersion = previousVersions.get(pkg.dir);
|
||||
if (previousVersion !== pkg.packageJson.version) {
|
||||
changedPackages.add(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
return [...changedPackages];
|
||||
}
|
||||
|
||||
export function getChangelogEntry(changelog: string, version: string) {
|
||||
let ast = unified().use(remarkParse).parse(changelog);
|
||||
|
||||
let highestLevel: number = BumpLevels.dep;
|
||||
|
||||
let nodes = ast.children as Array<any>;
|
||||
let headingStartInfo:
|
||||
| {
|
||||
index: number;
|
||||
depth: number;
|
||||
}
|
||||
| undefined;
|
||||
let endIndex: number | undefined;
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let node = nodes[i];
|
||||
if (node.type === "heading") {
|
||||
let stringified: string = mdastToString(node);
|
||||
let match = stringified.toLowerCase().match(/(major|minor|patch)/);
|
||||
if (match !== null) {
|
||||
let level = BumpLevels[match[0] as "major" | "minor" | "patch"];
|
||||
highestLevel = Math.max(level, highestLevel);
|
||||
}
|
||||
if (headingStartInfo === undefined && stringified === version) {
|
||||
headingStartInfo = {
|
||||
index: i,
|
||||
depth: node.depth,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
endIndex === undefined &&
|
||||
headingStartInfo !== undefined &&
|
||||
headingStartInfo.depth === node.depth
|
||||
) {
|
||||
endIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (headingStartInfo) {
|
||||
ast.children = (ast.children as any).slice(
|
||||
headingStartInfo.index + 1,
|
||||
endIndex
|
||||
);
|
||||
}
|
||||
return {
|
||||
content: unified().use(remarkStringify).stringify(ast),
|
||||
highestLevel: highestLevel,
|
||||
};
|
||||
}
|
||||
|
||||
export async function execWithOutput(
|
||||
command: string,
|
||||
args?: string[],
|
||||
options?: { ignoreReturnCode?: boolean; cwd?: string }
|
||||
) {
|
||||
let myOutput = "";
|
||||
let myError = "";
|
||||
|
||||
return {
|
||||
code: await exec(command, args, {
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
myOutput += data.toString();
|
||||
},
|
||||
stderr: (data: Buffer) => {
|
||||
myError += data.toString();
|
||||
},
|
||||
},
|
||||
|
||||
...options,
|
||||
}),
|
||||
stdout: myOutput,
|
||||
stderr: myError,
|
||||
};
|
||||
}
|
||||
|
||||
export function sortTheThings(
|
||||
a: { private: boolean; highestLevel: number },
|
||||
b: { private: boolean; highestLevel: number }
|
||||
) {
|
||||
if (a.private === b.private) {
|
||||
return b.highestLevel - a.highestLevel;
|
||||
}
|
||||
if (a.private) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user