helpers 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # Generate a reproducible archive from the content of a directory
  2. #
  3. # $1 : input directory
  4. # $2 : leading component in archive
  5. # $3 : ISO8601 date: YYYY-MM-DDThh:mm:ssZZ
  6. # $4 : output file
  7. # $5... : globs of filenames to exclude from the archive, suitable for
  8. # find's -path option, and relative to the input directory $1
  9. #
  10. # Notes :
  11. # - the timestamp is internally rounded to the highest entire second
  12. # less than or equal to the timestamp (i.e. any sub-second fractional
  13. # part is ignored)
  14. # - must not be called with CWD as, or below, the input directory
  15. # - some temporary files are created in CWD, and removed at the end
  16. #
  17. # Example:
  18. # $ find /path/to/temp/dir
  19. # /path/to/temp/dir/
  20. # /path/to/temp/dir/some-file
  21. # /path/to/temp/dir/some-dir/
  22. # /path/to/temp/dir/some-dir/some-other-file
  23. #
  24. # $ mk_tar_gz /path/to/some/dir \
  25. # foo_bar-1.2.3 \
  26. # 1970-01-01T00:00:00Z \
  27. # /path/to/foo.tar.gz \
  28. # '.git/*' '.svn/*'
  29. #
  30. # $ tar tzf /path/to/foo.tar.gz
  31. # foo_bar-1.2.3/some-file
  32. # foo_bar-1.2.3/some-dir/some-other-file
  33. #
  34. mk_tar_gz() {
  35. local in_dir="${1}"
  36. local base_dir="${2}"
  37. local date="${3}"
  38. local out="${4}"
  39. shift 4
  40. local glob tmp pax_options
  41. local -a find_opts
  42. for glob; do
  43. find_opts+=( -or -path "./${glob#./}" )
  44. done
  45. # Drop sub-second precision to play nice with GNU tar's valid_timespec check
  46. date="$(date -d "${date}" -u +%Y-%m-%dT%H:%M:%S+00:00)"
  47. pax_options="delete=atime,delete=ctime,delete=mtime"
  48. pax_options+=",exthdr.name=%d/PaxHeaders/%f,exthdr.mtime={${date}}"
  49. tmp="$(mktemp --tmpdir="$(pwd)")"
  50. pushd "${in_dir}" >/dev/null
  51. # Establish list
  52. find . -not -type d -and -not \( -false "${find_opts[@]}" \) >"${tmp}.list"
  53. # Sort list for reproducibility
  54. LC_ALL=C sort <"${tmp}.list" >"${tmp}.sorted"
  55. # Create POSIX tarballs, since that's the format the most reproducible
  56. tar cf - --transform="s#^\./#${base_dir}/#S" \
  57. --numeric-owner --owner=0 --group=0 --mtime="${date}" \
  58. --format=posix --pax-option="${pax_options}" \
  59. -T "${tmp}.sorted" >"${tmp}.tar"
  60. # Compress the archive
  61. gzip -6 -n <"${tmp}.tar" >"${out}"
  62. rm -f "${tmp}"{.list,.sorted,.tar}
  63. popd >/dev/null
  64. }
  65. # Keep this line and the following as last lines in this file.
  66. # vim: ft=bash