diff --git a/scripts/package-codex-plugin.sh b/scripts/package-codex-plugin.sh index 00399f061b..2594fc10f6 100755 --- a/scripts/package-codex-plugin.sh +++ b/scripts/package-codex-plugin.sh @@ -294,7 +294,7 @@ case "$FORMAT" in ( cd "$STAGE" rm -f "$OUTPUT" - COPYFILE_DISABLE=1 zip -X -q - -@ <"$ARCHIVE_LIST" >"$OUTPUT" + COPYFILE_DISABLE=1 TZ=UTC zip -X -q - -@ <"$ARCHIVE_LIST" >"$OUTPUT" ) ;; tar.gz) diff --git a/tests/codex/test-package-codex-plugin.sh b/tests/codex/test-package-codex-plugin.sh index 62c73f1cc1..f3faab9602 100755 --- a/tests/codex/test-package-codex-plugin.sh +++ b/tests/codex/test-package-codex-plugin.sh @@ -135,6 +135,8 @@ echo "Codex package archive tests" metadata_source="$TEST_ROOT/metadata-source" archive="$TEST_ROOT/superpowers" +utc_archive="$TEST_ROOT/superpowers-utc.zip" +seoul_archive="$TEST_ROOT/superpowers-seoul.zip" tar_archive="$TEST_ROOT/superpowers.tar.gz" extracted="$TEST_ROOT/extracted" tar_extracted="$TEST_ROOT/tar-extracted" @@ -160,6 +162,26 @@ assert_contains "$output" "Archive:" "reports archive path" assert_contains "$output" "Format: zip" "reports default zip format" assert_contains "$output" "SHA-256:" "reports archive checksum" +if output="$(TZ=UTC "$SCRIPT_UNDER_TEST" --allow-dirty --metadata-source "$metadata_source" --output "$utc_archive" 2>&1)"; then + pass "package script writes ZIP archive under UTC" +else + fail "package script writes ZIP archive under UTC" + printf '%s\n' "$output" | sed 's/^/ /' +fi + +if output="$(TZ=Asia/Seoul "$SCRIPT_UNDER_TEST" --allow-dirty --metadata-source "$metadata_source" --output "$seoul_archive" 2>&1)"; then + pass "package script writes ZIP archive under Asia/Seoul" +else + fail "package script writes ZIP archive under Asia/Seoul" + printf '%s\n' "$output" | sed 's/^/ /' +fi + +if cmp -s "$utc_archive" "$seoul_archive"; then + pass "ZIP archives are byte-identical across time zones" +else + fail "ZIP archives are byte-identical across time zones" +fi + extract_archive "$archive" "$extracted" archive_paths="$(list_archive "$archive" | normalize_archive_paths)" @@ -210,8 +232,15 @@ assert_equals "$tar_archive_paths" "$archive_paths" "zip and tar.gz archives con tar_task_brief_mode="$(tar -tzvf "$tar_archive" skills/subagent-driven-development/scripts/task-brief | awk '{print $1}')" assert_equals "$tar_task_brief_mode" "-rwxr-xr-x" "tar.gz archive preserves executable script mode" -tar_metadata_times="$(tar -tzvf "$tar_archive" | awk '{print $6, $7, $8}' | sort -u)" -assert_equals "$tar_metadata_times" "Dec 31 1969" "tar.gz archive normalizes entry timestamps" +tar_metadata_times="$(python3 - "$tar_archive" <<'PY' +import sys +import tarfile + +with tarfile.open(sys.argv[1]) as archive: + print("\n".join(sorted({str(member.mtime) for member in archive.getmembers()}))) +PY +)" +assert_equals "$tar_metadata_times" "0" "tar.gz archive normalizes entry timestamps" metadata_archive="$TEST_ROOT/metadata-source.tar.gz" metadata_zip="$TEST_ROOT/metadata-source.zip"