archive step: mtime git/epoch silently stamp 1980-01-01 in linked git worktrees
- Dominant language
- Go
- Stars
- 1.4k
- Forks
- 175
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 134
Description
The archive step's promise is that identical committed sources yield byte-identical archives on any machine. In a linked worktree (`git worktree add`) the promise quietly inverts: the same commit produces different bytes than a plain checkout, with no warning that anything degraded.
## Describe the Bug
With `mtime: git` (or `epoch`), building from a plain checkout stamps entries with real commit timestamps. Building the same commit from a linked worktree stamps every entry with the 1980-01-01 fallback, so the two archives differ byte-for-byte and any hash-based consumer (S3 etags, SSM package manifests, lockfiles) sees a phantom change.
### Expected Behavior
The docs scope the fallback to operating "outside a Git repository" or untracked paths. A linked worktree is inside a git repository (`git log` works normally there), so I expected commit timestamps, matching the plain checkout.
### Actual Behavior
Every entry gets the fallback. `unzip -l` on two builds of the same commit:
```
=== control: plain checkout ===
6 07-28-2026 17:48 a.sh
=== trial: linked worktree of the same commit ===
6 12-31-1979 18:00 a.sh
```
(The trial row is 1980-01-01 00:00 UTC rendered in local time.)
### Steps to Reproduce
Self-contained script and one fresh run (Atmos 1.224.1, darwin/arm64)
```bash
#!/usr/bin/env bash
# Repro: archive step mtime git/epoch silently fall back to 1980-01-01
# inside a linked git worktree, while a plain checkout of the same commit
# resolves real commit timestamps.
set -euo pipefail
ATMOS="${ATMOS:-atmos}"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
cd "$tmp"
"$ATMOS" version
git init -q repo
cd repo
mkdir src
printf 'hello\n' > src/a.sh
git add src/a.sh
git -c user.name=repro -c user.email=repro@example.invalid -c commit.gpgsign=false \
commit -qm 'add a.sh'
git log -1 --format='committed: %cI'
cat > atmos.yaml <<'YAML'
base_path: ""
components: { terraform: { base_path: "components" } }
stacks: { base_path: "stacks", included_paths: ["**/*"], name_pattern: "{stage}" }
workflows: { base_path: "workflows" }
YAML
mkdir -p workflows stacks
cat > workflows/repro.yaml <<'YAML'
workflows:
build:
steps:
- name: pkg
type: archive
source: src
destination: out.zip
format: zip
action: replace
mtime: git
YAML
echo "=== control: plain checkout ==="
"$ATMOS" workflow build -f repro
unzip -l out.zip | grep 'a.sh'
echo "=== trial: linked worktree of the same commit ==="
git worktree add -q ../wt HEAD
cp atmos.yaml ../wt/
cp -R workflows ../wt/
mkdir -p ../wt/stacks
cd ../wt
"$ATMOS" workflow build -f repro
unzip -l out.zip | grep 'a.sh'
```
Transcript:
```
👽 Atmos 1.224.1 on darwin/arm64
committed: 2026-07-28T17:48:06-05:00
=== control: plain checkout ===
6 07-28-2026 17:48 a.sh
=== trial: linked worktree of the same commit ===
6 12-31-1979 18:00 a.sh
```
### Mechanism
`newMtimeConfig` is deliberately fail-open: any error locating the repository or its history falls back to `mtimeFallbackEpoch`, which is the right call for temp build dirs and untracked files but also swallows this case. In a linked worktree `.git` is a gitdir pointer file rather than a directory; go-git's linked-worktree support looks partial (go-git/go-git#1812 reviewed the API for opening them), and somewhere along `PlainOpenWithOptions(DetectDotGit)` / `Worktree()` / `Head()` the resolution fails, so the fallback engages with no signal. This may be the archive-step sibling of the worktree gaps #1509 addresses for `describe affected`.
Relevant paths: `pkg/archive/mtime.go` (`newMtimeConfig`, `modTimeFor`, `mtimeFallbackEpoch`).
### Ask
Could `newMtimeConfig` resolve the gitdir pointer for linked worktrees, or at least log a warning when a `.git` entry exists but git resolution falls back to the epoch? The silent divergence is the painful part; a build that warns is easy to live with. Happy to follow up with a PR.
Environment: Atmos 1.224.1 (`pkg/archive/mtime.go` is unchanged on `main`), darwin/arm64, git version 2.55.0.
Contributor guide
Research direction
Read pkg/archive/mtime.go, especially newMtimeConfig, modTimeFor, and mtimeFallbackEpoch, then run the self-contained linked-worktree reproduction in the issue. Done means the same commit gets matching archive timestamps in a plain checkout and linked worktree, or the fallback produces a warning when Git resolution fails.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, go
- Domain
- build-system, cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 70/100