From 345df9570640a8fd3809b032cdae8602fa02df26 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 3 Dec 2024 19:17:44 +0100 Subject: [PATCH 1/5] Tar and upload files as an artifact. --- .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/modules.xml | 8 +++ .idea/upload-artifact.iml | 8 +++ .idea/workspace.xml | 7 ++ README.md | 5 ++ action.yml | 70 +++++++++++++++++++ 6 files changed, 104 insertions(+) create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/upload-artifact.iml create mode 100644 .idea/workspace.xml create mode 100644 action.yml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6f543d1 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/upload-artifact.iml b/.idea/upload-artifact.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/upload-artifact.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..d12cdb6 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/README.md b/README.md index f4d5959..992d0b9 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ # Preserving Artifact Upload Action + +**Based on:** + * [actions/upload-artifact#38 - upload-artifact does not retain artifact permissions (08. Sep. 2024)](https://github.com/actions/upload-artifact/issues/38#issuecomment-2336484584) + * [Gist: +rcdailey/download-tar-action.yml](https://gist.github.com/rcdailey/cd3437bb2c63647126aa5740824b2a4f) diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..cb144f4 --- /dev/null +++ b/action.yml @@ -0,0 +1,70 @@ +name: Upload (preserving) Artifact +description: Tarball files before uploading the artifact to preserve file privileges. +author: Patrick Lehmann (@Paebbels) + +inputs: + name: + description: "Name of the artifact." + type: string + path: + description: A list of file/directory pattern to be tarballed and uploaded as an artifact. + required: true + type: string +# if-no-files-found: +# retention-days: +# compression-level: + overwrite: + required: false + default: false + type: boolean +# include-hidden-files: + temp_directory: + required: false + default: '__upload_artifact__' + type: string + +outputs: + artifact-id: +# description: "Random number" + value: ${{ steps.upload.outputs.artifact-id }} + artifact-url: + value: ${{ steps.upload.outputs.artifact-url }} + +runs: + using: composite + steps: + - name: Copy files based on pattern into a temporary directory for later tarballing + shell: bash + run: | + mkdir -p "${{ inputs.temp_directory }}" + while IFS=$'\r\n' read -r pattern; do + # skip empty lines + [[ "$pattern" == "" ]] && continue + + cp -rv "${pattern}" "${{ inputs.temp_directory }}" + done <<<'${{ inputs.path }}' + + - name: Inspect temporary directory + shell: bash + run: | + tree "${{ inputs.temp_directory }}" + + - name: Create tarball + shell: bash + run: | + cd "${{ inputs.temp_directory }}" + tar -cvf artifact.tar * + + # https://github.com/actions/upload-artifact + - name: Upload artifact + uses: actions/upload-artifact@v4 + id: upload + with: + name: ${{ inputs.name }} + path: "${{ inputs.temp_directory }}/artifact.tar" + overwrite: ${{ inputs.overwrite }} + + - name: Remove temporary directory + shell: bash + run: | + rm -Rf "${{ inputs.temp_directory }}" From 532eeb8c76d3533db39fa7b14ffca85d3a317d55 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 3 Dec 2024 19:45:29 +0100 Subject: [PATCH 2/5] Added testcase. --- .github/workflows/ArtifactsUpload.yml | 96 +++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 .github/workflows/ArtifactsUpload.yml diff --git a/.github/workflows/ArtifactsUpload.yml b/.github/workflows/ArtifactsUpload.yml new file mode 100644 index 0000000..293dcee --- /dev/null +++ b/.github/workflows/ArtifactsUpload.yml @@ -0,0 +1,96 @@ +name: Verification of Preserving Artifact Upload + +on: + push: + workflow_dispatch: + +jobs: + Build: + name: Build something + runs-on: ubuntu-24.04 + + steps: + - name: 🖉 Build 1 + run: | + echo "Document 1 $(date --utc '+%d.%m.%Y - %H:%M:%S')" > document1.txt + echo "Analysis log $(date --utc '+%d.%m.%Y - %H:%M:%S')" > analysis.log + echo "Build log $(date --utc '+%d.%m.%Y - %H:%M:%S')" > build.log + + mkdir -p bin + echo "Program $(date --utc '+%d.%m.%Y - %H:%M:%S')" > bin/program.py + + mkdir -p lib + echo "Library $(date --utc '+%d.%m.%Y - %H:%M:%S')" > lib/common.py + + - name: 📤 Upload artifact + uses: pyTooling/upload-artifact@dev + with: + name: release + path: | + document1.txt + *.log + bin/ + lib/*.py +# if-no-files-found: error +# retention-days: 1 + + Verify: + name: Verify artifact content + runs-on: ubuntu-24.04 + + steps: + - name: 📥 Download artifact + uses: actions/download-artifact@v4 + with: + name: release + + - name: 📥 Download artifact + run: | + set +e + + ANSI_LIGHT_RED="\e[91m" + ANSI_LIGHT_GREEN="\e[92m" + ANSI_NOCOLOR="\e[0m" + + if [[ ! -f artifact.tar ]]; then + echo -e "${ANSI_LIGHT_RED}Artifact doesn't contain a tar file named 'artifact.tar'.${ANSI_NOCOLOR}" + exit 1 + fi + + - name: 📦 Extract tarball and check content + run: | + set +e + + ANSI_LIGHT_RED="\e[91m" + ANSI_LIGHT_GREEN="\e[92m" + ANSI_NOCOLOR="\e[0m" + + echo -n "Extracting tarball ... " + if [[ $? -ne 0 ]]; then + echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" + fi + + expected=( + "document1.txt" + "analysis.log" + "build.log" + "bin/program.py" + "lib/common.py" + ) + + errors=0 + for file in "${expected[@]}"; do + if [[ ! -f "$file" ]]; then + echo -e "${ANSI_LIGHT_RED}Extracted artifact doesn't contain file '${file}'.${ANSI_NOCOLOR}" + errors=$((errors + 1)) + fi + done + + echo "" + if [[ $errors -ne 0 ]]; then + echo -e "${ANSI_LIGHT_RED}Counted ${errors} errors.${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_GREEN}No errors found.${ANSI_NOCOLOR}" + fi From 680b06e8d20968945c3d29641cfbcc8d5b366142 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 3 Dec 2024 19:48:03 +0100 Subject: [PATCH 3/5] Fix bugs. --- .github/workflows/ArtifactsUpload.yml | 35 ++++++++++++++++++++++++--- action.yml | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ArtifactsUpload.yml b/.github/workflows/ArtifactsUpload.yml index 293dcee..a14cdff 100644 --- a/.github/workflows/ArtifactsUpload.yml +++ b/.github/workflows/ArtifactsUpload.yml @@ -6,7 +6,7 @@ on: jobs: Build: - name: Build something + name: Upload artifact runs-on: ubuntu-24.04 steps: @@ -22,6 +22,10 @@ jobs: mkdir -p lib echo "Library $(date --utc '+%d.%m.%Y - %H:%M:%S')" > lib/common.py + - name: 🔎 Inspect directory structure + run: | + tree . + - name: 📤 Upload artifact uses: pyTooling/upload-artifact@dev with: @@ -37,6 +41,8 @@ jobs: Verify: name: Verify artifact content runs-on: ubuntu-24.04 + needs: + - Build steps: - name: 📥 Download artifact @@ -44,7 +50,7 @@ jobs: with: name: release - - name: 📥 Download artifact + - name: 🔎 Inspect downloaded artifact content run: | set +e @@ -52,12 +58,20 @@ jobs: ANSI_LIGHT_GREEN="\e[92m" ANSI_NOCOLOR="\e[0m" + echo "List directory content" + ls -lAh . + echo "----------------------------------------" + + echo -n "Does tarball exist ... " if [[ ! -f artifact.tar ]]; then + echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Artifact doesn't contain a tar file named 'artifact.tar'.${ANSI_NOCOLOR}" exit 1 + else + echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" fi - - name: 📦 Extract tarball and check content + - name: 📦 Extract tarball run: | set +e @@ -66,12 +80,25 @@ jobs: ANSI_NOCOLOR="\e[0m" echo -n "Extracting tarball ... " + tar -xf artifact.tar if [[ $? -ne 0 ]]; then echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" fi - + + - name: 🔎 Inspect extracted tarball + run: | + tree . + + - name: 📋 Verify extracted tarball content + run: | + set +e + + ANSI_LIGHT_RED="\e[91m" + ANSI_LIGHT_GREEN="\e[92m" + ANSI_NOCOLOR="\e[0m" + expected=( "document1.txt" "analysis.log" diff --git a/action.yml b/action.yml index cb144f4..17874d7 100644 --- a/action.yml +++ b/action.yml @@ -41,7 +41,7 @@ runs: # skip empty lines [[ "$pattern" == "" ]] && continue - cp -rv "${pattern}" "${{ inputs.temp_directory }}" + cp -rv ${pattern} "${{ inputs.temp_directory }}" done <<<'${{ inputs.path }}' - name: Inspect temporary directory From afe24b3393db73727bcf1e928d444556ae6ac083 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Tue, 3 Dec 2024 20:17:46 +0100 Subject: [PATCH 4/5] Use tar directly without cp. --- .github/workflows/ArtifactsUpload.yml | 12 +++-- action.yml | 63 +++++++++++++++++---------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ArtifactsUpload.yml b/.github/workflows/ArtifactsUpload.yml index a14cdff..ec65a66 100644 --- a/.github/workflows/ArtifactsUpload.yml +++ b/.github/workflows/ArtifactsUpload.yml @@ -63,9 +63,9 @@ jobs: echo "----------------------------------------" echo -n "Does tarball exist ... " - if [[ ! -f artifact.tar ]]; then + if [[ ! -f __upload_artifact__.tar ]]; then echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" - echo -e "${ANSI_LIGHT_RED}Artifact doesn't contain a tar file named 'artifact.tar'.${ANSI_NOCOLOR}" + echo -e "${ANSI_LIGHT_RED}Artifact doesn't contain a tar file named '__upload_artifact__.tar'.${ANSI_NOCOLOR}" exit 1 else echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" @@ -80,7 +80,7 @@ jobs: ANSI_NOCOLOR="\e[0m" echo -n "Extracting tarball ... " - tar -xf artifact.tar + tar -xf __upload_artifact__.tar if [[ $? -ne 0 ]]; then echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" else @@ -109,7 +109,11 @@ jobs: errors=0 for file in "${expected[@]}"; do - if [[ ! -f "$file" ]]; then + echo -n "Checking '${file}' ... " + if [[ -f "$file" ]]; then + echo -e "${ANSI_LIGHT_GREEN}[PASSED]${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Extracted artifact doesn't contain file '${file}'.${ANSI_NOCOLOR}" errors=$((errors + 1)) fi diff --git a/action.yml b/action.yml index 17874d7..694418d 100644 --- a/action.yml +++ b/action.yml @@ -18,10 +18,10 @@ inputs: default: false type: boolean # include-hidden-files: - temp_directory: - required: false - default: '__upload_artifact__' + temp_tarball: type: string + required: false + default: '__upload_artifact__.tar' outputs: artifact-id: @@ -33,27 +33,33 @@ outputs: runs: using: composite steps: - - name: Copy files based on pattern into a temporary directory for later tarballing + - name: Create tarball from given file patterns + id: prepare shell: bash run: | - mkdir -p "${{ inputs.temp_directory }}" - while IFS=$'\r\n' read -r pattern; do - # skip empty lines - [[ "$pattern" == "" ]] && continue + set +e - cp -rv ${pattern} "${{ inputs.temp_directory }}" - done <<<'${{ inputs.path }}' + ANSI_LIGHT_RED="\e[91m" + ANSI_LIGHT_GREEN="\e[92m" + ANSI_NOCOLOR="\e[0m" - - name: Inspect temporary directory - shell: bash - run: | - tree "${{ inputs.temp_directory }}" + PATTERNS=() + while IFS=$'\r\n' read -r pattern; do + # skip empty or comment lines + [[ "${pattern}" == "" || "${pattern:0:1}" == "#" ]] && continue - - name: Create tarball - shell: bash - run: | - cd "${{ inputs.temp_directory }}" - tar -cvf artifact.tar * + PATTERNS+=($pattern) + done <<<'${{ inputs.path }}' + + # echo "PATTERNS: ${PATTERNS[@]}" + + echo -n "Creating temporary tarball ... " + tar -cf "${{ inputs.temp_tarball }}" "${PATTERNS[@]}" + if [[ $? -ne 0 ]]; then + echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" + fi # https://github.com/actions/upload-artifact - name: Upload artifact @@ -61,10 +67,23 @@ runs: id: upload with: name: ${{ inputs.name }} - path: "${{ inputs.temp_directory }}/artifact.tar" + path: ${{ inputs.temp_tarball }} overwrite: ${{ inputs.overwrite }} - - name: Remove temporary directory + - name: Remove temporary tarball + id: cleanup shell: bash run: | - rm -Rf "${{ inputs.temp_directory }}" + set +e + + ANSI_LIGHT_RED="\e[91m" + ANSI_LIGHT_GREEN="\e[92m" + ANSI_NOCOLOR="\e[0m" + + echo -n "Removing temporary tarball ... " + rm -f "${{ inputs.temp_tarball }}" + if [[ $? -ne 0 ]]; then + echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" + fi From 1d792fd007d7706b71eee2ba09ca1ef98af2612c Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Wed, 4 Dec 2024 01:05:52 +0100 Subject: [PATCH 5/5] Check file permissions (x-bit). --- .github/workflows/ArtifactsUpload.yml | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/ArtifactsUpload.yml b/.github/workflows/ArtifactsUpload.yml index ec65a66..dbd0ab2 100644 --- a/.github/workflows/ArtifactsUpload.yml +++ b/.github/workflows/ArtifactsUpload.yml @@ -18,9 +18,11 @@ jobs: mkdir -p bin echo "Program $(date --utc '+%d.%m.%Y - %H:%M:%S')" > bin/program.py + chmod u+x bin/program.py mkdir -p lib echo "Library $(date --utc '+%d.%m.%Y - %H:%M:%S')" > lib/common.py + chmod +x lib/common.py - name: 🔎 Inspect directory structure run: | @@ -125,3 +127,35 @@ jobs: else echo -e "${ANSI_LIGHT_GREEN}No errors found.${ANSI_NOCOLOR}" fi + + - name: 📋 Verify file permissions + run: | + set +e + + ANSI_LIGHT_RED="\e[91m" + ANSI_LIGHT_GREEN="\e[92m" + ANSI_NOCOLOR="\e[0m" + + expected=( + "bin/program.py" + "lib/common.py" + ) + + errors=0 + for file in "${expected[@]}"; do + echo -n "Checking '${file}' ... " + if [[ -x "$file" ]]; then + echo -e "${ANSI_LIGHT_GREEN}[PASSED]${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" + echo -e "${ANSI_LIGHT_RED}File '${file}' isn't executable.${ANSI_NOCOLOR}" + errors=$((errors + 1)) + fi + done + + echo "" + if [[ $errors -ne 0 ]]; then + echo -e "${ANSI_LIGHT_RED}Counted ${errors} errors.${ANSI_NOCOLOR}" + else + echo -e "${ANSI_LIGHT_GREEN}No errors found.${ANSI_NOCOLOR}" + fi