Remove grunt, add s3 publishing tests
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
// Legacy Gruntfile — kept only for the 'metrics' and 'version' tasks.
|
||||
// The main build pipeline uses rspack + swc (see rspack.config.js and .swcrc).
|
||||
module.exports = function (grunt) {
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
});
|
||||
|
||||
grunt.task.loadTasks('tasks');
|
||||
};
|
||||
Generated
+1564
-193
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,7 @@
|
||||
"test:browser": "vitest run --project browser",
|
||||
"test:unit": "vitest run --project node",
|
||||
"test:tasks": "vitest run --project tasks",
|
||||
"test:publish": "npm run build && vitest run --project publish",
|
||||
"test:browser-smoke": "playwright test --config tests/browser/playwright.config.js",
|
||||
"test:serve": "npx serve -l 9999 .",
|
||||
"test:integration": "npm run build && ./tests/integration/run-integration-tests.sh",
|
||||
@@ -77,6 +78,7 @@
|
||||
"concurrently": "^5.0.0",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint-plugin-compat": "^7.0.1",
|
||||
"fauxqs": "^2.3.1",
|
||||
"fs-extra": "^8.1.0",
|
||||
"husky": "^3.1.0",
|
||||
"lint-staged": "^16.3.2",
|
||||
|
||||
+34
-19
@@ -13,18 +13,7 @@ async function main() {
|
||||
const commitInfo = await git.commitInfo();
|
||||
console.log('tag: ', commitInfo.tagName);
|
||||
|
||||
const suffixes = [];
|
||||
|
||||
// Publish the master as "latest" and with the commit-id
|
||||
if (commitInfo.isMaster) {
|
||||
suffixes.push('-latest');
|
||||
suffixes.push('-' + commitInfo.headSha);
|
||||
}
|
||||
|
||||
// Publish tags by their tag-name
|
||||
if (commitInfo.tagName != null && semver.valid(commitInfo.tagName)) {
|
||||
suffixes.push('-' + commitInfo.tagName);
|
||||
}
|
||||
const suffixes = buildSuffixes(commitInfo);
|
||||
|
||||
if (suffixes.length > 0) {
|
||||
validateS3Env();
|
||||
@@ -33,6 +22,21 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
function buildSuffixes(commitInfo) {
|
||||
const suffixes = [];
|
||||
|
||||
if (commitInfo.isMaster) {
|
||||
suffixes.push('-latest');
|
||||
suffixes.push('-' + commitInfo.headSha);
|
||||
}
|
||||
|
||||
if (commitInfo.tagName != null && semver.valid(commitInfo.tagName)) {
|
||||
suffixes.push('-' + commitInfo.tagName);
|
||||
}
|
||||
|
||||
return suffixes;
|
||||
}
|
||||
|
||||
function validateS3Env() {
|
||||
const bucket = process.env.S3_BUCKET_NAME,
|
||||
region = process.env.S3_REGION,
|
||||
@@ -44,12 +48,14 @@ function validateS3Env() {
|
||||
}
|
||||
}
|
||||
|
||||
async function publish(suffixes) {
|
||||
const publishPromises = suffixes.map((suffix) => publishSuffix(suffix));
|
||||
async function publish(suffixes, overrides) {
|
||||
const publishPromises = suffixes.map((suffix) =>
|
||||
publishSuffix(suffix, overrides)
|
||||
);
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function publishSuffix(suffix) {
|
||||
async function publishSuffix(suffix, overrides) {
|
||||
const filenames = [
|
||||
'handlebars.js',
|
||||
'handlebars.min.js',
|
||||
@@ -59,18 +65,19 @@ async function publishSuffix(suffix) {
|
||||
const publishPromises = filenames.map(async (filename) => {
|
||||
const nameInBucket = getNameInBucket(filename, suffix);
|
||||
const localFile = getLocalFile(filename);
|
||||
await uploadToBucket(localFile, nameInBucket);
|
||||
await uploadToBucket(localFile, nameInBucket, overrides);
|
||||
console.log(`Published ${localFile} to build server (${nameInBucket})`);
|
||||
});
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function uploadToBucket(localFile, nameInBucket) {
|
||||
const s3 = getS3Client();
|
||||
async function uploadToBucket(localFile, nameInBucket, overrides) {
|
||||
const s3 = overrides?.s3Client ?? getS3Client();
|
||||
const bucket = overrides?.bucket ?? process.env.S3_BUCKET_NAME;
|
||||
|
||||
return s3.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: process.env.S3_BUCKET_NAME,
|
||||
Bucket: bucket,
|
||||
Key: nameInBucket,
|
||||
Body: fs.readFileSync(localFile, 'utf8'),
|
||||
})
|
||||
@@ -98,6 +105,14 @@ function getLocalFile(filename) {
|
||||
return 'dist/' + filename;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildSuffixes,
|
||||
validateS3Env,
|
||||
publish,
|
||||
getNameInBucket,
|
||||
getLocalFile,
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
const fs = require('fs');
|
||||
const { S3, GetObjectCommand } = require('@aws-sdk/client-s3');
|
||||
|
||||
const {
|
||||
buildSuffixes,
|
||||
validateS3Env,
|
||||
publish,
|
||||
getNameInBucket,
|
||||
getLocalFile,
|
||||
} = require('../publish-to-aws');
|
||||
|
||||
const BUCKET = 'builds.handlebarsjs.com';
|
||||
const PUBLISHED_FILES = [
|
||||
'handlebars.js',
|
||||
'handlebars.min.js',
|
||||
'handlebars.runtime.js',
|
||||
'handlebars.runtime.min.js',
|
||||
];
|
||||
|
||||
let startFauxqs;
|
||||
let server;
|
||||
let s3Client;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ startFauxqs } = await import('fauxqs'));
|
||||
server = await startFauxqs({ port: 0, logger: false });
|
||||
server.createBucket(BUCKET);
|
||||
|
||||
s3Client = new S3({
|
||||
endpoint: server.address,
|
||||
region: 'us-east-1',
|
||||
credentials: { accessKeyId: 'test', secretAccessKey: 'test' },
|
||||
forcePathStyle: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server?.stop();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
server.reset();
|
||||
server.createBucket(BUCKET);
|
||||
});
|
||||
|
||||
async function getObjectBody(key) {
|
||||
const response = await s3Client.send(
|
||||
new GetObjectCommand({ Bucket: BUCKET, Key: key })
|
||||
);
|
||||
return response.Body.transformToString();
|
||||
}
|
||||
|
||||
describe('buildSuffixes', () => {
|
||||
it('should add -latest and commit sha for master', () => {
|
||||
const result = buildSuffixes({
|
||||
isMaster: true,
|
||||
headSha: 'abc123',
|
||||
tagName: null,
|
||||
});
|
||||
expect(result).toEqual(['-latest', '-abc123']);
|
||||
});
|
||||
|
||||
it('should add tag suffix for valid semver tag', () => {
|
||||
const result = buildSuffixes({
|
||||
isMaster: false,
|
||||
headSha: 'abc123',
|
||||
tagName: '4.7.0',
|
||||
});
|
||||
expect(result).toEqual(['-4.7.0']);
|
||||
});
|
||||
|
||||
it('should add both master and tag suffixes when on master with a tag', () => {
|
||||
const result = buildSuffixes({
|
||||
isMaster: true,
|
||||
headSha: 'abc123',
|
||||
tagName: '4.7.0',
|
||||
});
|
||||
expect(result).toEqual(['-latest', '-abc123', '-4.7.0']);
|
||||
});
|
||||
|
||||
it('should return empty array for non-master with no valid tag', () => {
|
||||
const result = buildSuffixes({
|
||||
isMaster: false,
|
||||
headSha: 'abc123',
|
||||
tagName: null,
|
||||
});
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore invalid semver tags', () => {
|
||||
const result = buildSuffixes({
|
||||
isMaster: false,
|
||||
headSha: 'abc123',
|
||||
tagName: 'not-semver',
|
||||
});
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNameInBucket', () => {
|
||||
it('should insert suffix before .js extension', () => {
|
||||
expect(getNameInBucket('handlebars.js', '-latest')).toBe(
|
||||
'handlebars-latest.js'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle .min.js files', () => {
|
||||
expect(getNameInBucket('handlebars.min.js', '-4.7.0')).toBe(
|
||||
'handlebars.min-4.7.0.js'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle runtime files', () => {
|
||||
expect(getNameInBucket('handlebars.runtime.js', '-abc123')).toBe(
|
||||
'handlebars.runtime-abc123.js'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLocalFile', () => {
|
||||
it('should prefix with dist/', () => {
|
||||
expect(getLocalFile('handlebars.js')).toBe('dist/handlebars.js');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateS3Env', () => {
|
||||
const S3_KEYS = [
|
||||
'S3_BUCKET_NAME',
|
||||
'S3_REGION',
|
||||
'S3_ACCESS_KEY_ID',
|
||||
'S3_SECRET_ACCESS_KEY',
|
||||
];
|
||||
const saved = {};
|
||||
|
||||
beforeEach(() => {
|
||||
for (const key of S3_KEYS) {
|
||||
saved[key] = process.env[key];
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const key of S3_KEYS) {
|
||||
if (saved[key] === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = saved[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw when S3 env vars are missing', () => {
|
||||
for (const key of S3_KEYS) {
|
||||
delete process.env[key];
|
||||
}
|
||||
|
||||
expect(() => validateS3Env()).toThrow('Missing S3 config values');
|
||||
});
|
||||
|
||||
it('should not throw when all S3 env vars are set', () => {
|
||||
process.env.S3_BUCKET_NAME = 'test-bucket';
|
||||
process.env.S3_REGION = 'us-east-1';
|
||||
process.env.S3_ACCESS_KEY_ID = 'key';
|
||||
process.env.S3_SECRET_ACCESS_KEY = 'secret';
|
||||
|
||||
expect(() => validateS3Env()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('publish to S3', () => {
|
||||
const overrides = {};
|
||||
|
||||
beforeAll(() => {
|
||||
overrides.s3Client = s3Client;
|
||||
overrides.bucket = BUCKET;
|
||||
});
|
||||
|
||||
it('should upload all 4 files for a single suffix', async () => {
|
||||
await publish(['-latest'], overrides);
|
||||
|
||||
for (const filename of PUBLISHED_FILES) {
|
||||
const key = getNameInBucket(filename, '-latest');
|
||||
const body = await getObjectBody(key);
|
||||
const localContent = fs.readFileSync(getLocalFile(filename), 'utf8');
|
||||
expect(body).toBe(localContent);
|
||||
}
|
||||
});
|
||||
|
||||
it('should upload files for multiple suffixes', async () => {
|
||||
await publish(['-latest', '-abc123'], overrides);
|
||||
|
||||
for (const suffix of ['-latest', '-abc123']) {
|
||||
for (const filename of PUBLISHED_FILES) {
|
||||
const key = getNameInBucket(filename, suffix);
|
||||
const body = await getObjectBody(key);
|
||||
expect(body).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should upload correct content from dist/', async () => {
|
||||
await publish(['-v1.0.0'], overrides);
|
||||
|
||||
const key = getNameInBucket('handlebars.js', '-v1.0.0');
|
||||
const uploaded = await getObjectBody(key);
|
||||
const local = fs.readFileSync('dist/handlebars.js', 'utf8');
|
||||
expect(uploaded).toBe(local);
|
||||
});
|
||||
|
||||
it('should produce correct keys for a version tag', async () => {
|
||||
await publish(['-4.7.0'], overrides);
|
||||
|
||||
const expectedKeys = PUBLISHED_FILES.map((f) =>
|
||||
getNameInBucket(f, '-4.7.0')
|
||||
);
|
||||
|
||||
for (const key of expectedKeys) {
|
||||
const body = await getObjectBody(key);
|
||||
expect(body).toBeTruthy();
|
||||
}
|
||||
|
||||
expect(expectedKeys).toEqual([
|
||||
'handlebars-4.7.0.js',
|
||||
'handlebars.min-4.7.0.js',
|
||||
'handlebars.runtime-4.7.0.js',
|
||||
'handlebars.runtime.min-4.7.0.js',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = { createRegisterAsyncTaskFn };
|
||||
|
||||
function createRegisterAsyncTaskFn(grunt) {
|
||||
return function registerAsyncTask(name, asyncFunction) {
|
||||
grunt.registerTask(name, function () {
|
||||
asyncFunction()
|
||||
.catch((error) => {
|
||||
grunt.fatal(error);
|
||||
})
|
||||
.finally(this.async());
|
||||
});
|
||||
};
|
||||
}
|
||||
+28
-23
@@ -1,23 +1,22 @@
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
const git = require('./util/git');
|
||||
const semver = require('semver');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
|
||||
registerAsyncTask('version', async () => {
|
||||
const pkg = grunt.config('pkg');
|
||||
const version = grunt.option('ver');
|
||||
async function main() {
|
||||
const version = process.argv[2];
|
||||
if (!semver.valid(version)) {
|
||||
throw new Error(
|
||||
'Must provide a version number (Ex: --ver=1.0.0):\n\t' +
|
||||
'Must provide a valid semver version as first argument (e.g.: node tasks/version.js 1.0.0):\n\t' +
|
||||
version +
|
||||
'\n\n'
|
||||
);
|
||||
}
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||
pkg.version = version;
|
||||
grunt.config('pkg', pkg);
|
||||
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
|
||||
await git.add('package.json');
|
||||
|
||||
const replaceSpec = [
|
||||
{
|
||||
@@ -43,21 +42,27 @@ module.exports = function (grunt) {
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
replaceSpec.map((replaceSpec) =>
|
||||
replaceAndAdd(
|
||||
replaceSpec.path,
|
||||
replaceSpec.regex,
|
||||
replaceSpec.replacement
|
||||
)
|
||||
replaceSpec.map((spec) =>
|
||||
replaceAndAdd(spec.path, spec.regex, spec.replacement)
|
||||
)
|
||||
);
|
||||
execSync('npm run build', { stdio: 'inherit' });
|
||||
});
|
||||
|
||||
async function replaceAndAdd(path, regex, value) {
|
||||
let content = grunt.file.read(path);
|
||||
execSync('npm run build', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
async function replaceAndAdd(filePath, regex, value) {
|
||||
let content = fs.readFileSync(filePath, 'utf8');
|
||||
content = content.replace(regex, value);
|
||||
grunt.file.write(path, content);
|
||||
await git.add(path);
|
||||
}
|
||||
};
|
||||
fs.writeFileSync(filePath, content);
|
||||
await git.add(filePath);
|
||||
}
|
||||
|
||||
module.exports = { replaceAndAdd };
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((err) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ Execute the following commands in the project root:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npx grunt prepare
|
||||
npm run build
|
||||
docker pull mcr.microsoft.com/playwright:focal
|
||||
docker run -it --rm --volume $(pwd):/srv/app --workdir /srv/app --ipc=host mcr.microsoft.com/playwright:focal npm run test:browser
|
||||
```
|
||||
|
||||
@@ -17,6 +17,7 @@ export default defineConfig({
|
||||
test: {
|
||||
name: 'tasks',
|
||||
include: ['tasks/tests/*.test.js'],
|
||||
exclude: ['tasks/tests/publish-to-aws.test.js'],
|
||||
globals: true,
|
||||
pool: 'forks',
|
||||
},
|
||||
@@ -28,6 +29,14 @@ export default defineConfig({
|
||||
globals: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: {
|
||||
name: 'publish',
|
||||
include: ['tasks/tests/publish-to-aws.test.js'],
|
||||
globals: true,
|
||||
pool: 'forks',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: {
|
||||
name: 'browser',
|
||||
|
||||
Reference in New Issue
Block a user