diff --git a/tasks/aws-s3-builds-page/fileList.hbs b/tasks/aws-s3-builds-page/fileList.hbs new file mode 100644 index 00000000..265f3248 --- /dev/null +++ b/tasks/aws-s3-builds-page/fileList.hbs @@ -0,0 +1,127 @@ + + + + + + + + Handlebars.js Builds + + +

Handlebars.js builds

+

See https://handlebarsjs.com for documentation.

+

Machine-readable version: {{jsonListUrl}}

+ + + + + + + + + + {{#each fileList as | file |}} + + + + + + {{/each}} + + +
NameSizeLast-Modified
{{key}}{{file.size}}{{file.lastModified}}
+ + + \ No newline at end of file diff --git a/tasks/aws-s3-builds-page/generateFileList-test.js b/tasks/aws-s3-builds-page/generateFileList-test.js index e3cf3d70..9d2cd794 100644 --- a/tasks/aws-s3-builds-page/generateFileList-test.js +++ b/tasks/aws-s3-builds-page/generateFileList-test.js @@ -13,12 +13,18 @@ const s3Client = createS3Client(); runTest(async ({ log }) => { log('Generate file list'); - const filename = `test-file-list${crypto.randomUUID()}`; + const filename = `test-file-list-${crypto.randomUUID()}`; await generateFileList(filename); log(`Checking JSON at ${s3Client.fileUrl(`${filename}.json`)}`); const jsonList = JSON.parse(await s3Client.fetchFile(`${filename}.json`)); - assert(jsonList.includes('handlebars-v4.7.7.js')); + assert(jsonList.find(s3obj => s3obj.key === 'handlebars-v4.7.7.js')); + + log(`Checking HTML at ${s3Client.fileUrl(`${filename}.html`)}`); + const htmlList = await s3Client.fetchFile(`${filename}.html`); + assert(htmlList.includes('handlebars-v4.7.7.js')); + assert(htmlList.includes('handlebarsjs.com')); + assert(!htmlList.includes('index.html')); log(`Deleting file ${filename}.json`); await s3Client.deleteFile(`${filename}.json`); diff --git a/tasks/aws-s3-builds-page/generateFileList.js b/tasks/aws-s3-builds-page/generateFileList.js index 49cceb50..efe38302 100644 --- a/tasks/aws-s3-builds-page/generateFileList.js +++ b/tasks/aws-s3-builds-page/generateFileList.js @@ -1,13 +1,39 @@ /* eslint-disable no-console */ const { createS3Client } = require('./s3client'); +const Handlebars = require('../..'); +const fs = require('node:fs/promises'); +const path = require('path'); async function generateFileList(nameWithoutExtension) { const s3Client = createS3Client(); const fileList = await s3Client.listFiles(); + const relevantFiles = fileList.filter(s3obj => s3obj.key.endsWith('.js')); + + await uploadJson(s3Client, relevantFiles, nameWithoutExtension); + await uploadHtml(s3Client, relevantFiles, nameWithoutExtension); +} + +async function uploadJson(s3Client, fileList, nameWithoutExtension) { const fileListJson = JSON.stringify(fileList, null, 2); await s3Client.uploadData(fileListJson, nameWithoutExtension + '.json', { contentType: 'application/json' }); } +async function uploadHtml(s3Client, fileList, nameWithoutExtension) { + const templateStr = await fs.readFile( + path.join(__dirname, 'fileList.hbs'), + 'utf-8' + ); + const template = Handlebars.compile(templateStr); + Handlebars.registerHelper('json', obj => JSON.stringify(obj)); + const fileListHtml = template({ + fileList, + jsonListUrl: nameWithoutExtension + '.json' + }); + await s3Client.uploadData(fileListHtml, nameWithoutExtension + '.html', { + contentType: 'text/html' + }); +} + module.exports = { generateFileList }; diff --git a/tasks/aws-s3-builds-page/s3client/listFiles.js b/tasks/aws-s3-builds-page/s3client/listFiles.js index 8796e2ed..922332ae 100644 --- a/tasks/aws-s3-builds-page/s3client/listFiles.js +++ b/tasks/aws-s3-builds-page/s3client/listFiles.js @@ -14,11 +14,19 @@ async function listFiles(s3Client, bucket) { IsTruncated, NextContinuationToken } = await s3Client.send(command); - files.push(...Contents.map(s3obj => s3obj.Key)); + files.push(...Contents.map(dataFromS3Object)); isTruncated = IsTruncated; command.input.ContinuationToken = NextContinuationToken; } return files; } +function dataFromS3Object(s3obj) { + return { + key: s3obj.Key, + size: s3obj.Size, + lastModified: s3obj.LastModified.toISOString() + }; +} + module.exports = { listFiles }; diff --git a/tasks/aws-s3-builds-page/s3client/s3-test.js b/tasks/aws-s3-builds-page/s3client/s3-test.js index 58e5448b..6a0a442b 100644 --- a/tasks/aws-s3-builds-page/s3client/s3-test.js +++ b/tasks/aws-s3-builds-page/s3client/s3-test.js @@ -2,6 +2,7 @@ const { createS3Client } = require('./index'); const crypto = require('crypto'); const { runTest } = require('../test-utils/runTest'); +const assert = require('node:assert'); // This is a test file. It is intended to be run manually // with the proper environment variables set @@ -12,6 +13,8 @@ const { runTest } = require('../test-utils/runTest'); const client = createS3Client(); +const ISO_DATE = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/; + runTest(async ({ log }) => { const uuid = crypto.randomUUID(); const filename = `test-file-${uuid}`; @@ -22,7 +25,7 @@ runTest(async ({ log }) => { log(`Check if uploaded "${filename}"`); const listing = await client.listFiles(); - if (!listing.includes(filename)) { + if (!listing.find(s3obj => s3obj.key === filename)) { throw new Error(`File "${filename}" has not been uploaded`); } @@ -46,13 +49,24 @@ runTest(async ({ log }) => { await expectContentType(filename, 'text/plain'); log(`Check contents of "${filename}"`); expectStringContains('Hello world', await client.fetchFile(filename)); + const helloWorldObj = (await client.listFiles()).find( + s3obj => s3obj.key === filename + ); + assert.equal(helloWorldObj.size, 11, 'Checking file size of hello world'); + assert.match( + helloWorldObj.lastModified, + ISO_DATE, + 'Last modified must be an iso-date' + ); log(`Delete "${filename}"`); await client.deleteFile(filename); log(`Check if deleted "${filename}"`); - const listingAfterDelete = await client.listFiles(); - if (listingAfterDelete.includes(filename)) { + const foundFile = (await client.listFiles()).find( + s3obj => s3obj.key === filename + ); + if (foundFile != null) { throw new Error(`File "${filename}" has not been deleted`); } }); diff --git a/tasks/aws-s3-builds-page/test-utils/runTest.js b/tasks/aws-s3-builds-page/test-utils/runTest.js index 1c94718a..21963039 100644 --- a/tasks/aws-s3-builds-page/test-utils/runTest.js +++ b/tasks/aws-s3-builds-page/test-utils/runTest.js @@ -18,8 +18,10 @@ function runTest(asyncFn) { async function detectSurplusFiles() { const listing = await s3Client.listFiles(); let surplusFileDetected = false; - const testFilesInBucket = listing.filter(name => name.includes('test-file')); - for (const filename of testFilesInBucket) { + const testFilesInBucket = listing.filter(name => + name.key.includes('test-file') + ); + for (const { key: filename } of testFilesInBucket) { if (process.argv[2] === '--delete-surplus') { await s3Client.deleteFile(filename); } else { diff --git a/tasks/publish-to-aws.js b/tasks/publish-to-aws.js index 522ef0f8..ff7037c6 100644 --- a/tasks/publish-to-aws.js +++ b/tasks/publish-to-aws.js @@ -2,6 +2,7 @@ const git = require('./util/git'); const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); const semver = require('semver'); const { publishWithSuffixes } = require('./aws-s3-builds-page/publish'); +const { generateFileList } = require('./aws-s3-builds-page/generateFileList'); module.exports = function(grunt) { const registerAsyncTask = createRegisterAsyncTaskFn(grunt); @@ -28,6 +29,7 @@ module.exports = function(grunt) { if (suffixes.length > 0) { await publishWithSuffixes(suffixes); + await generateFileList('index'); } }); };