@aws-cdk-testing/cli-integ
Version:
Integration tests for the AWS CDK CLI
263 lines (231 loc) • 7.68 kB
text/typescript
/* eslint-disable no-console */
import * as path from 'path';
import * as fs from 'fs-extra';
import * as glob from 'glob';
import * as yargs from 'yargs';
import { shell } from '..';
import { TestRepository } from '../staging/codeartifact';
import { uploadJavaPackages, mavenLogin } from '../staging/maven';
import { uploadNpmPackages, npmLogin } from '../staging/npm';
import { uploadDotnetPackages, nugetLogin } from '../staging/nuget';
import { uploadPythonPackages, pypiLogin } from '../staging/pypi';
import { UsageDir } from '../staging/usage-dir';
async function main() {
await yargs
.usage('$0 <command>')
.option('npm', {
description: 'Upload NPM packages only',
type: 'boolean',
requiresArg: false,
})
.option('python', {
description: 'Upload Python packages only',
type: 'boolean',
requiresArg: false,
})
.option('java', {
description: 'Upload Java packages only',
type: 'boolean',
requiresArg: false,
})
.option('dotnet', {
description: 'Upload Dotnet packages only',
type: 'boolean',
requiresArg: false,
})
.option('regression', {
description: 'Enable access to previous versions of the staged packages (this is expensive for CodeArtifact so we only do it when necessary)',
type: 'boolean',
requiresArg: false,
default: false,
})
.command('publish <DIRECTORY>', 'Publish a given directory', cmd => cmd
.positional('DIRECTORY', {
descripton: 'Directory distribution',
type: 'string',
demandOption: true,
})
.option('name', {
alias: 'n',
description: 'Name of the repository to create (default: generate unique name)',
type: 'string',
requiresArg: true,
}), async (args) => {
await validateDirectory(args);
const repo = await (args.name ? TestRepository.newWithName(args.name) : TestRepository.newRandom());
const usageDir = UsageDir.default();
await doLogin(repo, usageDir, args);
await publish(repo, usageDir, args);
header('Done');
usageDir.advertise();
})
.command('login', 'Login to a given repository', cmd => cmd
.option('name', {
alias: 'n',
description: 'Name of the repository to log in to',
type: 'string',
requiresArg: true,
demandOption: true,
}), async (args) => {
const repo = TestRepository.existing(args.name);
const usageDir = UsageDir.default();
await doLogin(repo, usageDir, args);
usageDir.advertise();
})
.command('run <DIRECTORY> <COMMAND..>', 'Publish and run a command', cmd => cmd
.positional('DIRECTORY', {
descripton: 'Directory distribution',
type: 'string',
demandOption: true,
})
.positional('COMMAND', {
alias: 'c',
description: 'Run the given command with the packages staged',
type: 'string',
array: true,
demandOption: true,
})
.option('cleanup', {
alias: 'C',
description: 'Cleanup the repository afterwards',
type: 'boolean',
default: true,
requiresArg: false,
}), async (args) => {
await validateDirectory(args);
const repo = await TestRepository.newRandom();
const usageDir = UsageDir.default();
await doLogin(repo, usageDir, args);
await publish(repo, usageDir, args);
try {
await usageDir.activateInCurrentProcess();
await shell(args.COMMAND ?? [], {
shell: true,
show: 'always',
});
} finally {
if (args.cleanup) {
await repo.delete();
}
}
})
.command('cleanup', 'Clean up testing repository', cmd => cmd
.option('name', {
alias: 'n',
description: 'Name of the repository to cleanup (default: most recent)',
type: 'string',
requiresArg: true,
}), async (args) => {
const usageDir = UsageDir.default();
let repositoryName = args.name;
if (!repositoryName) {
repositoryName = (await usageDir.currentEnv()).CODEARTIFACT_REPO;
}
if (!repositoryName) {
console.log(`No --name given and no $CODEARTIFACT_REPO found in ${usageDir.directory}, nothing cleaned up`);
return;
}
const repo = TestRepository.existing(repositoryName);
await repo.delete();
})
.command('gc', 'Clean up day-old testing repositories', cmd => cmd, async () => {
await TestRepository.gc();
})
.demandCommand(1, 'You must supply a command')
.help()
.showHelpOnFail(false)
.parse();
}
async function validateDirectory(args: {
DIRECTORY: string;
}) {
if (!await fs.pathExists(path.join(args.DIRECTORY, 'build.json'))) {
throw new Error(`${args.DIRECTORY} does not look like a CDK dist directory (build.json missing)`);
}
}
async function doLogin(repo: TestRepository, usageDir: UsageDir, args: {
npm?: boolean;
python?: boolean;
java?: boolean;
dotnet?: boolean;
}) {
const login = await repo.loginInformation();
const oldEnv = await usageDir.currentEnv();
await usageDir.clean();
await usageDir.addToEnv({
CODEARTIFACT_REPO: login.repositoryName,
});
if (oldEnv.BUILD_VERSION) {
await usageDir.addToEnv({
BUILD_VERSION: oldEnv.BUILD_VERSION,
});
}
const doRepo = whichRepos(args);
await doRepo.npm(() => npmLogin(login, usageDir));
await doRepo.python(() => pypiLogin(login, usageDir));
await doRepo.java(() => mavenLogin(login, usageDir));
await doRepo.dotnet(() => nugetLogin(login, usageDir));
}
async function publish(repo: TestRepository, usageDir: UsageDir, args: {
DIRECTORY: string;
npm?: boolean;
python?: boolean;
java?: boolean;
dotnet?: boolean;
regression?: boolean;
}) {
const directory = `${args.DIRECTORY}`;
const login = await repo.loginInformation();
const doRepo = whichRepos(args);
const buildJson = await fs.readJson(path.join(directory, 'build.json'));
await usageDir.addToEnv({
BUILD_VERSION: buildJson.version,
});
await doRepo.npm(async () => {
header('NPM');
await uploadNpmPackages(glob.sync(path.join(directory, 'js', '*.tgz')), login, usageDir);
});
await doRepo.python(async () => {
header('Python');
await uploadPythonPackages(glob.sync(path.join(directory, 'python', '*')), login);
});
await doRepo.java(async () => {
header('Java');
await uploadJavaPackages(glob.sync(path.join(directory, 'java', '**', '*.pom')), login, usageDir);
});
await doRepo.dotnet(async () => {
header('.NET');
await uploadDotnetPackages(glob.sync(path.join(directory, 'dotnet', '**', '*.nupkg')), usageDir);
});
if (args.regression) {
console.log('🛍 Configuring packages for upstream versions');
await repo.markAllUpstreamAllow();
}
}
function whichRepos(args: {
npm?: boolean;
python?: boolean;
java?: boolean;
dotnet?: boolean;
}) {
const all = args.npm === undefined && args.python === undefined && args.java === undefined && args.dotnet === undefined;
const invoke = (block: () => Promise<void>) => block();
const skip = () => { };
return {
npm: args.npm || all ? invoke : skip,
python: args.python || all ? invoke : skip,
java: args.java || all ? invoke : skip,
dotnet: args.dotnet || all ? invoke : skip,
};
}
function header(caption: string) {
console.log('');
console.log('/'.repeat(70));
console.log(`// ${caption}`);
console.log('');
}
main().catch(e => {
// eslint-disable-next-line no-console
console.error(e);
process.exitCode = 1;
});