Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support reproducible builds using SOURCE_DATE_EPOCH #2645

Open
1 task done
peterhirn opened this issue Aug 8, 2024 · 0 comments · May be fixed by #2646
Open
1 task done

Support reproducible builds using SOURCE_DATE_EPOCH #2645

peterhirn opened this issue Aug 8, 2024 · 0 comments · May be fixed by #2646

Comments

@peterhirn
Copy link

peterhirn commented Aug 8, 2024

Describe the feature

I'd like to create reproducible builds using a framework based on Nitro.

I discovered three instances where Nitro injects non-reproducible timestamps into the output:

Using kaniko --reproducible I was able to create a fully reproducible build (ie. identical container sha256) after applying this patch with pnpm patch

patches/nitropack.patch

diff --git a/dist/nitro.mjs b/dist/nitro.mjs
index d54f501a2e63b68520868b8a40ec923cb73122ac..d00ac0d99091306fb3f9f6932dcd830bae60dcc9 100644
--- a/dist/nitro.mjs
+++ b/dist/nitro.mjs
@@ -1094,7 +1094,7 @@ function publicAssets(nitro) {
             type: nitro._prerenderMeta?.[assetId]?.contentType || mimeType,
             encoding,
             etag,
-            mtime: stat.mtime.toJSON(),
+            mtime: (process.env.SOURCE_DATE_EPOCH ? new Date(process.env.SOURCE_DATE_EPOCH * 1000) : stat.mtime).toJSON(),
             size: stat.size,
             path: relative(nitro.options.output.serverDir, fullPath),
             data: nitro.options.serveStatic === "inline" ? assetData.toString("base64") : void 0
@@ -1209,7 +1209,10 @@ function serverAssets(nitro) {
               type += "; charset=utf-8";
             }
             const etag = createEtag(await promises.readFile(fsPath));
-            const mtime = await promises.stat(fsPath).then((s) => s.mtime.toJSON());
+            const mtime =
+              process.env.SOURCE_DATE_EPOCH
+                  ? new Date(process.env.SOURCE_DATE_EPOCH * 1000).toJSON()
+                  : await promises.stat(fsPath).then((s) => s.mtime.toJSON());
             assets[id].meta = { type, etag, mtime };
           }
         }
@@ -2638,7 +2641,7 @@ async function _build(nitro, rollupConfig) {
   }
   const buildInfoPath = resolve(nitro.options.output.dir, "nitro.json");
   const buildInfo = {
-    date: (/* @__PURE__ */ new Date()).toJSON(),
+    date: (/* @__PURE__ */ process.env.SOURCE_DATE_EPOCH ? new Date(process.env.SOURCE_DATE_EPOCH * 1000) : new Date()).toJSON(),
     preset: nitro.options.preset,
     framework: nitro.options.framework,
     versions: {

This uses the recommended environment variable SOURCE_DATE_EPOCH, see https://reproducible-builds.org/docs/source-date-epoch/

Additional information

  • Would you be willing to help implement this feature?

Notes

Initially I tried to use libfaketime with a fixed timestamp. This did work for nitro.json (ie. new Date()), but Node.js stats.mtime somehow always returns the current time, even though other cli tools returned the fake time (eg. ls, stat, date). Adding native support for SOURCE_DATE_EPOCH seems to be the preferred solution anyways: https://reproducible-builds.org/docs/timestamps/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant