Skip to content

Commit

Permalink
Merge pull request #38 from embroider-build/publish-branch
Browse files Browse the repository at this point in the history
add --publish-branch if passed to release-plan publish
  • Loading branch information
mansona authored Dec 13, 2023
2 parents f93aadc + 73672df commit 8160913
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 33 deletions.
5 changes: 5 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ yargs(process.argv.slice(2))
type: 'boolean',
description:
'Run through the release, but log to stdout instead of tagging/pushing/publishing',
})
.option('publish-branch', {
type: 'string',
description:
'(pnpm) optionally pass on the --publish-branch if you need to publish from a branch other than main|master',
}),
async function (opts) {
await publish(opts);
Expand Down
73 changes: 73 additions & 0 deletions src/publish.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { describe, it, expect } from 'vitest';
import { npmPublish, IssueReporter } from './publish.js';
import { Solution } from './plan.js';

// we aren't currently using this so we can just ignore for now
const reporter = new IssueReporter();

describe('publish', function () {
describe('npmPublish', function () {
it('adds the correct args with no options', async function () {
const thingy = await npmPublish(
new Map([['thingy', { oldVersion: '3' }]]) as Solution,
reporter,
{},
'face',
);

expect(thingy).toMatchInlineSnapshot(`
{
"args": [
"publish",
"--access=public",
],
"released": Map {},
}
`);
});

it('adds otp if passed by options', async function () {
const thingy = await npmPublish(
new Map([['thingy', { oldVersion: '3' }]]) as Solution,
reporter,
{
otp: '12345',
},
'face',
);

expect(thingy).toMatchInlineSnapshot(`
{
"args": [
"publish",
"--access=public",
"--otp=12345",
],
"released": Map {},
}
`);
});

it('adds publish-branch if passed by options', async function () {
const thingy = await npmPublish(
new Map([['thingy', { oldVersion: '3' }]]) as Solution,
reporter,
{
publishBranch: 'best-branch',
},
'face',
);

expect(thingy).toMatchInlineSnapshot(`
{
"args": [
"publish",
"--access=public",
"--publish-branch=best-branch",
],
"released": Map {},
}
`);
});
});
});
84 changes: 51 additions & 33 deletions src/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import fsExtra from 'fs-extra';

const { existsSync } = fsExtra;

type PublishOptions = {
skipRepoSafetyCheck?: boolean;
dryRun?: boolean;
otp?: string;
publishBranch?: string;
};

async function hasCleanRepo(): Promise<boolean> {
const result = await execa('git', ['status', '--porcelain=v1']);
return result.stdout.length === 0;
Expand All @@ -27,7 +34,7 @@ function success(message: string) {
process.stdout.write(`\n 🎉 ${message} 🎉\n`);
}

class IssueReporter {
export class IssueReporter {
hadIssues = false;
reportFailure(message: string): void {
this.hadIssues = true;
Expand All @@ -50,7 +57,7 @@ async function doesTagExist(tag: string) {
async function makeTags(
solution: Solution,
reporter: IssueReporter,
dryRun: boolean,
options: PublishOptions,
): Promise<void> {
for (const [pkgName, entry] of solution) {
if (!entry.impact) {
Expand All @@ -67,7 +74,7 @@ async function makeTags(
return;
}

if (dryRun) {
if (options.dryRun) {
info(`--dryRun active. Skipping \`git tag ${tag}\``);
continue;
}
Expand All @@ -84,8 +91,8 @@ async function makeTags(
}
}

async function pushTags(reporter: IssueReporter, dryRun: boolean) {
if (dryRun) {
async function pushTags(reporter: IssueReporter, options: PublishOptions) {
if (options.dryRun) {
info(`--dryRun active. Skipping \`git push --tags\``);
return;
}
Expand All @@ -103,7 +110,7 @@ function chooseRepresentativeTag(solution: Solution): string {
return tagFor(pkgName, entry);
}
}
process.stderr.write('Found no releaseable packages in the plan');
process.stderr.write('Found no releasable packages in the plan');
process.exit(-1);
}

Expand Down Expand Up @@ -159,7 +166,7 @@ async function createGithubRelease(
description: string,
tagName: string,
reporter: IssueReporter,
dryRun: boolean,
options: PublishOptions,
): Promise<void> {
try {
const preExisting = await doesReleaseExist(octokit, tagName, reporter);
Expand All @@ -169,7 +176,7 @@ async function createGithubRelease(
return;
}

if (dryRun) {
if (options.dryRun) {
info(
`--dryRun active. Skipping creating a Release on GitHub for ${tagName}`,
);
Expand Down Expand Up @@ -211,13 +218,29 @@ async function doesVersionExist(
}
}

async function npmPublish(
/**
* Call npm publish or pnpm publish on each of the packages in a plan
*
* @returns Promise<T> return value only used for testing
*/
export async function npmPublish(
solution: Solution,
reporter: IssueReporter,
dryRun: boolean,
options: PublishOptions,
packageManager: string,
otp?: string,
): Promise<void> {
): Promise<{ args: string[]; released: Map<string, string> }> {
const args = ['publish', '--access=public'];

if (options.otp) {
args.push(`--otp=${options.otp}`);
}

if (options.publishBranch) {
args.push(`--publish-branch=${options.publishBranch}`);
}

const released = new Map();

for (const [pkgName, entry] of solution) {
if (!entry.impact) {
continue;
Expand All @@ -231,25 +254,21 @@ async function npmPublish(

if (preExisting) {
info(`${pkgName} has already been publish @ version ${entry.newVersion}`);
return;
continue;
}

if (dryRun) {
if (options.dryRun) {
info(
`--dryRun active. Skipping \`${packageManager} publish --access=public${
otp ? ' --otp=*redacted*' : ''
options.otp ? ' --otp=*redacted*' : ''
}\` for ${pkgName}, which would publish version ${entry.newVersion}`,
);

released.set(pkgName, entry.newVersion);
continue;
}

try {
const args = ['publish', '--access=public'];

if (otp) {
args.push(`--otp=${otp}`);
}

await execa(packageManager, args, {
cwd: dirname(entry.pkgJSONPath),
stderr: 'inherit',
Expand All @@ -261,6 +280,11 @@ async function npmPublish(
);
}
}

return {
args,
released,
};
}

function packageManager(): string {
Expand All @@ -271,13 +295,7 @@ function packageManager(): string {
return 'npm';
}

export async function publish(opts: {
skipRepoSafetyCheck?: boolean;
dryRun?: boolean;
otp?: string;
}) {
const dryRun = opts.dryRun ?? false;

export async function publish(opts: PublishOptions) {
if (!opts.skipRepoSafetyCheck) {
if (!(await hasCleanRepo())) {
process.stderr.write(`You have uncommitted changes.
Expand All @@ -303,22 +321,22 @@ To publish a release you should start from a clean repo. Run "npx release-plan p
// the end.
const reporter = new IssueReporter();

await makeTags(solution, reporter, dryRun);
await npmPublish(solution, reporter, dryRun, packageManager(), opts.otp);
await pushTags(reporter, dryRun);
await makeTags(solution, reporter, opts);
await npmPublish(solution, reporter, opts, packageManager());
await pushTags(reporter, opts);
await createGithubRelease(
octokit,
description,
representativeTag,
reporter,
dryRun,
opts,
);

if (reporter.hadIssues) {
process.stderr.write(`\nSome parts of the release were unsuccessful.\n`);
process.exit(-1);
} else {
if (dryRun) {
if (opts.dryRun) {
success(`--dryRun active. Would have successfully published release!`);
return;
}
Expand Down

0 comments on commit 8160913

Please sign in to comment.