Skip to content

Test

Test #12352

Workflow file for this run

name: Test
on:
push:
branches:
# Do not build PRs twice
- 'master'
paths:
- 'data/**'
pull_request:
paths:
- 'data/**'
# Merges by a workflow (auto-merge in publish-pr-screenshot.yml) do not
# trigger push runs, so it starts this with the merge commit
workflow_dispatch:
inputs:
commit:
description: 'Commit on master whose changed files in data/ to test'
required: false
files:
description: 'Or: names of entries in data/ to re-test, separated by blanks (e.g. to regenerate database/)'
required: false
# A new push to a PR makes the previous test run obsolete
concurrency:
group: test-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
env:
DISPLAY: :99
IS_PULLREQUEST: ${{ github.event_name == 'pull_request' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WORKER_TIMEOUT: 600 # seconds per app; must stay below the job's timeout-minutes
jobs:
test:
# Pushes (and manual runs) write database/ and commit it: only here. In a
# fork, a push to its master would commit "Add automatically parsed data"
# to the fork, and a PR from that branch would carry the commit (#3948).
# PRs are tested wherever they are opened.
if: github.event_name == 'pull_request' || github.repository == 'AppImage/appimage.github.io'
runs-on: ubuntu-22.04
timeout-minutes: 20
permissions:
actions: write
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# https://github.com/actions/checkout#Fetch-all-history-for-all-tags-and-branches
# According to the docs for checkout@v2, Only a single commit is fetched by default, but
# providing a fetch-depth of 0 should fetch all history.
with:
fetch-depth: 0
- name: Check that the pull request changes exactly one file in data/
# Not for branches in this repository, so that maintainers can test
# changes to the test together with a change in data/
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
run: |
CHANGED=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD")
if [ "$(echo "$CHANGED" | grep -c .)" -ne 1 ] || ! echo "$CHANGED" | grep -qxE 'data/[^/]+' ; then
{
echo "ERROR: A pull request must change exactly one file in data/ and nothing else, but this one changes:"
echo "$CHANGED" | sed 's/^/ /'
} | tee log.txt
exit 1
fi
- name: Configure APT Packages
uses: awalsh128/cache-apt-pkgs-action@v1.4.3
# This action handles caching APT packages, and leaves the option for package configuration scripts to be run if needed,
# saving a boat-load of time, if needed bump the action version.
with:
packages: sudo imagemagick libasound2-dev pulseaudio-utils alsa-utils alsa-oss libjack0 desktop-file-utils xmlstarlet xsel xterm xvfb icewm x11-utils x11-apps netpbm xdotool libgl1-mesa-dri libgl1-mesa-dev mesa-utils libosmesa6 libsdl1.2-dev libsdl2-2.0-0 fonts-wqy-microhei libfile-mimeinfo-perl tesseract-ocr tesseract-ocr-eng tesseract-ocr-chi-sim tesseract-ocr-ara tesseract-ocr-rus tesseract-ocr-ell libfuse2 stalonetray
version: 1.5
# Bump me ^ if you have added any new packages
- name: Cache test dependencies
uses: actions/cache@v4
with:
path: deps
key: test-deps-${{ hashFiles('code/fetch-deps.sh') }}
- name: Fetch test dependencies
run: |
bash code/fetch-deps.sh # No-op when restored from the cache
cp -a deps/. . # worker.sh expects them in the working directory
- name: Install dependencies
shell: bash
run: |
# set -e
sudo bash code/prep-dummy-soundcard.sh
# TODO Cache gem and npm packages
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: Main test
id: main
env:
DISPATCH_COMMIT: ${{ inputs.commit }}
DISPATCH_FILES: ${{ inputs.files }}
run: |
touch .test-start # Screenshots newer than this are from this run
mkdir $HOME/.icewm/
cat > $HOME/.icewm/preferences <<EOF
ShowTaskBar = 0
TaskBarAutoHide = 1
TaskBarShowWorkspaces = 0
TaskBarShowAllWindows = 0
TaskBarShowClock = 0
TaskBarShowMailboxStatus = 0
TaskBarShowCPUStatus = 0
TaskBarShowWindowListMenu = 0
EOF
Xvfb :99 -screen 0 800x600x24 >/dev/null 2>&1 & # Need to set bit depth, otherwise get some black screenshots
# Files in data/ added or changed by this PR (or by this push)
if [ "$IS_PULLREQUEST" = true ] ; then
FILES=$(git diff --name-only --diff-filter=AMR "origin/${{ github.base_ref }}...HEAD" -- data/)
elif [ -n "$DISPATCH_FILES" ] ; then
FILES=""
for NAME in $DISPATCH_FILES ; do
[[ "$NAME" =~ ^[A-Za-z0-9._+-]+$ ]] && [ -f "data/$NAME" ] || { echo "Not an entry in data/: $NAME" ; exit 1 ; }
FILES="$FILES data/$NAME"
done
elif [ -n "$DISPATCH_COMMIT" ] ; then
[[ "$DISPATCH_COMMIT" =~ ^[0-9a-f]{7,40}$ ]] || { echo "Not a commit: $DISPATCH_COMMIT" ; exit 1 ; }
FILES=$(git diff --name-only --diff-filter=AMR "$DISPATCH_COMMIT~1" "$DISPATCH_COMMIT" -- data/)
elif [ -n "${{ github.event.before }}" ] && git cat-file -e "${{ github.event.before }}" 2>/dev/null ; then
FILES=$(git diff --name-only --diff-filter=AMR "${{ github.event.before }}" HEAD -- data/)
else
FILES=$(git diff --name-only --diff-filter=AMR HEAD~1 HEAD -- data/)
fi
echo "Changed files:"
echo "$FILES"
if [ -z "$FILES" ] ; then
# E.g. a PR that only removes an application
echo "No added or changed files in data/, nothing to test"
touch .nothing-tested
exit 0
fi
for FILE in $FILES; do
echo "$FILE"
# The timeout keeps a hanging application from running into the job
# timeout, which would cancel the job without a report on the PR
# Name rules are errors only for files a PR adds (existing entries may have older names)
STRICT=false
if [ "$IS_PULLREQUEST" = true ] && [ -n "$(git diff --name-only --diff-filter=A "origin/${{ github.base_ref }}...HEAD" -- "$FILE")" ] ; then
STRICT=true
fi
export STRICT
timeout --kill-after=30 "$WORKER_TIMEOUT" bash -e code/worker.sh "$(readlink -f "$FILE")" 2>&1 | tee -a log.txt
EXIT_CODE=${PIPESTATUS[0]} # bashism; get the return code of the leftmost element of the pipe
if [ $EXIT_CODE -eq 124 ] || [ $EXIT_CODE -eq 137 ] ; then
echo "ERROR: Timed out after $WORKER_TIMEOUT seconds while testing $FILE" | tee -a log.txt
fi
if [ $EXIT_CODE -ne 0 ]; then
echo "worker.sh exited with a non-zero code: $EXIT_CODE"
# Re-testing chosen entries: go on with the others
if [ -n "$DISPATCH_FILES" ] ; then
FAILED="${FAILED:-} $FILE"
# Drop what the failed file left in database/ and apps/, so that nothing commits it
git checkout -q -- database apps 2>/dev/null || true
git clean -fdq -- database apps
continue
fi
exit $EXIT_CODE
fi
done
killall Xvfb
if [ -n "${FAILED:-}" ] ; then
echo "Failed:$FAILED"
exit 1
fi
- name: Collect result for the PR comment
if: always() && github.event_name == 'pull_request'
run: |
# Read by publish-pr-screenshot.yml, which has the permissions to comment on PRs from forks
[ -e .nothing-tested ] && exit 0 # No artifact, no comment
mkdir -p pr-result
echo "${{ github.event.pull_request.number }}" > pr-result/pr-number
# If the screenshot's text is predominantly Chinese (check-screenshot.sh
# OCRs it), record it so publish-pr-screenshot.yml can invite the author
# to offer an English default instead of posting the usual report.
grep -q 'not in English but in Chinese script' log.txt && echo chinese > pr-result/language.txt || true
if [ "${{ steps.main.outcome }}" = success ] ; then
find database -type f -path '*/screenshot.png' -newer .test-start -print | while IFS= read -r screenshot ; do
appname=$(basename "$(dirname "$screenshot")" | tr -cd 'A-Za-z0-9._-')
cp "$screenshot" "pr-result/${appname}.png"
done
# For the screenshot-ok label: a screenshot, and code/check-screenshot.sh had no remarks
if ls pr-result/*.png >/dev/null 2>&1 && ! grep -qE '^(WARNING|ERROR): The (window|screenshot)' log.txt ; then
touch pr-result/screenshot-ok
fi
else
cp failure-screens/*.png pr-result/ 2>/dev/null || true
bash code/diagnose.sh --excerpt log.txt > pr-result/error.txt
bash code/diagnose.sh --labels log.txt > pr-result/labels.txt
[ -s pr-result/labels.txt ] || echo error-other > pr-result/labels.txt
fi
# Also on success, e.g., a helper failing because of a too new glibc
bash code/diagnose.sh log.txt > pr-result/hints.md
# Remarks about the name of the file in data/ and of the AppImage
grep -hE "^(ERROR|WARNING): ((File name|AppImage name) '|The names of the .* differ: )" log.txt | sort -u > pr-result/names.txt || true
# Notes about the name of the file in data/ (not warnings)
grep -h "^NOTE: File name '" log.txt | sort -u > pr-result/notes.txt || true
# Whether the AppImage needs a compatible C library on the host
grep -h '^X-AppImage-\(Libc\|Runtime\|Self-Contained\|Glibc-Required\)=' log.txt | sort -u > pr-result/libc.txt || true
ls -la pr-result/
- name: Upload result
if: always() && github.event_name == 'pull_request' && hashFiles('pr-result/**') != ''
uses: actions/upload-artifact@v4
with:
name: pr-result
path: pr-result/
retention-days: 7