Skip to content

Commit 144b368

Browse files
authored
chore(build): create and upload .sig files for download artifacts MONGOSH-1074 (#1169)
1 parent 29170b1 commit 144b368

File tree

7 files changed

+82
-42
lines changed

7 files changed

+82
-42
lines changed

packages/build/src/packaging/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ export {
55
PackageFile,
66
PackageInformation
77
} from './package';
8+
9+
export {
10+
notarizeArtifact
11+
} from './notary-service';

packages/build/src/packaging/msi-sign.spec.ts renamed to packages/build/src/packaging/notary-service.spec.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
11
import { expect } from 'chai';
2-
import os from 'os';
32
import path from 'path';
43
import sinon from 'sinon';
5-
import { notarizeMsi, NotarizeMsiOptions } from './msi-sign';
4+
import { notarizeArtifact, NotarizeOptions } from './notary-service';
65

7-
describe('packaging msi-sign', () => {
6+
describe('packaging artifact signing', () => {
87
context('with invalid options', () => {
98
it('fails when file is missing', async() => {
10-
const e = await notarizeMsi('', {} as any).catch(e => e);
9+
const e = await notarizeArtifact('', {} as any).catch(e => e);
1110
expect(e).to.not.be.undefined;
1211
expect(e.message).to.match(/missing file/);
1312
});
1413
it('fails when signingKeyName is missing', async() => {
15-
const e = await notarizeMsi('a file', {} as any).catch(e => e);
14+
const e = await notarizeArtifact('a file', {} as any).catch(e => e);
1615
expect(e).to.not.be.undefined;
1716
expect(e.message).to.match(/missing signing key name/);
1817
});
1918
it('fails when authToken is missing', async() => {
20-
const e = await notarizeMsi('a file', {
19+
const e = await notarizeArtifact('a file', {
2120
signingKeyName: 'keyName'
2221
} as any).catch(e => e);
2322
expect(e).to.not.be.undefined;
2423
expect(e.message).to.match(/missing auth token/);
2524
});
2625
it('fails when signingComment is missing', async() => {
27-
const e = await notarizeMsi('a file', {
26+
const e = await notarizeArtifact('a file', {
2827
signingKeyName: 'keyName',
2928
authToken: 'token'
3029
} as any).catch(e => e);
@@ -35,7 +34,7 @@ describe('packaging msi-sign', () => {
3534

3635
context('with correct options', () => {
3736
let spawnSync: sinon.SinonStub;
38-
const signingOptions: NotarizeMsiOptions = {
37+
const signingOptions: NotarizeOptions = {
3938
signingKeyName: 'keyName',
4039
authToken: 'authToken',
4140
signingComment: 'A Comment'
@@ -46,18 +45,25 @@ describe('packaging msi-sign', () => {
4645
});
4746

4847
it('runs notary client', async() => {
49-
await notarizeMsi(
48+
await notarizeArtifact(
5049
__filename,
5150
signingOptions,
5251
spawnSync
5352
);
5453

54+
const authTokenFile = spawnSync
55+
.getCall(0)
56+
.args[1]
57+
.find((arg: string) => arg.includes('notary-mongosh-token'));
58+
5559
expect(spawnSync).to.have.been.calledWith(
5660
'python',
5761
[
58-
'C:\\cygwin\\usr\\local\\bin\\notary-client.py',
62+
process.platform === 'win32' ?
63+
'C:\\cygwin\\usr\\local\\bin\\notary-client.py' :
64+
'/usr/local/bin/notary-client.py',
5965
'--key-name', 'keyName',
60-
'--auth-token-file', path.join(os.homedir(), '.notary-mongosh-token.tmp'),
66+
'--auth-token-file', authTokenFile,
6167
'--comment', 'A Comment',
6268
'--notary-url', 'http://notary-service.build.10gen.cc:5000/',
6369
'--outputs', 'sig',

packages/build/src/packaging/msi-sign.ts renamed to packages/build/src/packaging/notary-service.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import os from 'os';
33
import path from 'path';
44
import { spawnSync as spawnSyncFn } from '../helpers';
55

6-
const DEFAULT_OPTIONS: Partial<NotarizeMsiOptions> = {
6+
const DEFAULT_OPTIONS: Partial<NotarizeOptions> = {
77
serverUrl: 'http://notary-service.build.10gen.cc:5000/',
8-
clientPath: 'C:\\cygwin\\usr\\local\\bin\\notary-client.py',
8+
clientPath: process.platform === 'win32' ?
9+
'C:\\cygwin\\usr\\local\\bin\\notary-client.py' :
10+
'/usr/local/bin/notary-client.py',
911
pythonExecutable: 'python'
1012
};
1113

12-
export interface NotarizeMsiOptions {
14+
export interface NotarizeOptions {
1315
signingKeyName: string;
1416
authToken: string;
1517
signingComment: string;
@@ -18,19 +20,20 @@ export interface NotarizeMsiOptions {
1820
pythonExecutable?: string;
1921
}
2022

21-
export async function notarizeMsi(
23+
export async function notarizeArtifact(
2224
file: string,
23-
opts: NotarizeMsiOptions,
25+
opts: NotarizeOptions,
2426
spawnSync: typeof spawnSyncFn = spawnSyncFn
2527
): Promise<void> {
2628
if (!file) {
27-
throw new Error('notarize MSI: missing file');
29+
throw new Error('notarize artifact: missing file');
2830
}
2931
const options = validateOptions(opts);
3032

31-
const authTokenFile = path.join(os.homedir(), '.notary-mongosh-token.tmp');
33+
const authTokenFile = path.join(os.homedir(), `.notary-mongosh-token.${Date.now()}.tmp`);
3234
await fs.writeFile(authTokenFile, options.authToken, {
33-
encoding: 'utf8'
35+
encoding: 'utf8',
36+
mode: 0o600
3437
});
3538

3639
try {
@@ -56,19 +59,19 @@ export async function notarizeMsi(
5659
}
5760
}
5861

59-
function validateOptions(options: NotarizeMsiOptions): Required<NotarizeMsiOptions> {
62+
function validateOptions(options: NotarizeOptions): Required<NotarizeOptions> {
6063
const opts = {
6164
...DEFAULT_OPTIONS,
6265
...options
6366
};
6467
if (!opts.signingKeyName) {
65-
throw new Error('notarize MSI: missing signing key name');
68+
throw new Error('notarize artifact: missing signing key name');
6669
}
6770
if (!opts.authToken) {
68-
throw new Error('notarize MSI: missing auth token');
71+
throw new Error('notarize artifact: missing auth token');
6972
}
7073
if (!opts.signingComment) {
71-
throw new Error('notarize MSI: missing signing comment');
74+
throw new Error('notarize artifact: missing signing comment');
7275
}
73-
return opts as Required<NotarizeMsiOptions>;
76+
return opts as Required<NotarizeOptions>;
7477
}

packages/build/src/packaging/run-package.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Config, Platform, validateBuildVariant } from '../config';
55
import { downloadMongocrypt } from './download-mongocryptd';
66
import { downloadManpage } from './download-manpage';
77
import { macOSSignAndNotarize } from './macos-sign';
8-
import { notarizeMsi } from './msi-sign';
8+
import { notarizeArtifact } from './notary-service';
99
import { createPackage, PackageFile } from './package';
1010

1111
export async function runPackage(
@@ -60,7 +60,7 @@ export async function runPackage(
6060
const packaged = await runCreatePackage();
6161

6262
if (distributionBuildVariant === 'win32msi-x64') {
63-
await notarizeMsi(
63+
await notarizeArtifact(
6464
packaged.path,
6565
{
6666
signingKeyName: config.notarySigningKeyName || '',

packages/build/src/run-draft.spec.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import sinon from 'ts-sinon';
33
import { ALL_BUILD_VARIANTS, Config } from './config';
44
import { uploadArtifactToDownloadCenter as uploadArtifactToDownloadCenterFn } from './download-center';
55
import { downloadArtifactFromEvergreen as downloadArtifactFromEvergreenFn } from './evergreen';
6+
import { notarizeArtifact as notarizeArtifactFn } from './packaging';
67
import { generateChangelog as generateChangelogFn } from './git';
78
import { GithubRepo } from '@mongodb-js/devtools-github-repo';
89
import { ensureGithubReleaseExistsAndUpdateChangelogFn, runDraft } from './run-draft';
@@ -19,12 +20,14 @@ describe('draft', () => {
1920
let githubRepo: GithubRepo;
2021
let uploadArtifactToDownloadCenter: typeof uploadArtifactToDownloadCenterFn;
2122
let downloadArtifactFromEvergreen: typeof downloadArtifactFromEvergreenFn;
23+
let notarizeArtifact: typeof notarizeArtifactFn;
2224

2325
beforeEach(() => {
2426
config = { ...dummyConfig };
2527

2628
uploadArtifactToDownloadCenter = sinon.spy();
2729
downloadArtifactFromEvergreen = sinon.spy();
30+
notarizeArtifact = sinon.spy();
2831
});
2932

3033
describe('runDraft', () => {
@@ -49,7 +52,8 @@ describe('draft', () => {
4952
githubRepo,
5053
uploadArtifactToDownloadCenter,
5154
downloadArtifactFromEvergreen,
52-
ensureGithubReleaseExistsAndUpdateChangelog
55+
ensureGithubReleaseExistsAndUpdateChangelog,
56+
notarizeArtifact
5357
);
5458
});
5559

@@ -63,12 +67,16 @@ describe('draft', () => {
6367
expect(downloadArtifactFromEvergreen).to.have.been.callCount(ALL_BUILD_VARIANTS.length);
6468
});
6569

70+
it('asks the notary service to sign files', () => {
71+
expect(notarizeArtifact).to.have.been.callCount(ALL_BUILD_VARIANTS.length);
72+
});
73+
6674
it('uploads artifacts to download center', () => {
67-
expect(uploadArtifactToDownloadCenter).to.have.been.callCount(ALL_BUILD_VARIANTS.length);
75+
expect(uploadArtifactToDownloadCenter).to.have.been.callCount(ALL_BUILD_VARIANTS.length * 2);
6876
});
6977

7078
it('uploads the artifacts to the github release', () => {
71-
expect(uploadReleaseAsset).to.have.been.callCount(ALL_BUILD_VARIANTS.length);
79+
expect(uploadReleaseAsset).to.have.been.callCount(ALL_BUILD_VARIANTS.length * 2);
7280
});
7381
});
7482

@@ -83,7 +91,8 @@ describe('draft', () => {
8391
githubRepo,
8492
uploadArtifactToDownloadCenter,
8593
downloadArtifactFromEvergreen,
86-
ensureGithubReleaseExistsAndUpdateChangelog
94+
ensureGithubReleaseExistsAndUpdateChangelog,
95+
notarizeArtifact
8796
);
8897
expect(ensureGithubReleaseExistsAndUpdateChangelog).to.not.have.been.called;
8998
expect(downloadArtifactFromEvergreen).to.not.have.been.called;
@@ -105,14 +114,16 @@ describe('draft', () => {
105114
githubRepo,
106115
uploadArtifactToDownloadCenter,
107116
downloadArtifactFromEvergreen,
108-
ensureGithubReleaseExistsAndUpdateChangelog
117+
ensureGithubReleaseExistsAndUpdateChangelog,
118+
notarizeArtifact
109119
);
110120
} catch (e) {
111121
expect(e.message).to.contain('Missing package information from config');
112122
expect(ensureGithubReleaseExistsAndUpdateChangelog).to.not.have.been.called;
113123
expect(downloadArtifactFromEvergreen).to.not.have.been.called;
114124
expect(uploadArtifactToDownloadCenter).to.not.have.been.called;
115125
expect(uploadReleaseAsset).to.not.have.been.called;
126+
expect(notarizeArtifact).to.not.have.been.called;
116127
return;
117128
}
118129
expect.fail('Expected error');

packages/build/src/run-draft.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from 'path';
33
import { ALL_BUILD_VARIANTS, Config, getReleaseVersionFromTag } from './config';
44
import { uploadArtifactToDownloadCenter as uploadArtifactToDownloadCenterFn } from './download-center';
55
import { downloadArtifactFromEvergreen as downloadArtifactFromEvergreenFn } from './evergreen';
6+
import { notarizeArtifact as notarizeArtifactFn } from './packaging';
67
import { generateChangelog as generateChangelogFn } from './git';
78
import { GithubRepo } from '@mongodb-js/devtools-github-repo';
89
import { getPackageFile } from './packaging';
@@ -12,7 +13,8 @@ export async function runDraft(
1213
githubRepo: GithubRepo,
1314
uploadToDownloadCenter: typeof uploadArtifactToDownloadCenterFn = uploadArtifactToDownloadCenterFn,
1415
downloadArtifactFromEvergreen: typeof downloadArtifactFromEvergreenFn = downloadArtifactFromEvergreenFn,
15-
ensureGithubReleaseExistsAndUpdateChangelog: typeof ensureGithubReleaseExistsAndUpdateChangelogFn = ensureGithubReleaseExistsAndUpdateChangelogFn
16+
ensureGithubReleaseExistsAndUpdateChangelog: typeof ensureGithubReleaseExistsAndUpdateChangelogFn = ensureGithubReleaseExistsAndUpdateChangelogFn,
17+
notarizeArtifact: typeof notarizeArtifactFn = notarizeArtifactFn
1618
): Promise<void> {
1719
if (!config.triggeringGitTag || !getReleaseVersionFromTag(config.triggeringGitTag)) {
1820
console.error('mongosh: skipping draft as not triggered by a git tag that matches a draft/release tag');
@@ -42,19 +44,31 @@ export async function runDraft(
4244
tmpDir
4345
);
4446

45-
await uploadToDownloadCenter(
46-
downloadedArtifact,
47-
config.downloadCenterAwsKey as string,
48-
config.downloadCenterAwsSecret as string
49-
);
50-
51-
await githubRepo.uploadReleaseAsset(
52-
githubReleaseTag,
47+
await notarizeArtifact(
48+
tarballFile.path,
5349
{
54-
path: downloadedArtifact,
55-
contentType: tarballFile.contentType
50+
signingKeyName: config.notarySigningKeyName || '',
51+
authToken: config.notaryAuthToken || '',
52+
signingComment: 'Evergreen Automatic Signing (mongosh)'
5653
}
5754
);
55+
const signatureFile = tarballFile.path + '.sig';
56+
57+
await Promise.all([
58+
[ downloadedArtifact, tarballFile.contentType ],
59+
[ signatureFile, 'application/pgp-signature' ]
60+
].flatMap(([ path, contentType ]) => [
61+
uploadToDownloadCenter(
62+
path,
63+
config.downloadCenterAwsKey as string,
64+
config.downloadCenterAwsSecret as string
65+
),
66+
67+
githubRepo.uploadReleaseAsset(
68+
githubReleaseTag,
69+
{ path, contentType }
70+
)
71+
]));
5872
}
5973
}
6074

packages/build/test/helpers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ export const dummyConfig: Config = Object.freeze({
4545
appleNotarizationUsername: 'appleNotarizationUsername',
4646
appleNotarizationApplicationPassword: 'appleNotarizationApplicationPassword',
4747
appleCodesignIdentity: 'appleCodesignIdentity',
48+
notarySigningKeyName: 'notarySigningKey',
49+
notaryAuthToken: 'notaryAuthToken',
4850
isCi: true,
4951
platform: 'linux',
5052
repo: {

0 commit comments

Comments
 (0)