Skip to content

Commit

Permalink
Using gnu-tar on macOS.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paebbels committed Jan 20, 2025
1 parent 7d24514 commit 4658ed8
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 49 deletions.
7 changes: 1 addition & 6 deletions .github/workflows/ArtifactsUpload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -470,13 +470,8 @@ jobs:
matrix:
os:
- {icon: '🐧', name: 'Ubuntu', image: 'ubuntu-24.04'}
- {icon: '🍏', name: 'macOS', image: 'macos-14' }
- {icon: '🪟', name: 'Windows', image: 'windows-2022'}
option:
- {can-fail: false}
include:
- {os: {icon: '🍏', name: 'macOS', image: 'macos-14'}, option: {can-fail: true}}

continue-on-error: ${{ matrix.option.can-fail }}

defaults:
run:
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,27 @@ jobs:

## Limitations of `tar`

This action uses `tar` as provided by the GitHub runner's operating system images.
This composite action uses `tar`/`gtar` (GNU tar) as provided by the GitHub runner's operating system images.

### On Linux and Windows (GNU tar)
### On Linux, macOS and Windows (GNU tar)

To ensure files starting with a dash aren't considered command line options to `tar`, `tar` is called with
`--verbatim-files-from` option.

To ensure files are extracted and assigned to the owner/group of the extracting user, options `--owner=root:0` and
`--group=root:0` are used when creating the tarball.

In case, parameter `include-hidden-files` isn't set, hidden files (dot-files) are removed from the tarball in a further
cleanup step using the `--delete` command. This step is needed to ensure compatibility with the original
`actions/upload-artifact` action provided by GitHub.

### On macOS (BSD tar)
### Alternative BSD tar on macOS

BSD tar has even worse limitations than GNU tar and offers fewer features. Thus, this composite action uses GNU tar on
macOS, too. Fortunately, GNU tar (`gtar`) is already preinstalled via homebrew on all macOS runner images provided by
GitHub. For more details on BSD tar expand the following collapsible section.

<details><summary>Unused BSD tar on macOS </summary>
⚠ BSD tar doesn't support a `--delete` option. Thus, hidden files (dot files) can't be removed (excluded) from tarballs.
Removing discovered hidden files afterward from created tarballs is used on runner OS providing GNU tar. This technique
can't be applied to BSD tar. [^2]
Expand All @@ -129,7 +137,7 @@ as a command line option.

To ensure files are extracted and assigned to the owner/group of the extracting user, options `--gname=root`, `--gid=0`,
`--uname=root` and `--uid=0` are used when creating the tarball.

</details>

## Dependencies

