From e8788ea8b6fcf1be366fff3ac3f2329ce2d0e38a Mon Sep 17 00:00:00 2001 From: Asher Buk <2asherbuk@gmail.com> Date: Sun, 16 Nov 2025 02:23:52 +0200 Subject: [PATCH 1/3] replace Imgur with GitHub Release Assets for screenshot hosting Fixes #3548 ## Problem Imgur API returns "Too Many Requests" errors (code 1025), blocking successfully tested applications from being merged. ## Solution Replace Imgur with GitHub Release Assets: - Screenshots are uploaded to a draft release named 'ci-screenshots' - Inline images displayed in PR comments - Works for PRs from external contributors using workflow_run trigger --- .github/workflows/publish-pr-screenshot.yml | 95 +++++++++++++++++++++ .github/workflows/test.yml | 21 +++-- code/worker.sh | 2 - 3 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/publish-pr-screenshot.yml diff --git a/.github/workflows/publish-pr-screenshot.yml b/.github/workflows/publish-pr-screenshot.yml new file mode 100644 index 0000000000..5ed145e613 --- /dev/null +++ b/.github/workflows/publish-pr-screenshot.yml @@ -0,0 +1,95 @@ +name: Publish PR Screenshot + +on: + workflow_run: + workflows: ["Test"] + types: [completed] + +jobs: + publish: + if: > + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-latest + permissions: + actions: read + contents: write + pull-requests: write + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Extract PR number from event + id: extract-pr + shell: bash + run: | + PR_NUMBER=$(jq -r '.workflow_run.pull_requests[0].number // empty' "$GITHUB_EVENT_PATH") + echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_ENV + echo "PR_NUMBER=${PR_NUMBER}" + echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT + - name: Download screenshot artifacts + uses: dawidd6/action-download-artifact@v2 + with: + run_id: ${{ github.event.workflow_run.id }} + name: pr-screenshots + path: ./out + if_no_artifact_found: ignore + + - name: Prepare files + id: prep + shell: bash + run: | + set -e + mkdir -p upload + : > upload/list.txt + SHORT="${HEAD_SHA::8}" + COUNT=0 + FALLBACK="run-${{ github.event.workflow_run.id }}" + for f in out/database/*/screenshot.png; do + [ -f "$f" ] || continue + base="$(basename "$f")" + ext="${base##*.}" + appname="$(basename "$(dirname "$f")")" + # Produce deterministic upload name with app name + PN="${PR_NUMBER:-$FALLBACK}" + name="pr-${PN}-${SHORT}-${appname}.${ext}" + cp "$f" "upload/${name}" + echo "${name}" >> upload/list.txt + COUNT=$((COUNT+1)) + done + echo "COUNT=${COUNT}" >> $GITHUB_ENV + echo "count=${COUNT}" >> $GITHUB_OUTPUT + + - name: Ensure draft release exists + shell: bash + run: | + gh release view ci-screenshots >/dev/null 2>&1 || \ + gh release create ci-screenshots --draft -t "CI Screenshots" -n "Automated screenshots from PRs" + + - name: Upload assets + if: ${{ steps.prep.outputs.count != '0' }} + shell: bash + run: | + while IFS= read -r name; do + gh release upload ci-screenshots "upload/${name}" --clobber + done < upload/list.txt + + - name: Comment on PR with images + if: ${{ steps.prep.outputs.count != '0' && steps.extract-pr.outputs.pr_number != '' }} + shell: bash + run: | + repo="${{ github.repository }}" + { + echo "Automated screenshot(s) for PR #${PR_NUMBER} (commit ${HEAD_SHA}):" + echo "" + while IFS= read -r name; do + url="https://github.com/${repo}/releases/download/ci-screenshots/${name}" + echo "![Screenshot](${url})" + echo "" + done < upload/list.txt + } > comment.txt + gh api "repos/${repo}/issues/${PR_NUMBER}/comments" -F body=@comment.txt + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4db161533..22a3756984 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,5 @@ +name: Test + on: push: branches: @@ -19,6 +21,7 @@ jobs: runs-on: ubuntu-22.04 timeout-minutes: 10 permissions: + actions: write contents: write pull-requests: write steps: @@ -83,6 +86,15 @@ jobs: # xpra stop :99 killall Xvfb # bundle exec jekyll build # https://help.github.com/en/articles/viewing-jekyll-build-error-messages#configuring-a-third-party-service-to-display-jekyll-build-error-messages + - name: Upload screenshot artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: pr-screenshots + path: | + database/*/screenshot.png + if-no-files-found: ignore + retention-days: 7 - name: Check log if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'probonopd' shell: bash @@ -91,13 +103,12 @@ jobs: grep -r "Running as root without --no-sandbox is not supported" log.txt && MESSAGE="Pending #2563 (\`Running as root without --no-sandbox is not supported\`)." && exit 1 grep -r "version \`GLIBC_.*' not found" && MESSAGE="This was compiled on a too new system and hence cannot run on all still-supported versions of Ubuntu." && exit 1 echo "${MESSAGE}" - SCREENSHOT_URL=$(cat log.txt | grep -o -e "https://i.imgur.com.*.png") || true - if [[ -z "${SCREENSHOT_URL}" ]]; then - echo "No screenshot URL found." + COUNT=$(ls database/*/screenshot.png 2>/dev/null | wc -l) + if [[ "${COUNT}" -eq 0 ]]; then + echo "No screenshot file found." exit 1 fi - echo "SCREENSHOT_URL=$SCREENSHOT_URL" >> $GITHUB_ENV || true - echo "SCREENSHOT_URL: ${SCREENSHOT_URL}" || true + echo "Found ${COUNT} screenshot file(s)." # The following is disabled because it gives errors about missing permissions # whenever a PR is made by anyone but the repo maintainer # - name: Post Screenshot URL diff --git a/code/worker.sh b/code/worker.sh index 683232445e..b0bc3ca5ce 100644 --- a/code/worker.sh +++ b/code/worker.sh @@ -573,8 +573,6 @@ if [ "$IS_PULLREQUEST" = true ]; then cat "apps/${INPUTBASENAME}.md" || exit 1 cat "database/${INPUTBASENAME}/"*.desktop || exit 1 # Asterisk must not be inside quotes, https://travis-ci.org/AppImage/appimage.github.io/builds/360847207#L782 ls -lh "database/${INPUTBASENAME}/screenshot.png" || exit 1 - wget -q https://raw.githubusercontent.com/tremby/imgur.sh/1c64feeefb6590741eb3d034575f9c788469b0a8/imgur.sh - bash imgur.sh "database/${INPUTBASENAME}/screenshot.png" echo "" echo "We will assume the test is OK (a pull request event was triggered and the required files exist)." exit 0 From 36d2f39172b14c307a666795c71183d1d481725e Mon Sep 17 00:00:00 2001 From: Asher Buk <2asherbuk@gmail.com> Date: Sun, 16 Nov 2025 21:19:19 +0200 Subject: [PATCH 2/3] sanitize filenames for artifact upload - Add step to sanitize screenshot filenames before upload - Remove invalid characters (: * ? < > |) from app names - Fixes error with apps like 'fre:ac' containing colons - Update publish workflow to handle flat file structure --- .github/workflows/publish-pr-screenshot.yml | 7 +++---- .github/workflows/test.yml | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-pr-screenshot.yml b/.github/workflows/publish-pr-screenshot.yml index 5ed145e613..82942bb35d 100644 --- a/.github/workflows/publish-pr-screenshot.yml +++ b/.github/workflows/publish-pr-screenshot.yml @@ -48,14 +48,13 @@ jobs: SHORT="${HEAD_SHA::8}" COUNT=0 FALLBACK="run-${{ github.event.workflow_run.id }}" - for f in out/database/*/screenshot.png; do + for f in out/screenshots-upload/*.png; do [ -f "$f" ] || continue base="$(basename "$f")" - ext="${base##*.}" - appname="$(basename "$(dirname "$f")")" + appname="${base%.png}" # Remove .png extension to get app name # Produce deterministic upload name with app name PN="${PR_NUMBER:-$FALLBACK}" - name="pr-${PN}-${SHORT}-${appname}.${ext}" + name="pr-${PN}-${SHORT}-${appname}.png" cp "$f" "upload/${name}" echo "${name}" >> upload/list.txt COUNT=$((COUNT+1)) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 22a3756984..f1b812ed7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,6 +49,10 @@ jobs: sudo gem install dupervisor -v 1.0.5 # To convert ini to yaml files sudo npm install -g asar # to get pacakges.json from resources/app.asar for electron-builder applications # npm install -g @alexlafroscia/yaml-merge # to merge yaml files + - name: Mark screenshot start (PR only) + if: github.event_name == 'pull_request' + run: | + touch .screenshots_start - name: Main test # shell: bash run: | @@ -86,13 +90,26 @@ jobs: # xpra stop :99 killall Xvfb # bundle exec jekyll build # https://help.github.com/en/articles/viewing-jekyll-build-error-messages#configuring-a-third-party-service-to-display-jekyll-build-error-messages + - name: Prepare screenshots for upload + if: github.event_name == 'pull_request' + shell: bash + run: | + mkdir -p screenshots-upload + # only files modified during this run + while IFS= read -r screenshot; do + [ -f "$screenshot" ] || continue + appname=$(basename "$(dirname "$screenshot")") + # Sanitize filename: allow letters, digits, dot, underscore, hyphen + safe_name=$(echo "$appname" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9._-') + cp "$screenshot" "screenshots-upload/${safe_name}.png" + done < <(find database -type f -path '*/screenshot.png' -newer .screenshots_start -print) + ls -la screenshots-upload/ || true - name: Upload screenshot artifact if: github.event_name == 'pull_request' uses: actions/upload-artifact@v4 with: name: pr-screenshots - path: | - database/*/screenshot.png + path: screenshots-upload/*.png if-no-files-found: ignore retention-days: 7 - name: Check log From 3564a63d3fabcb1a14e8cc020e1ee12b17c9afb7 Mon Sep 17 00:00:00 2001 From: Asher Buk <2asherbuk@gmail.com> Date: Sun, 16 Nov 2025 23:10:00 +0200 Subject: [PATCH 3/3] ci: fix artifact path and publish release; guard workflow to upstream repo --- .github/workflows/publish-pr-screenshot.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-pr-screenshot.yml b/.github/workflows/publish-pr-screenshot.yml index 82942bb35d..a509ae0748 100644 --- a/.github/workflows/publish-pr-screenshot.yml +++ b/.github/workflows/publish-pr-screenshot.yml @@ -9,7 +9,8 @@ jobs: publish: if: > github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.event == 'pull_request' + github.event.workflow_run.event == 'pull_request' && + github.repository == 'AppImage/appimage.github.io' runs-on: ubuntu-latest permissions: actions: read @@ -48,7 +49,7 @@ jobs: SHORT="${HEAD_SHA::8}" COUNT=0 FALLBACK="run-${{ github.event.workflow_run.id }}" - for f in out/screenshots-upload/*.png; do + for f in out/*.png; do [ -f "$f" ] || continue base="$(basename "$f")" appname="${base%.png}" # Remove .png extension to get app name @@ -62,11 +63,12 @@ jobs: echo "COUNT=${COUNT}" >> $GITHUB_ENV echo "count=${COUNT}" >> $GITHUB_OUTPUT - - name: Ensure draft release exists + - name: Ensure release exists (published) shell: bash run: | gh release view ci-screenshots >/dev/null 2>&1 || \ - gh release create ci-screenshots --draft -t "CI Screenshots" -n "Automated screenshots from PRs" + gh release create ci-screenshots -t "CI Screenshots" -n "Automated screenshots from PRs" --prerelease + gh release edit ci-screenshots --draft=false - name: Upload assets if: ${{ steps.prep.outputs.count != '0' }}