Skip to content
Merged
21 changes: 21 additions & 0 deletions config/cta.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

module.exports = {
awsAccessKeyId: process.env.DOWNLOAD_CENTER_AWS_KEY,
awsSecretAccessKey: process.env.DOWNLOAD_CENTER_AWS_SECRET,
ctas: {
// Define the ctas per version here. '*' is the default cta which will be shown if there's no specific cta
// for the current version.
// '*': {
// runs: [
// { text: 'Example', style: 'bold' },
// ]
// },
// '1.2.3': {
// runs: [
// { text: 'Example', style: 'mongosh:uri' },
// ]
// }
},
isDryRun: false,
}
87 changes: 72 additions & 15 deletions packages/build/src/download-center/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
} from '@mongodb-js/dl-center/dist/download-center-config';
import {
ARTIFACTS_BUCKET,
ARTIFACTS_FOLDER,
JSON_FEED_ARTIFACT_KEY,
ARTIFACTS_URL_PUBLIC_BASE,
CONFIGURATION_KEY,
CONFIGURATIONS_BUCKET,
Expand All @@ -32,6 +32,24 @@ import path from 'path';
import semver from 'semver';
import { hashListFiles } from '../run-download-and-list-artifacts';

async function getCurrentJsonFeed(
dlcenterArtifacts: DownloadCenterCls
): Promise<JsonFeed | undefined> {
let existingJsonFeedText;
try {
existingJsonFeedText = await dlcenterArtifacts.downloadAsset(
JSON_FEED_ARTIFACT_KEY
);
} catch (err: any) {
console.warn('Failed to get existing JSON feed text', err);
if (err?.code !== 'NoSuchKey') throw err;
}

return existingJsonFeedText
? JSON.parse(existingJsonFeedText.toString())
: undefined;
}

export async function createAndPublishDownloadCenterConfig(
outputDir: string,
packageInformation: PackageInformationProvider,
Expand Down Expand Up @@ -80,20 +98,8 @@ export async function createAndPublishDownloadCenterConfig(
accessKeyId: awsAccessKeyId,
secretAccessKey: awsSecretAccessKey,
});
const jsonFeedArtifactkey = `${ARTIFACTS_FOLDER}/mongosh.json`;
let existingJsonFeedText;
try {
existingJsonFeedText = await dlcenterArtifacts.downloadAsset(
jsonFeedArtifactkey
);
} catch (err: any) {
console.warn('Failed to get existing JSON feed text', err);
if (err?.code !== 'NoSuchKey') throw err;
}

const existingJsonFeed: JsonFeed | undefined = existingJsonFeedText
? JSON.parse(existingJsonFeedText.toString())
: undefined;
const existingJsonFeed = await getCurrentJsonFeed(dlcenterArtifacts);
const injectedJsonFeed: JsonFeed | undefined = injectedJsonFeedFile
? JSON.parse(await fs.readFile(injectedJsonFeedFile, 'utf8'))
: undefined;
Expand Down Expand Up @@ -122,12 +128,42 @@ export async function createAndPublishDownloadCenterConfig(
await Promise.all([
dlcenter.uploadConfig(CONFIGURATION_KEY, config),
dlcenterArtifacts.uploadAsset(
jsonFeedArtifactkey,
JSON_FEED_ARTIFACT_KEY,
JSON.stringify(newJsonFeed, null, 2)
),
]);
}

export async function updateJsonFeedCTA(
config: UpdateCTAConfig,
DownloadCenter: typeof DownloadCenterCls = DownloadCenterCls
) {
const dlcenterArtifacts = new DownloadCenter({
bucket: ARTIFACTS_BUCKET,
accessKeyId: config.awsAccessKeyId,
secretAccessKey: config.awsSecretAccessKey,
});

const jsonFeed = await getCurrentJsonFeed(dlcenterArtifacts);
if (!jsonFeed) {
throw new Error('No existing JSON feed found');
}

jsonFeed.cta = config.ctas['*'];
for (const version of jsonFeed.versions) {
version.cta = config.ctas[version.version];
}

const patchedJsonFeed = JSON.stringify(jsonFeed, null, 2);
if (config.isDryRun) {
console.warn('Not uploading JSON feed in dry-run mode');
console.warn(`Patched JSON feed: ${patchedJsonFeed}`);
return;
}

await dlcenterArtifacts.uploadAsset(JSON_FEED_ARTIFACT_KEY, patchedJsonFeed);
}

export function getUpdatedDownloadCenterConfig(
downloadedConfig: DownloadCenterConfig,
getVersionConfig: () => ReturnType<typeof createVersionConfig>
Expand Down Expand Up @@ -201,13 +237,32 @@ export function createVersionConfig(
};
}

// TODO: this is duplicated in update-notification-manager.ts
interface GreetingCTADetails {
chunks: {
text: string;
style: string; // TODO: this is actually clr.ts/StyleDefinition
}[];
}

export interface UpdateCTAConfig {
ctas: {
[version: string]: GreetingCTADetails;
};
awsAccessKeyId: string;
awsSecretAccessKey: string;
isDryRun: boolean;
}

interface JsonFeed {
versions: JsonFeedVersionEntry[];
cta?: GreetingCTADetails;
}

interface JsonFeedVersionEntry {
version: string;
downloads: JsonFeedDownloadEntry[];
cta?: GreetingCTADetails;
}

interface JsonFeedDownloadEntry {
Expand Down Expand Up @@ -275,6 +330,8 @@ function mergeFeeds(...args: (JsonFeed | undefined)[]): JsonFeed {
if (index === -1) newFeed.versions.unshift(version);
else newFeed.versions.splice(index, 1, version);
}

newFeed.cta = feed?.cta ?? newFeed.cta;
}
newFeed.versions.sort((a, b) => semver.rcompare(a.version, b.version));
return newFeed;
Expand Down
15 changes: 10 additions & 5 deletions packages/build/src/download-center/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,30 @@ const fallback = require('./fallback.json');
/**
* The S3 bucket for download center configurations.
*/
export const CONFIGURATIONS_BUCKET = 'info-mongodb-com' as const;
export const CONFIGURATIONS_BUCKET = 'info-mongodb-com';

/**
* The S3 object key for the download center configuration.
*/
export const CONFIGURATION_KEY =
'com-download-center/mongosh.multiversion.json' as const;
'com-download-center/mongosh.multiversion.json';

/**
* The S3 bucket for download center artifacts.
*/
export const ARTIFACTS_BUCKET = 'downloads.10gen.com' as const;
export const ARTIFACTS_BUCKET = 'downloads.10gen.com';

/**
* The S3 "folder" for uploaded artifacts.
*/
export const ARTIFACTS_FOLDER = 'compass' as const;
export const ARTIFACTS_FOLDER = 'compass';

/**
* The S3 artifact key for the versions JSON feed.
*/
export const JSON_FEED_ARTIFACT_KEY = `${ARTIFACTS_FOLDER}/mongosh.json`;

export const ARTIFACTS_URL_PUBLIC_BASE =
'https://downloads.mongodb.com/compass/' as const;
'https://downloads.mongodb.com/compass/';

export const ARTIFACTS_FALLBACK = Object.freeze(fallback);
63 changes: 41 additions & 22 deletions packages/build/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { triggerRelease } from './local';
import type { ReleaseCommand } from './release';
import { release } from './release';
import type { Config, PackageVariant } from './config';
import { updateJsonFeedCTA, UpdateCTAConfig } from './download-center';

export { getArtifactUrl, downloadMongoDb };

const validCommands: (ReleaseCommand | 'trigger-release')[] = [
const validCommands: (ReleaseCommand | 'trigger-release' | 'update-cta')[] = [
'bump',
'compile',
'package',
Expand All @@ -20,11 +21,12 @@ const validCommands: (ReleaseCommand | 'trigger-release')[] = [
'download-crypt-shared-library',
'download-and-list-artifacts',
'trigger-release',
'update-cta',
] as const;

const isValidCommand = (
cmd: string
): cmd is ReleaseCommand | 'trigger-release' =>
): cmd is ReleaseCommand | 'trigger-release' | 'update-cta' =>
(validCommands as string[]).includes(cmd);

if (require.main === module) {
Expand All @@ -38,29 +40,46 @@ if (require.main === module) {
);
}

if (command === 'trigger-release') {
await triggerRelease(process.argv.slice(3));
} else {
const config: Config = require(path.join(
__dirname,
'..',
'..',
'..',
'config',
'build.conf.js'
));
switch (command) {
case 'trigger-release':
await triggerRelease(process.argv.slice(3));
break;
case 'update-cta':
const ctaConfig: UpdateCTAConfig = require(path.join(
__dirname,
'..',
'..',
'..',
'config',
'cta.conf.js'
));

const cliBuildVariant = process.argv
.map((arg) => /^--build-variant=(.+)$/.exec(arg))
.filter(Boolean)[0];
if (cliBuildVariant) {
config.packageVariant = cliBuildVariant[1] as PackageVariant;
validatePackageVariant(config.packageVariant);
}
ctaConfig.isDryRun ||= process.argv.includes('--dry-run');

config.isDryRun ||= process.argv.includes('--dry-run');
await updateJsonFeedCTA(ctaConfig);
break;
default:
const config: Config = require(path.join(
__dirname,
'..',
'..',
'..',
'config',
'build.conf.js'
));

await release(command, config);
const cliBuildVariant = process.argv
.map((arg) => /^--build-variant=(.+)$/.exec(arg))
.filter(Boolean)[0];
if (cliBuildVariant) {
config.packageVariant = cliBuildVariant[1] as PackageVariant;
validatePackageVariant(config.packageVariant);
}

config.isDryRun ||= process.argv.includes('--dry-run');

await release(command, config);
break;
}
})().then(
() => process.exit(0),
Expand Down
14 changes: 12 additions & 2 deletions packages/cli-repl/src/cli-repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,11 @@ export class CliRepl implements MongoshIOProvider {
markTime(TimingCategories.DriverSetup, 'completed SP setup');
const initialized = await this.mongoshRepl.initialize(
initialServiceProvider,
await this.getMoreRecentMongoshVersion()
{
moreRecentMongoshVersion: await this.getMoreRecentMongoshVersion(),
currentVersionCTA:
await this.updateNotificationManager.getGreetingCTAForCurrentVersion(),
}
);
markTime(TimingCategories.REPLInstantiation, 'initialized mongosh repl');
this.injectReplFunctions();
Expand Down Expand Up @@ -1264,21 +1268,27 @@ export class CliRepl implements MongoshIOProvider {
const updateURL = (await this.getConfig('updateURL')).trim();
if (!updateURL) return;

const { version: currentVersion } = require('../package.json');
const localFilePath = this.shellHomeDirectory.localPath(
'update-metadata.json'
);

this.bus.emit('mongosh:fetching-update-metadata', {
updateURL,
localFilePath,
currentVersion,
});
await this.updateNotificationManager.fetchUpdateMetadata(
updateURL,
localFilePath
localFilePath,
currentVersion
);
this.bus.emit('mongosh:fetching-update-metadata-complete', {
latest:
await this.updateNotificationManager.getLatestVersionIfMoreRecent(''),
currentVersion,
hasGreetingCTA:
!!(await this.updateNotificationManager.getGreetingCTAForCurrentVersion()),
});
} catch (err: any) {
this.bus.emit('mongosh:error', err, 'startup');
Expand Down
Loading
Loading