diff --git a/build-utils/travis-apidoc.sh b/.github/scripts/apidoc.sh similarity index 59% rename from build-utils/travis-apidoc.sh rename to .github/scripts/apidoc.sh index ecb6188d..5694c161 100755 --- a/build-utils/travis-apidoc.sh +++ b/.github/scripts/apidoc.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Process (API) docs after a successful build on Travis (via ../.travis.yml). +# Process (API) docs after a successful build on GitHub Actions. # # Updated/changed documentation for "master" is pushed to gh-pages. # In case of pull requests or other branches, it will get added to a separate branch. @@ -9,48 +9,49 @@ # NOTE: stdout/stderr might/should be discarded to not leak sensitive information. echo "Post-processing (API) documentation." -echo "TRAVIS_PULL_REQUEST: $TRAVIS_PULL_REQUEST" -echo "TRAVIS_BRANCH: $TRAVIS_BRANCH" +echo "PR Number: $PR_NUMBER" +echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF" -if [ -z "$GH_APIDOC_TOKEN" ]; then - echo "No GH_APIDOC_TOKEN available. Skipping." - exit +if [ -z "$APIDOC_TOKEN" ]; then + echo "No APIDOC_TOKEN available. Skipping." + exit fi -# NOTE: DO NOT USE "set -x", or anything else that would reveal GH_APIDOC_TOKEN! +# NOTE: DO NOT USE "set -x", or anything else that would reveal APIDOC_TOKEN! +# GitHub Actions does filter secrets, but extra caution won't hurt set -e set +x -# Display exit code in term of failure (probably due to 'set -x'). +# Display exit code in case of failure (probably due to 'set -x'). trap '[ "$?" = 0 ] || echo "EXIT CODE: $?"' EXIT -REPO_APIDOC="https://${GH_APIDOC_TOKEN}@github.com/awesomeWM/apidoc" +REPO_APIDOC="https://${APIDOC_TOKEN}@github.com/awesomeWM/apidoc" REPO_DIR="$PWD" -export GIT_AUTHOR_NAME="awesome-robot on Travis CI" +export GIT_AUTHOR_NAME="awesome-robot on GitHub Actions" export GIT_AUTHOR_EMAIL="awesome-robot@users.noreply.github.com" export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL" git clone --depth 1 --branch gh-pages "$REPO_APIDOC" build/apidoc \ - 2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g" + 2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g" cd build/apidoc # This will re-use already existing branches (updated PR). -if [ "$TRAVIS_PULL_REQUEST" != false ]; then - BRANCH="pr-$TRAVIS_PULL_REQUEST" -elif [ "$TRAVIS_BRANCH" != master ]; then - # Use merge-base of master in branch name, to keep different branches with - # the same name apart. - # shellcheck disable=SC2015 - BRANCH="$TRAVIS_BRANCH-$(cd "$REPO_DIR" \ - && git fetch --unshallow origin master \ - && git rev-parse --short "$(git merge-base HEAD FETCH_HEAD || true)" || true)" +if [ "$PR_NUMBER" != false ]; then + BRANCH="pr-$PR_NUMBER" +elif [ "$GITHUB_HEAD_REF" != master ]; then + # Use merge-base of master in branch name, to keep different branches with + # the same name apart. + # shellcheck disable=SC2015 + BRANCH="$GITHUB_HEAD_REF-$(cd "$REPO_DIR" \ + && git fetch --unshallow origin master \ + && git rev-parse --short "$(git merge-base HEAD FETCH_HEAD || true)" || true)" else BRANCH="gh-pages" fi if [ "$BRANCH" != "gh-pages" ]; then - git checkout -b "$BRANCH" "origin/${BRANCH}" || git checkout -b "$BRANCH" + git checkout -b "$BRANCH" "origin/${BRANCH}" 2> /dev/null || git checkout -b "$BRANCH" fi # Use a temporary branch for the two commits, which allows for a better UI. @@ -62,7 +63,7 @@ cat > ../doc/README.md <Release:" \ - -I "

API documentation for awesome, a highly configurable X window manager (version .*)\.

" \ - -x .git | patch -p1 + -I "

API documentation for awesome, a highly configurable X window manager (version .*)\.

