diff --git a/.github/workflows/ArtifactsUpload.yml b/.github/workflows/ArtifactsUpload.yml index 716b991..feae1bf 100644 --- a/.github/workflows/ArtifactsUpload.yml +++ b/.github/workflows/ArtifactsUpload.yml @@ -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: diff --git a/README.md b/README.md index dbbedea..6ca4a3c 100644 --- a/README.md +++ b/README.md @@ -98,9 +98,9 @@ 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. @@ -108,9 +108,17 @@ To ensure files starting with a dash aren't considered command line options to ` 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. + +
Unused BSD tar on macOS ⚠ 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] @@ -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. - +
## Dependencies diff --git a/action.yml b/action.yml index 67bab4c..0d6ce75 100644 --- a/action.yml +++ b/action.yml @@ -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 @@ -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}" @@ -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' @@ -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"