From 2e5c5d33e4ff1430bf8390386ce9d9a804d58e82 Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Sun, 25 Jan 2026 18:32:09 +0100 Subject: [PATCH] build(scripts): use submodule commit info for reproducible builds Replace non-deterministic timestamps with submodule commit hash and date for reproducible builds. The year range for holiday data is now derived from the submodule's commit timestamp instead of the current date. Ref: https://reproducible-builds.org/docs/timestamps/ --- scripts/fetch-school-holidays.mjs | 41 ++++++++++++++++++++++---- src/holidays/generated-openholidays.js | 2 +- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/scripts/fetch-school-holidays.mjs b/scripts/fetch-school-holidays.mjs index 357e6bb7..13f720e2 100755 --- a/scripts/fetch-school-holidays.mjs +++ b/scripts/fetch-school-holidays.mjs @@ -340,15 +340,41 @@ function convertCSVToInternalFormat(csvData, country, yearRange) { return result; } +/** + * Get submodule commit info for reproducible builds + */ +async function getSubmoduleInfo() { + const { execSync } = await import('child_process'); + const submodulePath = path.join(ROOT_DIR, 'submodules', 'openholidaysapi.data'); + + const hash = execSync('git rev-parse --short HEAD', { + cwd: submodulePath, + encoding: 'utf8' + }).trim(); + + // Commit timestamp in seconds (for year range calculation) + const commitUnixTimestamp = parseInt( + execSync('git show --no-patch --format=%ct HEAD', { + cwd: submodulePath, + encoding: 'utf8' + }).trim(), + 10 + ); + + return { hash, commitUnixTimestamp }; +} + /** * Generate JavaScript file with holiday definitions */ -async function generateJavaScriptFile(countriesData, yearRange) { +async function generateJavaScriptFile(countriesData, yearRange, submodule) { + const commitDate = new Date(submodule.commitUnixTimestamp * 1000).toISOString().split('T')[0]; + const lines = [ '/**', ' * Auto-generated school holidays from OpenHolidays API Data (Git Submodule)', ' * DO NOT EDIT MANUALLY - Run: node scripts/fetch-school-holidays.mjs', - ` * Generated: ${new Date().toISOString()}`, + ` * Submodule: ${submodule.hash} (${commitDate})`, ' */', '' ]; @@ -635,11 +661,14 @@ async function buildSchoolHolidays() { } } - // Generate JavaScript file (limit to current year ±15) - const currentYear = new Date().getFullYear(); - const yearRange = [currentYear - 15, currentYear + 15]; + // Generate JavaScript file (limit to submodule commit year ±15) + // Using submodule timestamp ensures reproducible builds + // @see https://reproducible-builds.org/docs/timestamps/ + const submodule = await getSubmoduleInfo(); + const referenceYear = new Date(submodule.commitUnixTimestamp * 1000).getUTCFullYear(); + const yearRange = [referenceYear - 15, referenceYear + 15]; console.log(`📅 Year range: ${yearRange[0]}–${yearRange[1]}`); - const jsContent = await generateJavaScriptFile(results, yearRange); + const jsContent = await generateJavaScriptFile(results, yearRange, submodule); await fs.writeFile(GENERATED_FILE, jsContent, 'utf8'); const jsStats = await fs.stat(GENERATED_FILE); diff --git a/src/holidays/generated-openholidays.js b/src/holidays/generated-openholidays.js index 50cac251..ddf7aff7 100644 --- a/src/holidays/generated-openholidays.js +++ b/src/holidays/generated-openholidays.js @@ -1,7 +1,7 @@ /** * Auto-generated school holidays from OpenHolidays API Data (Git Submodule) * DO NOT EDIT MANUALLY - Run: node scripts/fetch-school-holidays.mjs - * Generated: 2026-01-21T17:35:41.390Z + * Submodule: dd3dfa8 (2026-01-21) */ export const ad = {