Skip to content

perl: complete reproducibility — pin all host/time-derived Config fields - #291

Merged
bryan-minimal merged 4 commits into
mainfrom
bryan/perl-config-over
Jun 29, 2026
Merged

perl: complete reproducibility — pin all host/time-derived Config fields#291
bryan-minimal merged 4 commits into
mainfrom
bryan/perl-config-over

Conversation

@bryan-minimal

@bryan-minimal bryan-minimal commented Jun 23, 2026

Copy link
Copy Markdown
Member

Problem

The merged #249 pinned perl's build time via -D cf_time, but the rebuild-world audit caught that fix was incomplete — a forced double-build still DIFFERS. perl's Configure recomputes cf_time from date unconditionally, so the -D cf_time override never sticks. The build wall-clock and the sandbox's per-build minimal-<pid> hostname still leak into Config_heavy.pl, perlbug, and perlthanks via five fields:

cf_time     = build wall-clock     (the original target, not actually pinned)
myuname     = `uname -a`           ("Target system" line — embeds nodename)
myhostname  = minimal-<pid>
cf_email    = build@minimal-<pid>.nonet
perladmin   = build@minimal-<pid>.nonet

Fix

Pin them all via config.over, which Configure sources after it computes everything — perl's documented override hook (unlike -D, which gets overwritten):

  • cf_time / cf_by derived from SOURCE_DATE_EPOCH
  • myuname with only the nodename sanitized (keeps real kernel info)
  • myhostname / cf_email / perladmin → fixed tokens

Verification

Two from-scratch forced rebuilds (--rebuild --no-fetch, aarch64) → byte-identical: repro-check diff reports 2736/2736 files identical, REPRODUCIBLE. Before: DIFFERS on the 3 Config files. (This time the double-build was actually run end-to-end — the first two attempts at this fix were rejected by the verify before landing.)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Improved reproducibility of Perl builds by ensuring generated configuration metadata is deterministic, including time-based fields and host-derived identity values. This reduces build-to-build variability caused by system environment differences during the build process.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c8a6e75-4866-46dd-a1bd-9dc8d2b74cee

📥 Commits

Reviewing files that changed from the base of the PR and between bbd9243 and 208e821.

📒 Files selected for processing (1)
  • packages/perl/build.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/perl/build.sh

📝 Walkthrough

Walkthrough

packages/perl/build.sh now derives Perl configuration values into config.over, including a deterministic timestamp and host identity fields, and stops passing cf_time and cf_by directly on the Configure command line.

Changes

Perl Deterministic Configuration

Layer / File(s) Summary
config.over generation and Configure wiring
packages/perl/build.sh
Computes CF_TIME from SOURCE_DATE_EPOCH, derives MYUNAME from uname -a with sanitization, writes config.over with timestamp and identity values, and drops the -D cf_time and -D cf_by arguments from Configure.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • gominimal/pkgs#249: Modifies the same Perl build script to pin cf_time and cf_by, using a different mechanism to pass those values into Configure.
  • gominimal/pkgs#266: Also changes packages/perl/build.sh around SOURCE_DATE_EPOCH handling for reproducible Perl configuration.

Suggested reviewers

  • twitchyliquid64

Poem

🐇 I hop through builds with steady tread,
A fixed-time carrot keeps clocks well-fed.
config.over hums, the host details align,
No drifting moon, no sneaky time line.
Deterministic munches, neat and sublime.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: pinning host- and time-derived Perl Config fields for reproducible builds.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bryan/perl-config-over

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/perl/build.sh (1)

30-30: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Sanitize the nodename by field position rather than regex substitution.

Using $(uname -n) as a sed pattern is fragile: if the nodename is empty the expression degrades to s//builder/g (sed errors on "no previous regular expression", failing the build under set -e), and any regex metacharacter or a / in the nodename would misfire or break the delimiter. Since the nodename is reliably the second whitespace-delimited field of uname -a (the kernel-version field may contain spaces but the nodename never does), replacing field 2 with awk is robust and avoids the regex pitfalls entirely.