Expand Down
81 changes: 42 additions & 39 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,23 @@ runs:
ANSI_LIGHT_BLUE="\e[94m"
ANSI_NOCOLOR=$'\x1b[0m'
if ! [[ "${{ runner.os }}" == "macOS" || "${{ runner.os }}" == "Linux" || "${{ runner.os }}" == "Windows" ]]; then
printf "::error title=%s::%s\n" "pyTooling/upload-artifact" "Unsupported runner OS '${{ runner.os }}'."
exit 1
fi
if [[ "${{ inputs.investigate }}" == "true" ]]; then
printf "::group::${ANSI_LIGHT_BLUE}List all shell options via 'shopt' ...${ANSI_NOCOLOR}\n"
printf "::group::${ANSI_LIGHT_BLUE}%s${ANSI_NOCOLOR}\n" "List all shell options via 'shopt' ..."
shopt
printf "::endgroup::\n"
fi
if [[ "${{ runner.os }}" == "macOS" ]]; then
tarProg="gtar"
else
tarProg="tar"
fi
if [[ "${{ inputs.working-directory }}" != "" ]]; then
printf "%s" "Changing artifact root directory to '${{ inputs.working-directory }}' ... "
if [[ -d "${{ inputs.working-directory }}" ]]; then
Expand Down Expand Up @@ -190,48 +201,32 @@ runs:
done | sort | uniq
}
printf "::group::${ANSI_LIGHT_BLUE}List all pattern for 'tar' ...${ANSI_NOCOLOR}\n"
printf "::group::${ANSI_LIGHT_BLUE}List all pattern for '${tarProg}' ...${ANSI_NOCOLOR}\n"
while IFS=$'\r\n' read -r pattern; do
printf " %s\n" "${pattern}"
done <<<$(print_files_unique "${PATTERNS[@]}")
printf "::endgroup::\n"
printf "%s\n" "Checking tar ($(which tar)): ${ANSI_CYAN}$(tar --version | head -n 1)${ANSI_NOCOLOR}"
printf "%s" "Assemble OS specific tar command line options ... "
if [[ "${{ runner.os }}" == "macOS" ]]; then
printf "%s\n" "${ANSI_LIGHT_YELLOW}[macOS]${ANSI_NOCOLOR}"
# Options for BSD tar
tarOptions=("--gname=root" "--gid=0" "--uname=root" "--uid=0")
# Option to read filenames from file
filesFrom=("-T")
elif [[ "${{ runner.os }}" == "Linux" || "${{ runner.os }}" == "Windows" ]]; then
printf "%s\n" "${ANSI_LIGHT_GREEN}[${{ runner.os }}]${ANSI_NOCOLOR}"
# Options for GNU tar
tarOptions=("--owner=root:0" "--group=root:0")
printf "%s\n" "Checking ${tarProg} ($(which ${tarProg})): ${ANSI_CYAN}$(${tarProg} --version | head -n 1)${ANSI_NOCOLOR}"
printf "%s\n" "Assemble OS specific tar command line options ... ${ANSI_LIGHT_GREEN}[${{ runner.os }}]${ANSI_NOCOLOR}"
# Option to read filenames from file
filesFrom=("--verbatim-files-from" "--files-from")
else
printf "%s\n" "${ANSI_LIGHT_RED}[UNSUPPORTED]${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "pyTooling/upload-artifact" "Unsupported runner OS '${{ runner.os }}'."
exit 1
fi
# Options for GNU tar
tarOptions=("--owner=root:0" "--group=root:0")
# Option to read filenames from file
filesFrom=("--verbatim-files-from" "--files-from")
printf "%s" "Creating temporary tarball '${tarDirectory}${{ inputs.tarball-name }}' ... "
msg=$(tar -cf "${tarDirectory}${{ inputs.tarball-name }}" "${tarOptions[@]}" "${filesFrom[@]}" <(print_files_unique "${PATTERNS[@]}"))
msg=$(${tarProg} -cf "${tarDirectory}${{ inputs.tarball-name }}" "${tarOptions[@]}" "${filesFrom[@]}" <(print_files_unique "${PATTERNS[@]}"))
if [[ $? -ne 0 ]]; then
printf "%s\n" "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}"
printf "::error title=%s::%s\n" "pyTooling/upload-artifact" "tar: ${msg}"
printf "::error title=%s::%s\n" "pyTooling/upload-artifact" "${tarProg}: ${msg}"
exit 1
else
printf "%s\n" "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}"
fi
dirCount=$(tar -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -E '/$' | wc -l)
fileCount=$(tar -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -v -E '/$' | wc -l)
dirCount=$(${tarProg} -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -E '/$' | wc -l)
fileCount=$(${tarProg} -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -v -E '/$' | wc -l)
printf "%s\n" "${ANSI_CYAN}Collected items:${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_CYAN}Directories: ${dirCount}${ANSI_NOCOLOR}"
printf " %s\n" "${ANSI_CYAN}Files: ${fileCount}${ANSI_NOCOLOR}"
Expand Down Expand Up @@ -264,17 +259,19 @@ runs:
ANSI_LIGHT_BLUE="\e[94m"
ANSI_NOCOLOR=$'\x1b[0m'
if [[ "${{ runner.os }}" != "macOS" ]]; then
printf "::group::${ANSI_LIGHT_BLUE}Removing unwanted files from '${{ inputs.tarball-name }}' ...:${ANSI_NOCOLOR}\n"
while IFS=$'\r\n' read -r file; do
printf " %s\n" "${file}"
tar -vf "${{ inputs.tarball-name }}" --delete "${file}"
done <<<$(tar -tf "${{ inputs.tarball-name }}" | grep -E '^\.|/\.')
printf "::endgroup::\n"
if [[ "${{ runner.os }}" == "macOS" ]]; then
tarProg="gtar"
else
printf "::warning title=%s::%s\n" "pyTooling/upload-artifact" "macOS doesn't support removing hidden files."
tarProg="tar"
fi
printf "::group::${ANSI_LIGHT_BLUE}Removing unwanted files from '${{ inputs.tarball-name }}' ...:${ANSI_NOCOLOR}\n"
while IFS=$'\r\n' read -r file; do
printf " %s\n" "${file}"
${tarProg} -vf "${{ inputs.tarball-name }}" --delete "${file}"
done <<<$(${tarProg} -tf "${{ inputs.tarball-name }}" | grep -E '^\.|/\.')
printf "::endgroup::\n"
- name: List content of the generated tarball
id: investigate
if: inputs.mode == 'tar' && inputs.investigate == 'true'
Expand All @@ -290,11 +287,17 @@ runs:
ANSI_LIGHT_BLUE="\e[94m"
ANSI_NOCOLOR=$'\x1b[0m'
if [[ "${{ runner.os }}" == "macOS" ]]; then
tarProg="gtar"
else
tarProg="tar"
fi
printf "::group::${ANSI_LIGHT_BLUE}Content of '${{ inputs.tarball-name }}':${ANSI_NOCOLOR}\n"
tar -tvf "${{ inputs.tarball-name }}"
${tarProg} -tvf "${{ inputs.tarball-name }}"
printf "\n"
dirCount=$(tar -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -E '/$' | wc -l)
fileCount=$(tar -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -v -E '/$' | wc -l)
dirCount=$(${tarProg} -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -E '/$' | wc -l)
fileCount=$(${tarProg} -tf "${tarDirectory}${{ inputs.tarball-name }}" | grep -v -E '/$' | wc -l)
printf "%s\n" "${ANSI_CYAN}Directories: ${dirCount} Files: ${fileCount}${ANSI_NOCOLOR}"
printf "::endgroup::\n"
Expand Down

0 comments on commit 4658ed8

Please sign in to comment.