Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions scripts/build-time
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,28 @@ const verbose = (...args) => flags.verbose && console.log(...args);
const exit = (code = 0) => process.exit(flags.forceSuccess ? 0 : code);

try {
const now = new Date();
const timestamp = new Date().toLocaleString(undefined, {dateStyle: 'short', timeStyle: 'medium'});
// Check for reproducible build mode (for Nix and other package managers)
// Use SOURCE_DATE_EPOCH if provided for deterministic builds
let now;
let usingSourceDateEpoch = false;

if (process.env.SOURCE_DATE_EPOCH) {
// Nix and other reproducible build systems set this to make builds deterministic
// This allows the build-time.json to exist but with a fixed timestamp
now = new Date(parseInt(process.env.SOURCE_DATE_EPOCH) * 1000);
usingSourceDateEpoch = true;
verbose('Using SOURCE_DATE_EPOCH for deterministic build:'.green(), process.env.SOURCE_DATE_EPOCH);
} else if (process.env.REPRODUCIBLE_BUILD === '1') {
// Skip build-time.json creation entirely (alternative approach)
log('Skipping build-time.json creation for reproducible build'.yellow());
verbose('REPRODUCIBLE_BUILD=1 detected, skipping...'.dim());
exit(0);
} else {
// Normal build - use current time
now = new Date();
}

const timestamp = now.toLocaleString(undefined, {dateStyle: 'short', timeStyle: 'medium'});
const buildTimeData = {
date: now.toDateString(),
timestamp,
Expand Down Expand Up @@ -131,7 +151,11 @@ try {
}

log('✓ created JSON at:'.bgGreen().black().bold(), path.basename(jsonFilePath).cyan().dim());
log('✓ with timestamp: '.bgGreen().black().bold(), timestamp.blue().dim());
if (usingSourceDateEpoch) {
log('✓ with timestamp: '.bgGreen().black().bold(), timestamp.blue().dim(), '(from SOURCE_DATE_EPOCH)'.gray().dim());
} else {
log('✓ with timestamp: '.bgGreen().black().bold(), timestamp.blue().dim());
}

} catch (error) {
error("ERROR:".bgRed().white().bold(), "Script execution failed.".red());
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ commandBin.command('info')
}
// handle `[--man-file | --log-file] (--show)?`
if (args.manFile || args.logFile || args.logsFile) {
exitCode = CommanderSubcommand.info.handleFileArgs(args);
exitCode = CommanderSubcommand.info.handleFileArgs(args) ?? 0;
shouldExit = true;
}
// handle `--virtual-fs`
Expand Down
38 changes: 28 additions & 10 deletions src/utils/commander-cli-subcommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,19 +272,22 @@ const getOutTime = () => {
}
}

// Fallback to reading from file (for development version)
// Try reading from file (for development version)
const buildFile = getFishBuildTimeFilePath();
try {
const fileContent = readFileSync(buildFile, 'utf8');
const buildTimeData = JSON.parse(fileContent);
return buildTimeData.timestamp || buildTimeData.isoTimestamp;
} catch (e) {
logger.logToStderr(`Error reading build-time file: ${buildFile}`);
logger.error([
`Error reading build-time file: ${buildFile}`,
`Could not read build time from file: ${e}`,
]);
return 'unknown';
// Fallback to executable modification time for reproducible builds
try {
const execPath = getCurrentExecutablePath();
const stats = fs.statSync(execPath);
return stats.mtime.toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'medium' });
} catch (statErr) {
logger.logToStderr(`Error reading executable stats: ${statErr}`);
return 'unknown';
}
}
};

Expand All @@ -308,15 +311,30 @@ export const getBuildTimeJsonObj = (): BuildTimeJsonObj | undefined => {
}
}

// Fallback to reading from file (for development version)
// Try reading from file (for development version)
try {
const jsonFile = getFishBuildTimeFilePath();
const jsonContent = readFileSync(jsonFile, 'utf8');
const jsonObj: BuildTimeJsonObj = JSON.parse(jsonContent);
return { ...jsonObj, date: new Date(jsonObj.date) };
} catch (e) {
logger.logToStderr(`Error reading build-time JSON file: ${e}`);
logger.error(`Error reading build-time JSON file: ${e}`);
// Fallback to executable modification time for reproducible builds
try {
const execPath = getCurrentExecutablePath();
const stats = fs.statSync(execPath);
const mtime = stats.mtime;

return {
date: mtime.toDateString(),
timestamp: mtime.toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'medium' }),
isoTimestamp: mtime.toISOString(),
unix: Math.floor(mtime.getTime() / 1000),
version: PackageJSON.version,
nodeVersion: process.version,
};
} catch (statErr) {
logger.logToStderr(`Error reading executable stats: ${statErr}`);
}
}
return undefined;
};
Expand Down