♻️ Proposed robust sanitization
-MYUNAME="$(uname -a | sed "s/$(uname -n)/builder/g" | tr '[:upper:]' '[:lower:]' | sed 's#/##g')"
+MYUNAME="$(uname -a | awk '{$2="builder"; print}' | tr '[:upper:]' '[:lower:]' | tr -d '/')"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/perl/build.sh` at line 30, The MYUNAME assignment uses sed with a
dynamic nodename pattern from uname -n, which is fragile and can fail if the
nodename is empty (causing sed to error on an invalid regex) or contains regex
metacharacters or forward slashes. Instead of using sed to replace the nodename
pattern, use awk to directly replace the second whitespace-delimited field of
the uname -a output with "builder", since the nodename is reliably the second
field and this avoids regex pitfalls entirely. This makes the sanitization
robust and eliminates the dependency on the potentially problematic nodename
value being used as a sed pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/perl/build.sh`:
- Line 23: The export statement for SOURCE_DATE_EPOCH in build.sh violates repo
guidelines that state SOURCE_DATE_EPOCH should not be set in build.sh since it
is already exported by the sandbox. Remove the line that exports
SOURCE_DATE_EPOCH to comply with the guideline.

---

Nitpick comments:
In `@packages/perl/build.sh`:
- Line 30: The MYUNAME assignment uses sed with a dynamic nodename pattern from
uname -n, which is fragile and can fail if the nodename is empty (causing sed to
error on an invalid regex) or contains regex metacharacters or forward slashes.
Instead of using sed to replace the nodename pattern, use awk to directly
replace the second whitespace-delimited field of the uname -a output with
"builder", since the nodename is reliably the second field and this avoids regex
pitfalls entirely. This makes the sanitization robust and eliminates the
dependency on the potentially problematic nodename value being used as a sed
pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d344ac26-851f-4713-9283-204da83c3029

📥 Commits

Reviewing files that changed from the base of the PR and between 60b5fd6 and 97ab419.

📒 Files selected for processing (1)
  • packages/perl/build.sh

Comment thread packages/perl/build.sh Outdated

@edge-delta edge-delta Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the approach is sound — config.over is the correct override hook for perl's Configure, and the fields covered match the known reproducibility leaks. One robustness concern on the hostname substitution below.

@edge-delta edge-delta Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline comment on the sed hostname substitution — minor robustness concern for out-of-sandbox reproducibility.

