Bladeren bron

fix(codex): make package script and its test portable beyond macOS/bsdtar

The packaging pipeline only worked on a Mac with default umask, for
three stacked reasons:

- The deterministic-metadata tar flags (--uid/--gid/--uname/--gname)
  are bsdtar spellings; GNU tar rejects them, so the tar.gz archive
  step died on Linux. Detect the tar flavor and use --owner=:0
  --group=:0 --numeric-owner on GNU tar, which writes byte-identical
  ustar headers (uid/gid 0, empty uname/gname).
- Staged file modes depended on two umasks canceling out: git archive
  masks entry modes with tar.umask (git default 0002 -> 775), and the
  unflagged tar extraction re-masked with the process umask (022 on
  macOS -> 755, but 002 elsewhere -> 775). Pin tar.umask=0022 on the
  archive call and extract with -p so staged modes are canonical
  755/644 on every machine.
- The test's timestamp assertion parsed bsdtar's -tv column layout and
  expected epoch 0 rendered in a US timezone ("Dec 31 1969"); GNU tar
  uses different columns and UTC hosts render "1970-01-01". Assert
  mtime == 0 via python3 tarfile instead, matching how the test
  already checks zip timestamps.

tests/codex/test-package-codex-plugin.sh now passes on Linux/GNU tar;
the bsdtar branch preserves the exact flags that passed on macOS.
Jesse Vincent 1 maand geleden
bovenliggende
commit
5d5b6567a8
2 gewijzigde bestanden met toevoegingen van 20 en 6 verwijderingen
  1. 13 4
      scripts/package-codex-plugin.sh
  2. 7 2
      tests/codex/test-package-codex-plugin.sh

+ 13 - 4
scripts/package-codex-plugin.sh

@@ -230,14 +230,16 @@ prepare_metadata_root() {
 
 METADATA_ROOT="$(prepare_metadata_root "$METADATA_SOURCE")"
 
-git -C "$REPO_ROOT" archive --format=tar "$REF" -- \
+# Pin tar.umask and extract with -p so staged modes are canonical 755/644
+# regardless of the builder's git config or process umask.
+git -C "$REPO_ROOT" -c tar.umask=0022 archive --format=tar "$REF" -- \
   .codex-plugin \
   CODE_OF_CONDUCT.md \
   LICENSE \
   README.md \
   assets \
   skills \
-  | tar -xf - -C "$STAGE"
+  | tar -xpf - -C "$STAGE"
 
 VERSION="$(jq -r '.version // empty' "$STAGE/.codex-plugin/plugin.json")"
 [[ -n "$VERSION" ]] || die "could not read version from .codex-plugin/plugin.json"
@@ -298,12 +300,19 @@ case "$FORMAT" in
     )
     ;;
   tar.gz)
-    # Match the prior official archive's deterministic tar entry metadata.
+    # Match the prior official archive's deterministic tar entry metadata:
+    # ustar entries with uid/gid 0 and empty uname/gname. GNU tar and bsdtar
+    # (macOS) spell those flags differently.
+    if tar --version 2>/dev/null | grep -q 'GNU tar'; then
+      TAR_METADATA_FLAGS=(--owner=:0 --group=:0 --numeric-owner)
+    else
+      TAR_METADATA_FLAGS=(--uid 0 --gid 0 --uname '' --gname '')
+    fi
     TZ=UTC find "$STAGE" -exec touch -t 197001010000 {} +
     (
       cd "$STAGE"
       rm -f "$OUTPUT"
-      COPYFILE_DISABLE=1 tar -cf - --no-recursion --format ustar --uid 0 --gid 0 --uname '' --gname '' -T "$ARCHIVE_LIST" |
+      COPYFILE_DISABLE=1 tar -cf - --no-recursion --format ustar "${TAR_METADATA_FLAGS[@]}" -T "$ARCHIVE_LIST" |
         gzip -9n >"$OUTPUT"
     )
     ;;

+ 7 - 2
tests/codex/test-package-codex-plugin.sh

@@ -210,8 +210,13 @@ 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, tarfile
+with tarfile.open(sys.argv[1]) as archive:
+    print(sorted({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"