" \ + -x .git | patch -p1 git add --all . if git diff --cached --exit-code --quiet; then - echo "Documentation has not changed." - exit + echo "Documentation has not changed." + exit fi LAST_COMMIT_MSG="$(cd "$REPO_DIR" && git log -1 --pretty=format:%s)" LAST_COMMIT="$(cd "$REPO_DIR" && git rev-parse --short HEAD)" # Commit the relevant changes. -COMMIT_MSG="Update docs for $AWESOME_VERSION via Travis +COMMIT_MSG="Update docs for $AWESOME_VERSION via Github Actions Last commit message: $LAST_COMMIT_MSG -Commits: https://github.com/awesomeWM/awesome/compare/${TRAVIS_COMMIT_RANGE/.../..} -Build URL: https://travis-ci.com/awesomeWM/awesome/builds/${TRAVIS_BUILD_ID}" +Commits: https://github.com/awesomeWM/awesome/compare/${GITHUB_BASE_REF}..${GITHUB_HEAD_REF} +Build URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" + git commit -m "[relevant] $COMMIT_MSG" # Commit the irrelevant changes. mv .git ../doc cd ../doc git add --all . -BOILERPLATE_FAILED=0 -git commit -m "[boilerplate] $COMMIT_MSG" || export BOILERPLATE_FAILED=1 - -# Reorder/swap commits, to have "relevant" after "boilerplate". -# This makes it show up earlier in the Github interface etc. -if [ "$BOILERPLATE_FAILED" == "0" ]; then +if git commit -m "[boilerplate] $COMMIT_MSG"; then + # Reorder/swap commits, to have "relevant" after "boilerplate". + # This makes it show up earlier in the Github interface etc. git tag _old git reset --hard HEAD~2 git cherry-pick _old _old~1 @@ -113,9 +112,9 @@ fi git checkout "$BRANCH" OLD_REV="$(git rev-parse --short HEAD)" -if [ "$TRAVIS_PULL_REQUEST" != false ]; then +if [ "$PR_NUMBER" != false ]; then MERGE_COMMIT_MSG="$COMMIT_MSG -Pull request: https://github.com/awesomeWM/awesome/pull/${TRAVIS_PULL_REQUEST}" +Pull request: https://github.com/awesomeWM/awesome/pull/${PR_NUMBER}" else PR_OR_ISSUE="$(echo "$COMMIT_MSG" | head -n 1 | grep -o '#[0-9]\+' || true)" if [ -n "$PR_OR_ISSUE" ]; then @@ -137,24 +136,24 @@ fi git merge --no-ff -m "$MERGE_COMMIT_MSG" merged-update NEW_REV="$(git rev-parse --short HEAD)" -git push origin "$BRANCH" 2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g" +git push origin "$BRANCH" 2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g" # Generate compare view links. # NOTE: use "\n" for line endings, not real ones for valid json! COMPARE_LINKS="Compare view: https://github.com/awesomeWM/apidoc/compare/${OLD_REV}...${NEW_REV}" COMPARE_LINKS="$COMPARE_LINKS\nRelevant changes: https://github.com/awesomeWM/apidoc/commit/${RELEVANT_REV}" if [ "$BRANCH" != "gh-pages" ]; then - COMPARE_LINKS="$COMPARE_LINKS\nComparison against master (gh-pages): https://github.com/awesomeWM/apidoc/compare/gh-pages...${NEW_REV}" + COMPARE_LINKS="$COMPARE_LINKS\nComparison against master (gh-pages): https://github.com/awesomeWM/apidoc/compare/gh-pages...${NEW_REV}" fi # shellcheck disable=SC2028 echo "Compare links:\n$COMPARE_LINKS" # Post a comment to the PR. -if [ "$TRAVIS_PULL_REQUEST" != false ]; then - curl -H "Authorization: token $GH_APIDOC_TOKEN" \ - -d "{\"body\": \"Documentation has been updated for this PR.\n\n$COMPARE_LINKS\"}" \ - "https://api.github.com/repos/awesomeWM/awesome/issues/${TRAVIS_PULL_REQUEST}/comments" \ - 2>&1 | sed "s/$GH_APIDOC_TOKEN/GH_APIDOC_TOKEN/g" +if [ "$PR_NUMBER" != false ]; then + curl -H "Authorization: token $APIDOC_TOKEN" \ + -d "{\"body\": \"Documentation has been updated for this PR.\n\n$COMPARE_LINKS\"}" \ + "https://api.github.com/repos/awesomeWM/awesome/issues/${PR_NUMBER}/comments" \ + 2>&1 | sed "s/$APIDOC_TOKEN/APIDOC_TOKEN/g" fi # vim: filetype=sh:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/.github/workflows/apidoc.yml b/.github/workflows/apidoc.yml new file mode 100644 index 00000000..d651403e --- /dev/null +++ b/.github/workflows/apidoc.yml @@ -0,0 +1,156 @@ +name: Update API docs + +on: + # Trigger on push to branches `master` and `3.5`. + push: + branches: [ master, 3.5 ] + # Trigger on pull request events for PRs that have `master` as their target branch + pull_request: + branches: [ master ] + + # Allow running the workflow manually + workflow_dispatch: + +defaults: + run: + # GitHub Actions adds `errexit` and `pipefail` by default, but we add `xtrace` + # to improve debugging some of the longer scripts. + shell: /bin/bash -o errexit -o pipefail -o xtrace {0} + +jobs: + main: + runs-on: ubuntu-20.04 + + env: + # Used for stable dates in documentation examples. See #2070. + SOURCE_DATE_EPOCH: "1893456000" + + steps: + # Create a cache invalidation key based on the current year + week. + # This way, packages will be checked for updates once every week. + - name: Get Date + id: get-date + run: echo "::set-output name=date::$(/bin/date -u "+%Y%W")" + + - name: Cache apt packages + id: cache-apt + uses: actions/cache@v2 + with: + path: /var/cache/apt/archives + # The trailing number serves as a version flag that can be incremented + # to invalidate the cache after changing the list of packages. + key: ${{ github.workflow }}-${{ runner.os }}-${{ steps.get-date.outputs.date }}-apt-3 + + - name: Download apt packages + if: steps.cache-apt.outputs.cache-hit != 'true' + run: | + sudo apt-get update + sudo apt-get install --download-only -y --no-install-recommends \ + asciidoctor \ + cmake \ + dbus-x11 \ + gettext \ + gir1.2-gtk-3.0 \ + gir1.2-pango-1.0 \ + git \ + libdbus-1-dev \ + libgirepository1.0-dev \ + libnotify-bin \ + libpango1.0-dev \ + libstartup-notification0-dev \ + libx11-xcb-dev \ + libxcb-cursor-dev \ + libxcb-icccm4-dev \ + libxcb-keysyms1-dev \ + libxcb-randr0-dev \ + libxcb-shape0-dev \ + libxcb-util0-dev \ + libxcb-xfixes0-dev \ + libxcb-xinerama0-dev \ + libxcb-xkb-dev \ + libxcb-xrm-dev \ + libxcb-xtest0-dev \ + libxdg-basedir-dev \ + libxkbcommon-dev \ + libxkbcommon-x11-dev \ + xutils-dev \ + lua-discount \ + liblua5.3-dev \ + lua5.3 + + - name: Install downloaded packages + run: | + sudo dpkg -i /var/cache/apt/archives/*.deb + + - name: Cache luarocks + id: cache-luarocks + uses: actions/cache@v2 + with: + path: /tmp/luarocks + key: ${{ github.workflow }}-${{ runner.os }}-luarocks-3.5.0 + + - name: Install fresh Luarocks + if: steps.cache-luarocks.outputs.cache-hit != 'true' + run: | + wget -O /tmp/luarocks.tar.gz https://github.com/luarocks/luarocks/archive/v3.5.0.tar.gz + mkdir /tmp/luarocks + tar -xf /tmp/luarocks.tar.gz -C /tmp/luarocks --strip-components=1 + cd /tmp/luarocks + ./configure + make build + sudo make install + + - name: Install cached Luarocks + if: steps.cache-luarocks.outputs.cache-hit == 'true' + run: | + cd /tmp/luarocks + sudo make install + + - name: Install rocks + run: | + sudo -H luarocks install lgi + sudo -H luarocks install ldoc + + - uses: actions/checkout@v2 + + - name: Build Awesome version string + run: | + # If this workflow is triggered by a pull request, we get a base branch. + # Otherwise, check if the current commit has a meaningful name. + if [ -n "${{ github.base_ref }}" ]; then + AWESOME_VERSION="${{ github.base_ref }}" + else + AWESOME_VERSION="$(git rev-parse --abbrev-ref HEAD)" + fi + + AWESOME_VERSION="${AWESOME_VERSION}-g$(git rev-parse --short HEAD)" + + if [ "${{ github.event_name }}" == "pull_request" && -n "${{ matrix.test_prev_commits }}" ]; then + AWESOME_VERSION="${AWESOME_VERSION}-PR${{ github.event.number }}" + elif [ "${{ github.event_name }}" == "pull_request" ]; then + AWESOME_VERSION="v9999-PR${{ github.event.number }}" + fi + + echo "AWESOME_VERSION=${AWESOME_VERSION}" >> ${GITHUB_ENV} + + - name: Create Build Environment + run: cmake -E make_directory -B "${{ github.workspace }}/build" + + - name: Configure CMake + run: | + cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \ + -DAWESOME_VERSION=$AWESOME_VERSION \ + -DGENERATE_DOC=ON \ + -DGENERATE_MANPAGES=ON \ + -DDO_COVERAGE=OFF + + - name: Build + run: cd "${{ github.workspace }}/build" && make + + - name: Run apidoc script + env: + PR_NUMBER: ${{ github.event.number }} + APIDOC_TOKEN: ${{ secrets.APIDOC_TOKEN || github.token }} + run: .github/scripts/apidoc.sh + +# vim: filetype=yaml:expandtab:shiftwidth=2:tabstop=2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..d0fe4b82 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,387 @@ +name: Build & Test + +on: + # Trigger on push to branches `master` and `3.5`. + push: + branches: [ master, 3.5 ] + # Trigger on pull request events for PRs that have `master` as their target branch + pull_request: + branches: [ master ] + + # Allow running the workflow manually + workflow_dispatch: + +defaults: + run: + # GitHub Actions adds `errexit` and `pipefail` by default, but we add `xtrace` + # to improve debugging some of the longer scripts. + shell: /bin/bash -o errexit -o pipefail -o xtrace {0} + +jobs: + main: + # The type of runner that the job will run on + runs-on: ubuntu-20.04 + name: ${{ matrix.test_name }} + + strategy: + # Let other jobs continue even if one of them fails + fail-fast: false + matrix: + include: + - test_name: "codecov-lua5.3" + lua_version: "5.3" + lua_name: "lua5.3" + coverage: "codecov" + manual_screens: true + + - test_name: "coveralls-lua5.2" + lua_version: "5.2" + lua_name: "lua5.2" + coverage: "coveralls" + manual_screens: true + + - test_name: "lua5.1" + lua_version: "5.1" + lua_name: "lua5.1" + + - test_name: "luajit" + lua_version: "5.1" + lua_name: "luajit" + lua_library: "/usr/lib/x86_64-linux-gnu/libluajit-5.1.so" + lua_include: "/usr/include/luajit-2.1" + luarocks_args: "--lua-suffix=jit-2.1.0-beta3" + + # Lua 5.2 with fixed lgi version and screen size not divisible by 2. + - test_name: "fixed-lgi-lua5.2" + lua_version: "5.2" + lua_name: "lua5.2" + lgi_version: "0.9.2" + tests_screen_size: "1921x1079" + check_qa: true + empty_theme_while_loading: true + test_prev_commits: true + + env: + LUA: ${{ matrix.lua_version }} + LUAINCLUDE: ${{ matrix.lua_include || format('/usr/include/lua{0}', matrix.lua_version) }} + LUALIBRARY: ${{ matrix.lua_library || format('/usr/lib/x86_64-linux-gnu/liblua{0}.so', matrix.lua_version) }} + TESTS_SCREEN_SIZE: ${{ matrix.tests_screen_size }} + TEST_TIMEOUT: '100' + CI: 'true' + + steps: + # Create a cache invalidation key based on the current year + week. + # This way, packages will be checked for updates once every week. + - name: Get Date + id: get-date + run: echo "::set-output name=date::$(/bin/date -u "+%Y%W")" + + - name: Cache apt packages + id: cache-apt + uses: actions/cache@v2 + with: + path: /var/cache/apt/archives + # The trailing number serves as a version flag that can be incremented + # to invalidate the cache after changing the list of packages. + key: ${{ github.workflow }}-${{ runner.os }}-${{ steps.get-date.outputs.date }}-apt-4 + + - name: Download apt packages + if: steps.cache-apt.outputs.cache-hit != 'true' + run: | + sudo apt-get update + sudo apt-get install --download-only -y --no-install-recommends \ + asciidoctor \ + cmake \ + dbus-x11 \ + gettext \ + gir1.2-gtk-3.0 \ + gir1.2-pango-1.0 \ + git \ + libdbus-1-dev \ + libgirepository1.0-dev \ + libnotify-bin \ + libpango1.0-dev \ + libstartup-notification0-dev \ + libx11-xcb-dev \ + libxcb-cursor-dev \ + libxcb-icccm4-dev \ + libxcb-keysyms1-dev \ + libxcb-randr0-dev \ + libxcb-shape0-dev \ + libxcb-util0-dev \ + libxcb-xfixes0-dev \ + libxcb-xinerama0-dev \ + libxcb-xkb-dev \ + libxcb-xrm-dev \ + libxcb-xtest0-dev \ + libxdg-basedir-dev \ + libxkbcommon-dev \ + libxkbcommon-x11-dev \ + wmctrl \ + x11-apps \ + xcb-proto \ + xdotool \ + xorg \ + xserver-xephyr \ + xterm \ + xutils-dev \ + xvfb \ + zsh \ + lua-discount + + - name: Install downloaded packages + run: sudo dpkg -i /var/cache/apt/archives/*.deb + + - name: Install Lua packages + run: | + if [ "${{ matrix.lua_name }}" = "luajit" ]; then + sudo apt-get install libluajit-5.1-dev luajit + fi + sudo apt-get install liblua${{ matrix.lua_version }}-dev lua${{ matrix.lua_version }} + + - name: Cache luarocks + id: cache-luarocks + uses: actions/cache@v2 + with: + path: /tmp/luarocks + # The build input for luarocks changes per test, so we need separate caches + key: ${{ github.workflow }}-${{ runner.os }}-${{ matrix.test_name }}-luarocks-3.5.0 + + - name: Install fresh Luarocks + if: steps.cache-luarocks.outputs.cache-hit != 'true' + run: | + wget -O /tmp/luarocks.tar.gz https://github.com/luarocks/luarocks/archive/v3.5.0.tar.gz + mkdir /tmp/luarocks + tar -xf /tmp/luarocks.tar.gz -C /tmp/luarocks --strip-components=1 + cd /tmp/luarocks + ./configure --lua-version=${{ matrix.lua_version }} --with-lua-include=${LUAINCLUDE} ${{ matrix.luarocks_args }} + make build + sudo make install + + - name: Install cached Luarocks + if: steps.cache-luarocks.outputs.cache-hit == 'true' + run: | + cd /tmp/luarocks + sudo make install + + - name: Cache xcb-errors + id: cache-xcb-errors + uses: actions/cache@v2 + with: + path: /tmp/xcb-errors + key: ${{ github.workflow }}-${{ runner.os }}-xcb-errors-1.0 + + - name: Install fresh xcb-errors + if: steps.cache-xcb-errors.outputs.cache-hit != 'true' + run: | + git clone --recursive --depth 1 --branch 1.0 https://gitlab.freedesktop.org/xorg/lib/libxcb-errors.git /tmp/xcb-errors + cd /tmp/xcb-errors + ./autogen.sh --prefix=/usr + make + sudo make install + + - name: Install cached xcb-errors + if: steps.cache-xcb-errors.outputs.cache-hit == 'true' + run: | + cd /tmp/xcb-errors + sudo make install + + - name: Install rocks + run: | + sudo -H luarocks install lgi ${{ matrix.lgi_version }} + sudo -H luarocks install ldoc + sudo -H luarocks install busted + + - name: Install QA check rocks + if: matrix.check_qa + run: | + sudo -H luarocks install luacheck + sudo -H luarocks install depgraph + + - name: Install cluacov rock + if: matrix.coverage + run: sudo -H luarocks install cluacov + + - name: Install coveralls rock + if: matrix.coverage == 'coveralls' + run: sudo -H luarocks install luacov-coveralls + + - name: Install codecov.io uploader + if: matrix.coverage == 'codecov' + run: wget -O /tmp/codecov-bash https://codecov.io/bash + + # Check out repository to ${{ github.workspace }} + # Automatically picks the current branch/PR + - uses: actions/checkout@v2 + + - name: Create Build Environment + run: cmake -E make_directory -B "${{ github.workspace }}/build" + + - name: Build Awesome version string + run: | + # If this workflow is triggered by a pull request, we get a base branch. + # Otherwise, check if the current commit has a meaningful name. + if [ -n "${{ github.base_ref }}" ]; then + AWESOME_VERSION="${{ github.base_ref }}" + else + AWESOME_VERSION="$(git rev-parse --abbrev-ref HEAD)" + fi + + AWESOME_VERSION="${AWESOME_VERSION}-g$(git rev-parse --short HEAD)" + + if [ "${{ github.event_name }}" == "pull_request" && -n "${{ matrix.test_prev_commits }}" ]; then + AWESOME_VERSION="${AWESOME_VERSION}-PR${{ github.event.number }}" + elif [ "${{ github.event_name }}" == "pull_request" ]; then + AWESOME_VERSION="v9999-PR${{ github.event.number }}" + fi + + echo "AWESOME_VERSION=${AWESOME_VERSION}" >> ${GITHUB_ENV} + + - name: Configure CMake + run: | + cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \ + -DAWESOME_VERSION=$AWESOME_VERSION \ + -DLUA_LIBRARY=$LUALIBRARY \ + -DLUA_INCLUDE_DIR=$LUAINCLUDE \ + -DGENERATE_DOC=OFF \ + -DGENERATE_MANPAGES=OFF \ + -DDO_COVERAGE=${{ matrix.coverage }} \ + -DTEST_MANUAL_SCREENS=${{ matrix.manual_screens }} \ + -DSTRICT_TESTS=true \ + -DCMAKE_C_FLAGS="-Werror" + + # Break beautiful so that trying to access the theme before `beautiful.init()` causes an error + - name: Patch beautiful for an empty default theme + if: matrix.empty_theme_while_loading + run: | + sed -i -e 's/theme = {}/theme = setmetatable({}, { __index = function() error("May not access theme before beautiful.init()") end })/' lib/beautiful/init.lua + grep -q 'May not access' lib/beautiful/init.lua + + - name: Build + run: cd "${{ github.workspace }}/build" && make + + # Executable needs to be run once to provide coverage results + - name: Install and run + run: | + cd "${{ github.workspace }}/build" + sudo make install + awesome --version + + - name: Run integration tests + run: cd "${{ github.workspace }}/build" && make check-integration + + - name: Run unit tests + run: cd "${{ github.workspace }}/build" && make check-unit + + - name: Run examples tests + if: matrix.coverage + run: cd "${{ github.workspace }}/build" && make check-examples + + - name: Run requires tests + if: matrix.coverage + run: cd "${{ github.workspace }}/build" && make check-requires + + - name: Run themes tests + run: cd "${{ github.workspace }}/build" && make check-themes + + - name: Upload Lua code coverage report + if: matrix.coverage == 'codecov' + run: | + luacov + bash /tmp/codecov-bash -f build/luacov.report.out -X gcov -X coveragepy -F luacov + + - name: Upload C code coverage report + if: matrix.coverage == 'codecov' + run: | + # Report coverage for each .gcno file separately. + # gcov will create .gcov files for the same source (e.g. for + # globalconf.h from awesome.c.gcno and event.c.gcno). + i=0 + cd "${{ github.workspace }}/build" + find -path "*/lgi-check.dir" -prune -o \( -name '*.gcno' -print \) | while read -r gcno; do + gcov -pb "$gcno" + + mkdir gcov.$(( ++i )) + mv *.gcov "gcov.$i" + + # Delete any files for /usr. + # They are not relevant and might cause "Invalid path part" errors + # with Code Climate. + find "gcov.$i" -maxdepth 1 -type f -name '#usr#*.gcov' -delete + done + + # Upload to Codecov. + bash /tmp/codecov-bash -X gcov -X coveragepy -F gcov + + - name: Merge coverage + if: matrix.coverage == 'coveralls' + # Coveralls doesn't support GitHub Actions with `service_name` + `service_job_id` (yet?), + # but `luacov-coveralls` behaves as if they did by default. + # We need to explicitly remove the `service_name`, so that Coveralls uses the + # `repo_token` instead. + # See "Referencing a repository": https://docs.coveralls.io/api-introduction + run: | + luacov-coveralls \ + --verbose \ + --merge \ + --repo-token ${{ secrets.COVERALLS_REPO_TOKEN }} \ + --service-name "" + + # `check-qa` is the only test that doesn't get a coverage report, so it has to run after all of that. + - name: Run qa tests + if: matrix.check_qa + run: cd "${{ github.workspace }}/build" && make check-qa + + # Check each commit separately (to make git-bisect less annoying). + - name: Test previous commits + if: matrix.test_prev_commits && github.event_name == 'pull_request' + run: | + # `actions/checkout` creates a shallow repo (`--depth 1`) by default, + # which is fine for everything up until now. But we need individual commits now. + # And we only want to unshallow now, to not slow down the checkout for other jobs. + git fetch --unshallow + + rev_list="$(git rev-list --bisect-all origin/${{ github.base_ref }}..origin/${{ github.head_ref }})" + commits="$(echo "$rev_list" | grep -v 'dist=0' | cut -d' ' -f 1)" + n="$(echo "$commits" | wc -l)" + + echo "Testing $n commits:" + echo "$commits" | xargs -I{} git log -1 --pretty='%h %s' {} + + failed="" + for commit in $commits; do + echo "Testing commit $commit" + + # Some files are updated when compiling... + git checkout --force "$commit" + git show --stat --oneline + + cmake -S ${{ github.workspace }} -B "${{ github.workspace}}/build" \ + -DAWESOME_VERSION=$AWESOME_VERSION \ + -DLUA_LIBRARY=$LUALIBRARY \ + -DLUA_INCLUDE_DIR=$LUAINCLUDE \ + -DGENERATE_DOC=OFF \ + -DGENERATE_MANPAGES=OFF \ + -DDO_COVERAGE=OFF \ + -DTEST_MANUAL_SCREENS=${{ matrix.manual_screens }} \ + -DSTRICT_TESTS=true \ + -DCMAKE_C_FLAGS="-Werror" + + cd "${{ github.workspace}}/build" + if ! ( make && make check-unit ); then + failed="$failed $commit" + fi + done + + git checkout --quiet --force ${{ github.head_ref }} + if [ -n "$failed" ]; then + echo "Checks failed for these commits:" + + for c in $failed; do + git show --no-patch --pretty="%h %s (%an, %ad)" "$c" + done + + exit 1 + fi + +# vim: filetype=yaml:expandtab:shiftwidth=2:tabstop=2 diff --git a/.luacheckrc b/.luacheckrc index 6cbe09f9..3db9b50f 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -49,7 +49,7 @@ globals = { -- Enable cache (uses .luacheckcache relative to this rc file). cache = true --- Do not enable colors to make the Travis CI output more readable. +-- Do not enable colors to make the CI output more readable. color = false -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/.mergify.yml b/.mergify.yml index 0e35a72b..abb47aff 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -3,7 +3,8 @@ pull_request_rules: conditions: - label!=no-mergify - '#approved-reviews-by>=2' - - status-success=Travis CI - Pull Request + - status-success=Build & Test + - status-success=Update API docs - status-success=codecov/patch - status-success=coverage/coveralls actions: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 83212055..00000000 --- a/.travis.yml +++ /dev/null @@ -1,342 +0,0 @@ -dist: xenial -language: c - -# Build only master and stable branches. Other branches go through PRs. -branches: - only: - - master - - 3.5 - -addons: - apt: - packages: &BASE_PACKAGES - # Build dependencies. - # See also `apt-cache showsrc awesome | grep -E '^(Version|Build-Depends)'`. - - libnotify-bin - - libcairo2-dev - - gir1.2-gtk-3.0 - - libpango1.0-dev - - libxcb-xtest0-dev - - libxcb-icccm4-dev - - libxcb-randr0-dev - - libxcb-keysyms1-dev - - libxcb-xinerama0-dev - - libdbus-1-dev - - libxdg-basedir-dev - - libstartup-notification0-dev - - imagemagick - - libxcb1-dev - - libxcb-shape0-dev - - libxcb-util0-dev - - libx11-xcb-dev - - libxcb-cursor-dev - - libxcb-xkb-dev - - libxcb-xfixes0-dev - - libxkbcommon-dev - - libxkbcommon-x11-dev - # Deps for tests. - - dbus-x11 - - xterm - - xdotool - - wmctrl - - xterm - - xvfb - - zsh - - x11-apps - # Need xorg-macros - - xutils-dev - # lgi. - - gir1.2-pango-1.0 - - libgirepository1.0-dev - -jobs: - include: - - env: LUA=5.2 LUANAME=lua5.2 DO_COVERAGE=coveralls MANUAL_SCREENS=1 - addons: - apt: - packages: - - *BASE_PACKAGES - - liblua5.2-dev - - lua5.2 - - env: LUA=5.3 LUANAME=lua5.3 DO_COVERAGE=codecov MANUAL_SCREENS=1 - addons: - apt: - packages: - - *BASE_PACKAGES - - liblua5.3-dev - - lua5.3 - - env: LUA=5.1 LUANAME=lua5.1 BUILD_IN_DIR=/tmp/awesome-build WITH_XCB_ERRORS=yes - addons: - apt: - packages: - - *BASE_PACKAGES - - liblua5.1-dev - - lua5.1 - # For xcb-errors - - xcb-proto - - env: LUA=5.1 LUANAME=luajit-2.0 LUALIBRARY=/usr/lib/x86_64-linux-gnu/libluajit-5.1.so LUAROCKS_ARGS=--lua-suffix=jit-2.0.4 - addons: - apt: - packages: - - *BASE_PACKAGES - - libluajit-5.1-dev - - luajit - # Lua 5.2 with older lgi and screen size not divisible by 2. - # SOURCE_DATE_EPOCH: used for stable dates during build. - - env: LUA=5.2 LUANAME=lua5.2 LGIVER=0.8.0 TESTS_SCREEN_SIZE=1921x1079 BUILD_APIDOC=true DO_CHECKQA=1 EMPTY_THEME_WHILE_LOADING=1 SOURCE_DATE_EPOCH=1893456000 TEST_PREV_COMMITS=1 - addons: - apt: - packages: - - *BASE_PACKAGES - - liblua5.2-dev - - lua5.2 - - lua-discount - -env: - global: - # Secure GH_APIDOC_TOKEN to push to awesomeWM/apidoc. - - secure: "R/HYDclnws1I1+v9Yjt+RKa4CsFhbBT9tiwE3EfPhEj2KCYX4sFRMxuZvLf5sq0XWdrQaPhQ54fgAZGr3f054JKRXcTB0g9J6nhSHz9kIjPh446gafUhEeDQcZRwM/MeCWiwFIkiZm6smYoDFE9JTWu6quNV+lQ4kcVDOp2ibEc=" - -before_install: - - if [ "$BUILD_APIDOC" = true ] && [ -n "$DO_COVERAGE" ]; then echo "BUILD_APIDOC and DO_COVERAGE are not meant to be used together." >&2; exit 1; fi - - if [ -z $LUAINCLUDE ]; then LUAINCLUDE=/usr/include/${LUANAME}; fi - - if [ -z $LUALIBRARY ]; then LUALIBRARY=/usr/lib/x86_64-linux-gnu/lib${LUANAME}.so; fi - - cmake --version - -install: - - sudo gem install asciidoctor - - | - # Install xcb-util-xrm. - set -e - (git clone --depth 1 --recursive https://github.com/Airblader/xcb-util-xrm.git /tmp/xcb-util-xrm - cd /tmp/xcb-util-xrm - ./autogen.sh --prefix=/usr - make && sudo make install) - - # Install xcb-errors if needed - - | - set -e - if [[ "$WITH_XCB_ERRORS" == "yes" ]]; then - git clone --depth 1 --recursive https://gitlab.freedesktop.org/xorg/lib/libxcb-errors.git /tmp/xcb-errors - (cd /tmp/xcb-errors - ./autogen.sh --prefix=/usr - make && sudo make install) - fi - - - | - # Install Lua (per env). - if [[ "$LUANAME" == "luajit-2.0" ]]; then - # "Create" /usr/bin/lua if needed (Yup, this is a bad hack) - if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/luajit /usr/bin/lua; fi - else - # lua5.3 does not install /usr/bin/lua, but lua5.1/lua5.2 do. - if [ ! -e "/usr/bin/lua" ]; then sudo ln -s /usr/bin/${LUANAME} /usr/bin/lua; fi - fi - - lua -v - - # Install luarocks (for the selected Lua version). - - | - travis_retry wget https://github.com/luarocks/luarocks/archive/v3.0.4.tar.gz - tar xf v3.0.4.tar.gz -C /tmp - (cd /tmp/luarocks-* \ - && ./configure --lua-version=$LUA --with-lua-include=${LUAINCLUDE} ${LUAROCKS_ARGS} \ - && make build \ - && sudo make install) - - - travis_retry sudo luarocks install lgi $LGIVER - - # Install busted for "make check-unit". - - travis_retry sudo luarocks install busted - # Install luacheck for "make check-qa". - - if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install luacheck; fi - # Install depgraph for "make check-qa". - - if [ "$DO_CHECKQA" = 1 ]; then travis_retry sudo luarocks install depgraph; fi - - # Install ldoc for building docs and check-ldoc-warnings. - - | - if [ "$BUILD_APIDOC" = "true" ] || [ "$DO_CHECKQA" = 1 ]; then - travis_retry sudo luarocks install ldoc - fi - - # Install dependencies for code coverage testing. - - if [ -n "$DO_COVERAGE" ]; then sudo luarocks install cluacov; fi - - if [ "$DO_COVERAGE" = "coveralls" ]; then sudo luarocks install luacov-coveralls; fi - - # Determine custom version. - - export AWESOME_VERSION="${TRAVIS_BRANCH}-g$(git rev-parse --short HEAD)" - - | - if [ "$TRAVIS_PULL_REQUEST" != false ] && [ "$TEST_PREV_COMMITS" = 1 ]; then - AWESOME_VERSION="${AWESOME_VERSION}-PR${TRAVIS_PULL_REQUEST}" - elif [ "$TRAVIS_PULL_REQUEST" != false ]; then - AWESOME_VERSION="v9999-PR${TRAVIS_PULL_REQUEST}"; - fi - # function for codecov support - - if [ "$DO_COVERAGE" = "codecov" ]; then travis_retry wget -O /tmp/codecov-bash https://codecov.io/bash; fi - - | - do_codecov() { - echo "== do_codecov in $PWD: $*: build/luacov.stats.out: ==" - if [ "$DO_COVERAGE" = "codecov" ]; then - test -f build/luacov.stats.out || { echo 'build/luacov.stats.out does not exist.'; return 1; } - luacov || return 1 - travis_retry bash /tmp/codecov-bash -f build/luacov.report.out -X gcov -X coveragepy -F luacov -e LUANAME,LGIVER || return 1 - rm build/luacov.report.out build/luacov.stats.out - fi - return 0 - } - - | - do_codecov_gcov() { - echo "== do_codecov_gcov in $PWD: $*: ==" - if [ "$DO_COVERAGE" = "codecov" ]; then - # Report coverage for each .gcno file separately. - # gcov will create .gcov files for the same source (e.g. for - # globalconf.h from awesome.c.gcno and event.c.gcno). - i=0 - set -x - ( - cd build - find -path "*/lgi-check.dir" -prune -o \( -name '*.gcno' -print \) | while read -r gcno; do - gcov -pb "$gcno" - - mkdir gcov.$(( ++i )) - mv *.gcov "gcov.$i" - - # Delete any files for /usr. - # They are not relevant and might cause "Invalid path part" errors - # with Code Climate. - find "gcov.$i" -maxdepth 1 -type f -name '#usr#*.gcov' -delete - done - - # Upload to Codecov. - travis_retry bash /tmp/codecov-bash -X gcov -X coveragepy -F gcov -e LUANAME,LGIVER - ) - fi - return 0 - } - - # Functions for custom Travis folds. - - | - travis_fold_start() { - travis_fold start "$1" - travis_fold_current="$1" - } - travis_fold_end() { - travis_fold end "$travis_fold_current" - } - travis_run_in_fold() { - travis_fold_start "$1" - shift - "$@" || return 1 - travis_fold_end - return 0 - } -script: - - if [ "$MANUAL_SCREENS" != "1" ]; then export MANUAL_SCREENS=0; fi - - export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -D LUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION -D STRICT_TESTS=true -D DO_COVERAGE=$DO_COVERAGE -D TEST_MANUAL_SCREENS=$MANUAL_SCREENS -D CMAKE_C_FLAGS=-Werror" - - | - if [ "$EMPTY_THEME_WHILE_LOADING" = 1 ]; then - # Break beautiful so that trying to access the theme before beautiful.init() causes an error - sed -i -e 's/theme = {}/theme = setmetatable({}, { __index = function() error("May not access theme before beautiful.init()") end })/' lib/beautiful/init.lua \ - && grep -q 'May not access' lib/beautiful/init.lua - fi - - | - trap 'echo ERR CODE $? from $BASH_COMMAND : $LINENO' ERR - trap 'echo EXIT CODE $? from $BASH_COMMAND : $LINENO' EXIT - set -ex - if [ -n "$BUILD_IN_DIR" ]; then - # Explicitly remove the Makefile to not build from the src dir accidentally. - rm Makefile - SOURCE_DIRECTORY="$PWD" - mkdir "$BUILD_IN_DIR" - cd "$BUILD_IN_DIR" - travis_run_in_fold "build_in_dir" cmake $CMAKE_ARGS "$SOURCE_DIRECTORY" - fi - - travis_run_in_fold "make" make - - | - if [ "$TRAVIS_TEST_RESULT" = 0 ]; then - travis_run_in_fold "make.install" sudo env PATH=$PATH make install - awesome --version - fi - # Run checks. - - | - if [ "$TRAVIS_TEST_RESULT" = 0 ]; then - set -ex - if [ -n "$DO_COVERAGE" ]; then - travis_fold_start "DO_COVERAGE" - - # Run tests/examples explicitly. - make check-examples || exit 1 - do_codecov samples - - make check-unit || exit 1 - do_codecov unittests - make check-integration || exit 1 - do_codecov functionaltests - make check-themes || exit 1 - do_codecov themes - do_codecov_gcov c_code - - travis_fold_end - else - # TODO: does not run check-examples. Should it? - travis_run_in_fold "make.check-unit" make check-unit - travis_run_in_fold "make.check-integration" make check-integration - travis_run_in_fold "make.check-themes" make check-themes - fi - set +x - fi - # Run check-qa. - - | - if [ "$DO_CHECKQA" = 1 ]; then - travis_run_in_fold "make.check-qa" make check-qa - fi - - | - if [ "$TEST_PREV_COMMITS" = 1 ] && ! [ "$TRAVIS_PULL_REQUEST" = false ]; then - set -ex - # Check each commit separately (to make git-bisect less annoying). - # Fix Travis' commit range (https://github.com/travis-ci/travis-ci/issues/4596). - commit_range="${TRAVIS_COMMIT_RANGE/.../..}" - echo "Testing previous commits ($commit_range)" - rev_list="$(git rev-list --bisect-all $commit_range)" - echo "rev-list: $rev_list" - commits="$(echo "$rev_list" | grep -v 'dist=0' | cut -d\ -f 1)" - n="$(echo "$commits" | wc -l)" - echo "Testing $n commits: $commits" - i=0 - failed= - for commit in $commits; do - i=$((i+1)) - travis_fold_start "test_commit_${commit}_.$i.$n" - echo "Testing commit $commit" - git reset --hard # Some files are updated when compiling... - git checkout "$commit" - git --no-pager show --stat - - if ! make all check CMAKE_ARGS+="-D DO_COVERAGE=0"; then - failed="$failed $commit" - fi - travis_fold_end - done - - git checkout -qf FETCH_HEAD - if [ -n "$failed" ]; then - echo "Checks failed for these commits:" - for c in $failed; do - git log -1 --pretty="%h %s (%an, %ad)" "$c" - done - false - fi - fi - -after_success: - # Push updated API docs for relevant branches, e.g. non-PRs builds on master. - - if [ "$BUILD_APIDOC" = "true" ]; then build-utils/travis-apidoc.sh; fi - # Push code coverage information - - | - trap 'echo ERR CODE $? from $BASH_COMMAND : $LINENO' ERR - trap 'echo EXIT CODE $? from $BASH_COMMAND : $LINENO' EXIT - set -ex - if [ "$DO_COVERAGE" = "coveralls" ]; then - test -f build/luacov.stats.out || { echo 'build/luacov.stats.out does not exist.'; return 1; } - luacov-coveralls --verbose --merge - fi - - echo "The build finished normally" diff --git a/tests/examples/CMakeLists.txt b/tests/examples/CMakeLists.txt index 501a2fab..328ad98d 100644 --- a/tests/examples/CMakeLists.txt +++ b/tests/examples/CMakeLists.txt @@ -21,7 +21,7 @@ execute_process(COMMAND ${LUA_EXECUTABLE} -e "p = package.path:gsub(';', '\\\\;' OUTPUT_VARIABLE "LUA_PATH_") # Allow to use the example tests by themselves. -# This was used on Travis for separate coverage reporting, but is not really +# This was used during CI for separate coverage reporting, but is not really # required anymore. if(NOT SOURCE_DIR AND ${CMAKE_CURRENT_SOURCE_DIR} MATCHES "/tests/examples") get_filename_component(TOP_SOURCE_DIR diff --git a/tests/examples/shims/_date.lua b/tests/examples/shims/_date.lua index 8236b327..f84a632e 100644 --- a/tests/examples/shims/_date.lua +++ b/tests/examples/shims/_date.lua @@ -1,5 +1,5 @@ local source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") -if source_date_epoch then +if source_date_epoch and source_date_epoch ~= '' then local old_osdate = os.date os.date = function(format, timestamp) -- luacheck: ignore if timestamp then diff --git a/tests/run.sh b/tests/run.sh index ec4395c6..2e0f16a5 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -68,7 +68,6 @@ else tests="$this_dir/test*.lua" fi -# Travis. if [ "$CI" = true ]; then HEADLESS=1 TEST_PAUSE_ON_ERRORS=0 @@ -88,6 +87,8 @@ fi AWESOME_CLIENT="$source_dir/utils/awesome-client" D=:5 SIZE="${TESTS_SCREEN_SIZE:-1024x768}" +# Seconds after when awesome gets killed. +TEST_TIMEOUT=${TEST_TIMEOUT:-30} # Set up some env vars # Disable GDK's screen scaling support @@ -133,7 +134,7 @@ echo "awesome_log: $awesome_log" wait_until_success() { if (( verbose )); then set +x; fi - wait_count=60 # 60*0.05s => 3s. + wait_count=$((${TEST_TIMEOUT} * 20)) while true; do set +e eval reply="\$($2)" @@ -183,6 +184,13 @@ fi start_awesome() { cd "$build_dir" + # On some systems clients from a test may still linger for a while until they + # are fully killed. Since this can affect subsequent tests, we wait until all + # of them are gone. + wait_until_success \ + 'wait for X clients from previous test to close' \ + "[ -z \"\$(DISPLAY=\"$D\" xlsclients 2>/dev/null)\" ]" + # Kill awesome after $TEST_TIMEOUT seconds (e.g. for errors during test setup). # SOURCE_DIRECTORY is used by .luacov. DISPLAY="$D" SOURCE_DIRECTORY="$source_dir" \ @@ -195,12 +203,14 @@ start_awesome() { # Wait until the interface for awesome-client is ready (D-Bus interface). # Do this with dbus-send so that we can specify a low --reply-timeout - wait_until_success "wait for awesome startup via awesome-client" "dbus-send --reply-timeout=100 --dest=org.awesomewm.awful --print-reply / org.awesomewm.awful.Remote.Eval 'string:return 1' 2>&1" + wait_until_success "wait for awesome startup via awesome-client" "dbus-send --reply-timeout=${TEST_TIMEOUT} --dest=org.awesomewm.awful --print-reply / org.awesomewm.awful.Remote.Eval 'string:return 1' 2>&1" } -if command -v tput >/dev/null; then - color_red() { tput setaf 1; } - color_reset() { tput sgr0; } +# Only print colors when stdout is connected to a terminal. +# When it is piped/redirected, skip the color codes. +if [ -t 1 ]; then + color_red() { echo -e "\e[31m"; } + color_reset() { echo -e "\e[0m"; } else color_red() { :; } color_reset() { :; } @@ -208,8 +218,6 @@ fi count_tests=0 errors=() -# Seconds after when awesome gets killed. -TEST_TIMEOUT=${TEST_TIMEOUT:-30} for f in $tests; do echo "== Running $f ==" diff --git a/tests/test-leak-client.lua b/tests/test-leak-client.lua index 12b520e4..89e86558 100644 --- a/tests/test-leak-client.lua +++ b/tests/test-leak-client.lua @@ -4,6 +4,18 @@ local awful = require("awful") local wibox = require("wibox") local gtable = require("gears.table") +-- This test has been proven to perform unreliably on GitHub Actions. +-- The additional Lua process that creates the test client will sometimes +-- linger for an unpredictable amount of time. +-- Stalling the step to check for clean-up increases the chance for success, +-- but does not fix the issue. +-- See https://github.com/awesomeWM/awesome/pull/3292. +if os.getenv("GITHUB_ACTIONS") then + print("Skipping unreliable test 'test-leak-client'") + runner.run_steps { function() return true end } + return +end + -- Create a titlebar and return a table with references to its member widgets. local function create_titlebar(c) local parts = {} @@ -85,12 +97,12 @@ local steps = { -- Test that we have a client and that it's invalid (tostring() -- causes an "invalid object" error) local success, msg = pcall(function() tostring(objs[1]) end) - assert(not success) + assert(not success, msg) assert(msg:find("invalid object"), msg) -- Check that it is garbage-collectable collectgarbage("collect") - assert(#objs == 0) + assert(#objs == 0, "still clients left after garbage collect") return true end end,