Comment thread packages/perl/build.sh Outdated
# system" line, via `uname -a`), myhostname, and the derived cf_email/perladmin.
# config.over is sourced after all computation, so pin every host-derived field
# here. myuname keeps the real kernel info with just the nodename sanitized.
MYUNAME="$(uname -a | sed "s/$(uname -n)/builder/g" | tr '[:upper:]' '[:lower:]' | sed 's#/##g')"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug (robustness): $(uname -n) is interpolated directly into a sed regex pattern. If the hostname contains regex metacharacters (., *, [, \, etc.) — which is uncommon but possible — the substitution will silently misbehave or fail.

A safer approach would escape the hostname for use in a sed BRE, or use a fixed-string replacement tool:

MYUNAME="$(uname -a | sed "s/$(uname -n | sed 's/[.[\*^$]/\\\\&/g')/builder/g" | tr '[:upper:]' '[:lower:]' | sed 's#/##g')"

Or more readably, capture then use parameter expansion or awk:

_nodename="$(uname -n)"
MYUNAME="$(uname -a | awk -v n="$_nodename" -v r="builder" '{gsub(n, r)}1' | tr '[:upper:]' '[:lower:]' | sed 's#/##g')"

In practice, the sandbox hostname (minimal-<pid>) is safe, but if this script is ever reused outside the sandbox (as the SOURCE_DATE_EPOCH fallback suggests), a hostname with dots would break the substitution silently — the resulting myuname would still contain the original hostname, defeating the reproducibility goal.

bryan-minimal added a commit that referenced this pull request Jun 26, 2026
…t awk nodename sanitize

Per CodeRabbit on #291:
- The sandbox already exports SOURCE_DATE_EPOCH (our recipe says don't re-set it),
  so read it with a fallback at the use site instead of re-exporting.
- Sanitize the uname nodename via awk field-2 replacement rather than using
  $(uname -n) as a sed pattern, which breaks on an empty/regex/slash nodename.

The determinism mechanism (config.over pinning of the host/time fields) is
unchanged — both are robustness/style refinements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bryan-minimal

Copy link
Copy Markdown
Member Author

Thanks @coderabbitai — both addressed (e3a103c), and re-verified byte-identical.

  • Dropped the redundant export SOURCE_DATE_EPOCH: the sandbox already exports it (the repo recipe says not to re-set it), so build.sh reads it with a fallback at the use site — date -u -d "@${SOURCE_DATE_EPOCH:-0}".
  • awk field-2 nodename sanitize: uname -a | awk '{$2="builder"; print}' replaces field 2 directly — robust against an empty / regex-metachar / slash nodename, no sed-pattern pitfalls.

Confirmed the refinements don't regress determinism: a fresh from-scratch double-build is byte-identical (repro-check diff, --rebuild --no-fetch, aarch64 → REPRODUCIBLE).

(Follow-up 457960f rewords a comment whose literal text the disallowed-patterns checker rejects even in comments.)

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@bryan-minimal Thanks — that all sounds good. Dropping the redundant SOURCE_DATE_EPOCH export and switching the nodename rewrite to field-based awk both make sense, and the byte-identical re-verification is a strong confirmation.

🐇✨


✏️ Learnings added
Learnt from: bryan-minimal
URL: https://github.com/gominimal/pkgs/pull/291

Timestamp: 2026-06-26T22:19:09.214Z
Learning: In the `gominimal/pkgs` repository, `packages/perl/build.sh` should not re-export `SOURCE_DATE_EPOCH`; the sandbox already provides it and the repo recipe says not to re-set it. For Perl reproducibility logic in that file, read `SOURCE_DATE_EPOCH` at the use site with a fallback, e.g. `date -u -d "@${SOURCE_DATE_EPOCH:-0}"`.

Learnt from: bryan-minimal
URL: https://github.com/gominimal/pkgs/pull/291

Timestamp: 2026-06-26T22:19:09.214Z
Learning: In the `gominimal/pkgs` repository, `packages/perl/build.sh` should sanitize the nodename portion of `uname -a` by replacing field 2 with `awk` (`uname -a | awk '{$2="builder"; print}'`) rather than using `sed`, because the `awk` approach is robust to empty hostnames and regex metacharacters or slashes in the nodename.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

bryan-minimal and others added 3 commits June 29, 2026 08:02
#249 passed `-D cf_time`, but perl's Configure recomputes cf_time from `date`
UNCONDITIONALLY, so the override never stuck — the build wall-clock and the
sandbox's per-build `minimal-<pid>` hostname still leaked into Config_heavy.pl,
perlbug, and perlthanks through cf_time, myuname, myhostname, cf_email, and
perladmin.

Pin them all via config.over, which Configure sources AFTER it computes
everything (perl's documented override hook, unlike -D). cf_time/cf_by derive
from SOURCE_DATE_EPOCH; myuname keeps the real kernel info with only the nodename
sanitized; the remaining host-derived fields go to fixed tokens.

Verified byte-identical across two from-scratch forced rebuilds
(--rebuild --no-fetch, aarch64): repro-check diff -> 2736/2736 files identical,
REPRODUCIBLE. (Before: DIFFERS on the 3 Config files.) Found by the rebuild-world
audit, which caught that #249's fix was incomplete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t awk nodename sanitize

Per CodeRabbit on #291:
- The sandbox already exports SOURCE_DATE_EPOCH (our recipe says don't re-set it),
  so read it with a fallback at the use site instead of re-exporting.
- Sanitize the uname nodename via awk field-2 replacement rather than using
  $(uname -n) as a sed pattern, which breaks on an empty/regex/slash nodename.

The determinism mechanism (config.over pinning of the host/time fields) is
unchanged — both are robustness/style refinements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…l in build.sh

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bryan-minimal
bryan-minimal force-pushed the bryan/perl-config-over branch from 9357e4a to 208e821 Compare June 29, 2026 15:02
The fix is verified byte-identical locally; nushell #292 (same approach) is green.
No code change — empty commit to re-run the build executor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bryan-minimal
bryan-minimal added this pull request to the merge queue Jun 29, 2026
Merged via the queue into main with commit 1a2091d Jun 29, 2026
5 checks passed
@bryan-minimal
bryan-minimal deleted the bryan/perl-config-over branch June 29, 2026 20:15
@bryan-minimal

Copy link
Copy Markdown
Member Author

Part of the reproducibility epic: gominimal/inbox#253

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants