Skip to content

Commit

Permalink
refactor(build): restructure build helpers to use classes MONGOSH-2007 (
Browse files Browse the repository at this point in the history
  • Loading branch information
gagik authored Feb 26, 2025
1 parent bf1255f commit 78ad910
Show file tree
Hide file tree
Showing 18 changed files with 989 additions and 1,183 deletions.
77 changes: 41 additions & 36 deletions packages/build/src/homebrew/publish-to-homebrew.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import chai, { expect } from 'chai';
import sinon from 'sinon';
import type { GithubRepo } from '@mongodb-js/devtools-github-repo';
import { publishToHomebrew } from './publish-to-homebrew';
import type { HomebrewPublisherConfig } from './publish-to-homebrew';
import { HomebrewPublisher } from './publish-to-homebrew';

chai.use(require('sinon-chai'));

describe('Homebrew publish-to-homebrew', function () {
describe('HomebrewPublisher', function () {
let homebrewCore: GithubRepo;
let homebrewCoreFork: GithubRepo;
let createPullRequest: sinon.SinonStub;
let httpsSha256: sinon.SinonStub;
let generateFormula: sinon.SinonStub;
let updateHomebrewFork: sinon.SinonStub;

beforeEach(function () {
let testPublisher: HomebrewPublisher;

const setupHomebrewPublisher = (
config: Omit<HomebrewPublisherConfig, 'homebrewCore' | 'homebrewCoreFork'>
) => {
createPullRequest = sinon.stub();
httpsSha256 = sinon.stub();
generateFormula = sinon.stub();
updateHomebrewFork = sinon.stub();

homebrewCore = {
repo: {
Expand All @@ -32,9 +34,33 @@ describe('Homebrew publish-to-homebrew', function () {
repo: 'homebrew-core',
},
} as unknown as GithubRepo;

testPublisher = new HomebrewPublisher({
...config,
homebrewCore,
homebrewCoreFork,
});

httpsSha256 = sinon.stub(testPublisher, 'httpsSha256');
generateFormula = sinon.stub(testPublisher, 'generateFormula');
updateHomebrewFork = sinon.stub(testPublisher, 'updateHomebrewFork');
};

beforeEach(function () {
setupHomebrewPublisher({
packageVersion: '1.0.0',
githubReleaseLink: 'githubRelease',
isDryRun: false,
});
});

it('creates and merges a PR on update and cleans up', async function () {
setupHomebrewPublisher({
packageVersion: '1.0.0',
githubReleaseLink: 'githubRelease',
isDryRun: false,
});

httpsSha256
.rejects()
.withArgs(
Expand Down Expand Up @@ -69,16 +95,7 @@ describe('Homebrew publish-to-homebrew', function () {
)
.resolves({ prNumber: 42, url: 'url' });

await publishToHomebrew(
homebrewCore,
homebrewCoreFork,
'1.0.0',
'githubRelease',
false,
httpsSha256,
generateFormula,
updateHomebrewFork
);
await testPublisher.publish();

expect(httpsSha256).to.have.been.called;
expect(generateFormula).to.have.been.called;
Expand All @@ -87,6 +104,12 @@ describe('Homebrew publish-to-homebrew', function () {
});

it('does not try to push/merge when there is no formula update', async function () {
setupHomebrewPublisher({
packageVersion: '1.0.0',
githubReleaseLink: 'githubRelease',
isDryRun: false,
});

httpsSha256
.rejects()
.withArgs(
Expand All @@ -111,16 +134,7 @@ describe('Homebrew publish-to-homebrew', function () {
})
.resolves(undefined);

await publishToHomebrew(
homebrewCore,
homebrewCoreFork,
'1.0.0',
'githubRelease',
false,
httpsSha256,
generateFormula,
updateHomebrewFork
);
await testPublisher.publish();

expect(httpsSha256).to.have.been.called;
expect(generateFormula).to.have.been.called;
Expand Down Expand Up @@ -163,16 +177,7 @@ describe('Homebrew publish-to-homebrew', function () {
)
.resolves({ prNumber: 42, url: 'url' });

await publishToHomebrew(
homebrewCore,
homebrewCoreFork,
'1.0.0',
'githubRelease',
false,
httpsSha256,
generateFormula,
updateHomebrewFork
);
await testPublisher.publish();

expect(httpsSha256).to.have.been.called;
expect(generateFormula).to.have.been.called;
Expand Down
134 changes: 81 additions & 53 deletions packages/build/src/homebrew/publish-to-homebrew.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,88 @@
import type { GithubRepo } from '@mongodb-js/devtools-github-repo';
import { generateUpdatedFormula } from './generate-formula';
import { updateHomebrewFork } from './update-homebrew-fork';
import { httpsSha256 } from './utils';

export async function publishToHomebrew(
homebrewCore: GithubRepo,
homebrewCoreFork: GithubRepo,
packageVersion: string,
githubReleaseLink: string,
isDryRun: boolean,
httpsSha256Fn = httpsSha256,
generateFormulaFn = generateUpdatedFormula,
updateHomebrewForkFn = updateHomebrewFork
): Promise<void> {
const cliReplPackageUrl = `https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-${packageVersion}.tgz`;
const packageSha = isDryRun
? `dryRun-fakesha256-${Date.now()}`
: await httpsSha256Fn(cliReplPackageUrl);

const homebrewFormula = await generateFormulaFn(
{ version: packageVersion, sha: packageSha },
homebrewCore,
isDryRun
);
if (!homebrewFormula) {
console.warn('There are no changes to the homebrew formula');
return;
}
import { generateUpdatedFormula as generateUpdatedFormulaFn } from './generate-formula';
import { updateHomebrewFork as updateHomebrewForkFn } from './update-homebrew-fork';
import { httpsSha256 as httpsSha256Fn } from './utils';

export type HomebrewPublisherConfig = {
homebrewCore: GithubRepo;
homebrewCoreFork: GithubRepo;
packageVersion: string;
githubReleaseLink: string;
isDryRun?: boolean;
};

const forkBranch = await updateHomebrewForkFn({
packageVersion,
packageSha,
homebrewFormula,
homebrewCore,
homebrewCoreFork,
isDryRun,
});
if (!forkBranch) {
console.warn('There are no changes to the homebrew formula');
return;
export class HomebrewPublisher {
readonly httpsSha256: typeof httpsSha256Fn;
readonly generateFormula: typeof generateUpdatedFormulaFn;
readonly updateHomebrewFork: typeof updateHomebrewForkFn;

constructor(
public config: HomebrewPublisherConfig,
{
httpsSha256 = httpsSha256Fn,
generateFormula = generateUpdatedFormulaFn,
updateHomebrewFork = updateHomebrewForkFn,
} = {}
) {
this.httpsSha256 = httpsSha256;
this.generateFormula = generateFormula;
this.updateHomebrewFork = updateHomebrewFork;
}

const description = `This PR was created automatically and bumps \`mongosh\` to the latest published version \`${packageVersion}\`.\n\nFor additional details see ${githubReleaseLink}.`;
async publish(): Promise<void> {
const {
isDryRun,
homebrewCore,
packageVersion,
homebrewCoreFork,
githubReleaseLink,
} = this.config;

const cliReplPackageUrl = `https://registry.npmjs.org/@mongosh/cli-repl/-/cli-repl-${packageVersion}.tgz`;
const packageSha = isDryRun
? `dryRun-fakesha256-${Date.now()}`
: await this.httpsSha256(cliReplPackageUrl);

const homebrewFormula = await this.generateFormula(
{ version: packageVersion, sha: packageSha },
homebrewCore,
isDryRun || false
);
if (!homebrewFormula) {
console.warn('There are no changes to the homebrew formula');
return;
}

const forkBranch = await this.updateHomebrewFork({
packageVersion,
packageSha,
homebrewFormula,
homebrewCore,
homebrewCoreFork,
isDryRun: isDryRun || false,
});
if (!forkBranch) {
console.warn('There are no changes to the homebrew formula');
return;
}

const description = `This PR was created automatically and bumps \`mongosh\` to the latest published version \`${packageVersion}\`.\n\nFor additional details see ${githubReleaseLink}.`;

if (isDryRun) {
await homebrewCoreFork.deleteBranch(forkBranch);
console.warn('Deleted branch instead of creating homebrew PR');
return;
if (isDryRun) {
await homebrewCoreFork.deleteBranch(forkBranch);
console.warn('Deleted branch instead of creating homebrew PR');
return;
}
const pr = await homebrewCore.createPullRequest(
`mongosh ${packageVersion}`,
description,
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${homebrewCoreFork.repo.owner}:${forkBranch}`,
'master'
);
console.info(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Created PR #${pr.prNumber} in ${homebrewCore.repo.owner}/${homebrewCore.repo.repo}: ${pr.url}`
);
}
const pr = await homebrewCore.createPullRequest(
`mongosh ${packageVersion}`,
description,
`${homebrewCoreFork.repo.owner}:${forkBranch}`,
'master'
);
console.info(
`Created PR #${pr.prNumber} in ${homebrewCore.repo.owner}/${homebrewCore.repo.repo}: ${pr.url}`
);
}
44 changes: 25 additions & 19 deletions packages/build/src/npm-packages/bump.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { expect } from 'chai';
import type { SinonStub } from 'sinon';
import sinon from 'sinon';
import {
bumpMongoshReleasePackages,
updateShellApiMongoshVersion,
} from './bump';
import { PackageBumper } from './bump';
import { promises as fs } from 'fs';
import path from 'path';
import { PROJECT_ROOT } from './constants';

describe('npm-packages bump', function () {
describe('PackageBumper', function () {
let fsWriteFile: SinonStub;
const shellApiSrc = path.join(
PROJECT_ROOT,
Expand All @@ -19,9 +16,22 @@ describe('npm-packages bump', function () {
'mongosh-version.ts'
);

let testBumper: PackageBumper;
let getPackagesInTopologicalOrder: sinon.SinonStub;

beforeEach(function () {
fsWriteFile = sinon.stub(fs, 'writeFile');
fsWriteFile.resolves();

const spawnSync = sinon.stub();
spawnSync.resolves();

getPackagesInTopologicalOrder = sinon.stub();

testBumper = new PackageBumper({
spawnSync,
getPackagesInTopologicalOrder,
});
});

afterEach(function () {
Expand All @@ -30,15 +40,13 @@ describe('npm-packages bump', function () {

describe('bumpMongoshReleasePackages', function () {
let fsReadFile: SinonStub;
let getPackagesInTopologicalOrder: sinon.SinonStub;
beforeEach(function () {
fsReadFile = sinon.stub(fs, 'readFile');
getPackagesInTopologicalOrder = sinon.stub();
});

it('warns and does not run if version is not set', async function () {
const consoleWarnSpy = sinon.spy(console, 'warn');
await bumpMongoshReleasePackages('');
await testBumper.bumpMongoshReleasePackages('');
expect(consoleWarnSpy).calledOnceWith(
'mongosh: Release version not specified. Skipping mongosh bump.'
);
Expand All @@ -54,10 +62,6 @@ describe('npm-packages bump', function () {
'packages',
'autocomplete'
);
getPackagesInTopologicalOrder.resolves([
{ name: 'mongosh', location: mongoshPath },
{ name: '@mongosh/autocomplete', location: autocompletePath },
]);

const rootProjectJson = path.join(PROJECT_ROOT, 'package.json');
const mongoshProjectJson = path.join(mongoshPath, 'package.json');
Expand Down Expand Up @@ -106,12 +110,14 @@ describe('npm-packages bump', function () {
fsReadFile.withArgs(file, 'utf8').resolves(JSON.stringify(json));
}

const updateShellApiMongoshVersion = sinon.stub();
await bumpMongoshReleasePackages(
'9.9.9',
getPackagesInTopologicalOrder,
updateShellApiMongoshVersion
);
getPackagesInTopologicalOrder.resolves([
{ name: 'mongosh', location: mongoshPath },
{ name: '@mongosh/autocomplete', location: autocompletePath },
]);

sinon.stub(testBumper, 'updateShellApiMongoshVersion').resolves();

await testBumper.bumpMongoshReleasePackages('9.9.9');
expect(fsWriteFile).callCount(3);

expect(
Expand Down Expand Up @@ -168,7 +174,7 @@ describe('npm-packages bump', function () {
export const MONGOSH_VERSION = '2.3.8';`);

const newVersion = '3.0.0';
await updateShellApiMongoshVersion(newVersion);
await testBumper.updateShellApiMongoshVersion(newVersion);

expect(fsWriteFile).calledWith(
shellApiSrc,
Expand Down
Loading

0 comments on commit 78ad910

Please sign in to comment.