diff --git a/.github/workflows/Containerfile b/.github/workflows/Containerfile new file mode 100644 index 000000000..dd6fd37c0 --- /dev/null +++ b/.github/workflows/Containerfile @@ -0,0 +1,40 @@ +FROM ubuntu:latest + +RUN apt update +RUN apt upgrade -y + +# Install dependencies +RUN apt install -y --no-install-recommends \ + gcc clang \ + ca-certificates \ + desktop-file-utils \ + fuse3 \ + gettext \ + git \ + gnome-desktop-testing \ + gtk-doc-tools \ + libcap2-bin \ + libflatpak-dev \ + libfontconfig1-dev \ + libfuse3-dev \ + libgdk-pixbuf-2.0-dev \ + libgeoclue-2-dev \ + libglib2.0-dev \ + libjson-glib-dev \ + libpipewire-0.3-dev \ + libportal-dev \ + libsystemd-dev \ + libtool \ + llvm \ + python3-gi \ + shared-mime-info + +# Install meson +RUN apt install -y --no-install-recommends meson + +# Install pytest +RUN apt install -y --no-install-recommends \ + python3-pytest \ + python3-pytest-xdist \ + python3-dbusmock \ + python3-dbus diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 000000000..2366d25f1 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,102 @@ +name: Build and Test + +on: [push, pull_request] + +env: + IMAGE_TAG: 20240404-0 + TESTS_TIMEOUT: 10 # in minutes + +jobs: + build-container: + name: Create build container + runs-on: ubuntu-latest + permissions: + packages: write + + steps: + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Check if image already exists on GHCR + id: check + run: | + image=ghcr.io/${{ github.repository_owner }}/xdg-desktop-portal-build:${{ env.IMAGE_TAG }} + echo "exists=$(docker manifest inspect $image >/dev/null && echo 'true' || echo 'false')" \ + >> "$GITHUB_OUTPUT" + + - name: Build and push + if: ${{ steps.check.outputs.exists == 'false' }} + uses: docker/build-push-action@v5 + with: + push: true + file: .github/workflows/Containerfile + tags: ghcr.io/${{ github.repository_owner }}/xdg-desktop-portal-build:${{ env.IMAGE_TAG }} + + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + needs: build-container + strategy: + matrix: + compiler: ['gcc', 'clang'] + sanitizer: ['address'] + + container: + image: ghcr.io/${{ github.repository_owner }}/xdg-desktop-portal-build:latest + env: + CFLAGS: -Wp,-D_FORTIFY_SOURCE=2 + CC: ${{ matrix.compiler }} + TEST_IN_CI: 1 + G_MESSAGES_DEBUG: all + options: --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined + + steps: + - name: Configure environment + run: | + git config --global --add safe.directory $GITHUB_WORKSPACE + echo XDG_DATA_DIRS=$GITHUB_WORKSPACE/tests/share:/usr/local/share:/usr/share | tee -a $GITHUB_ENV + + - name: Check out xdg-desktop-portal + uses: actions/checkout@v4 + + - name: Check POTFILES.in + run: .github/workflows/check-potfiles.sh + + - name: Build xdg-desktop-portal + run: | + meson setup _build $MESON_OPTIONS + meson compile -C _build + env: + MESON_OPTIONS: -Dinstalled-tests=true -Dpytest=enabled -Db_sanitize=${{ matrix.sanitizer }} + + - name: Run xdg-desktop-portal tests + run: timeout --signal=KILL -v ${TESTS_TIMEOUT}m meson test -C _build + + - name: Install xdg-desktop-portal + run: meson install -C _build + + - name: Run xdg-desktop-portal installed-tests + run: | + test -n "$(gnome-desktop-testing-runner -l xdg-desktop-portal)" + gnome-desktop-testing-runner --report-directory installed-test-logs/ \ + -t $((TESTS_TIMEOUT * 60)) xdg-desktop-portal + + - name: Create dist tarball + run: | + ls -la + timeout --signal=KILL -v ${TESTS_TIMEOUT}m meson dist -C _build + + - name: Upload test logs + uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: test logs (${{ matrix.compiler }}, ${{ matrix.sanitizer }}) + path: | + tests/*.log + test-*.log + installed-test-logs/ + _build/meson-logs/testlog.txt diff --git a/.github/workflows/check-potfiles.sh b/.github/workflows/check-potfiles.sh new file mode 100755 index 000000000..39b673cae --- /dev/null +++ b/.github/workflows/check-potfiles.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# FIXME stolen from gnome-control-center, GPLv2 +# This project is LGPLv2 +# https://gitlab.gnome.org/GNOME/gnome-control-center/-/merge_requests/2269#note_2079291 + +srcdirs="src document-portal" +uidirs=$srcdirs +desktopdirs=$srcdirs +policydirs=$srcdirs +xmldirs=$srcdirs + +# find source files that contain gettext functions with literal or GETTEXT_PACKAGE argument +files=$(grep -lRs --include='*.c' 'gettext *(\("\|GETTEXT_PACKAGE,\)' $srcdirs) + +# find source files that contain gettext macros +files="$files "$(grep -lRs --include='*.c' --include='*.h' '[^I_)]_(' $srcdirs) + +# find ui files that contain translatable string +files="$files "$(grep -lRis --include='*.ui' 'translatable="[ty1]' $uidirs) + +# find .desktop files +files="$files "$(find $desktopdirs -name '*.desktop*') + +# find .policy files +files="$files "$(find $policydirs -name '*.policy*') + +# find .xml.in and .gschema.xml files +files="$files "$(find $xmldirs -not -name '*.gresource.xml*' \ + -name '*.xml.in*' -o \ + -name '*.gschema.xml') + +# filter out excluded files +if [ -f po/POTFILES.skip ]; then + files=$(for f in $files; do + ! grep -q "^$f" po/POTFILES.skip && echo "$f" + done) +fi + +# find those that aren't listed in POTFILES.in +missing=$(for f in $files; do + ! grep -q "^$f" po/POTFILES.in && echo "$f" + done) + +# find those that are listed in POTFILES.in but do not contain translatable strings +invalid=$(while IFS= read -r f; do + ! grep -q "$f" <<< "$files" && echo "$f" + done < <(grep '^[^#]' po/POTFILES.in)) + +# find out if POTFILES.in is sorted correctly, ignoring empty lines +sorted=$(grep '^[^#]' po/POTFILES.in | \ + LC_ALL=en_US.UTF-8 sort -cu 2>&1 >/dev/null | \ + awk -F ': *' '{print $5}') + +if [ ${#missing} -eq 0 ] && [ ${#invalid} -eq 0 ] && [ ${#sorted} -eq 0 ]; then + exit 0 +fi + +if [ ${#missing} -ne 0 ]; then + cat >&2 << EOT + +The following files are missing from po/POTFILES.in or po/POTFILES.skip: + +EOT + for f in $missing; do + echo " $f" >&2 + done +fi + +if [ ${#invalid} -ne 0 ]; then + cat >&2 << EOT + +The following files are in po/POTFILES.in but are missing or not translatable: + +EOT + for f in $invalid; do + echo " $f" >&2 + done +fi + +if [ ${#sorted} -ne 0 ]; then + cat >&2 << EOT + +The following file is not sorted properly in po/POTFILES.in: + +EOT + echo " $sorted" >&2 +fi + +echo >&2 + +exit 1 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml deleted file mode 100644 index 5e419074f..000000000 --- a/.github/workflows/check.yml +++ /dev/null @@ -1,137 +0,0 @@ -name: Portal CI - -on: [push, pull_request] - -env: - DEBIAN_FRONTEND: noninteractive - TESTS_TIMEOUT: 10 # in minutes - -jobs: - check: - name: Ubuntu 22.04 build - runs-on: ubuntu-22.04 - strategy: - matrix: - compiler: ['gcc', 'clang'] - sanitizer: ['address'] - - env: - UBUNTU_VERSION: '22.04' - CC: ${{ matrix.compiler }} - BASE_CFLAGS: -Wp,-D_FORTIFY_SOURCE=2 - BUILD_CONTAINER: ${{ matrix.compiler }}-build-container - RUN_CMD: docker exec -t -w /src -e TEST_IN_CI -e ASAN_OPTIONS -e G_MESSAGES_DEBUG -e XDG_DATA_DIRS ${{ matrix.compiler }}-build-container - AS_USER: runuser -u tester -- - BUILDDIR: builddir - - steps: - - name: Prepare environment - id: env-setup - run: | - echo "cflags=$BASE_CFLAGS" >> $GITHUB_OUTPUT; - - - name: Prepare container - run: | - docker run --name $BUILD_CONTAINER \ - --tty --device /dev/fuse --cap-add SYS_ADMIN \ - --security-opt apparmor:unconfined \ - -v $(pwd):/src \ - -e DEBIAN_FRONTEND \ - -e DEBCONF_NONINTERACTIVE_SEEN=true \ - -e TERM=dumb \ - -e MAKEFLAGS="-j $(getconf _NPROCESSORS_ONLN)" \ - -e CC -e CFLAGS="${{ steps.env-setup.outputs.cflags }}" \ - -d ubuntu:$UBUNTU_VERSION sleep infinity - - - name: Install dependencies - run: | - $RUN_CMD apt-get update --quiet - $RUN_CMD apt-get upgrade --quiet -y - $RUN_CMD apt-get install --quiet -y --no-install-recommends \ - ${{ matrix.compiler }} \ - desktop-file-utils \ - fuse3 \ - gettext \ - git \ - gnome-desktop-testing \ - gtk-doc-tools \ - libcap2-bin \ - libflatpak-dev \ - libfontconfig1-dev \ - libfuse3-dev \ - libgdk-pixbuf-2.0-dev \ - libgeoclue-2-dev \ - libglib2.0-dev \ - libjson-glib-dev \ - libpipewire-0.3-dev \ - libportal-dev \ - libsystemd-dev \ - libtool \ - llvm \ - python3-gi \ - shared-mime-info - - - name: Install dependencies for meson - run: | - $RUN_CMD apt-get install --quiet -y --no-install-recommends \ - meson - - - name: Install dependencies for the pytest test suite - run: | - $RUN_CMD apt-get install --quiet -y --no-install-recommends \ - python3-pytest python3-pytest-xdist python3-dbusmock python3-dbus - - - name: Check out xdg-desktop-portal - uses: actions/checkout@v3 - - - name: Setup test user - run: | - $RUN_CMD adduser --disabled-password --gecos "" tester - $RUN_CMD chown tester:tester . -R - - - name: Build xdg-desktop-portal - run: | - $RUN_CMD $AS_USER meson setup ${BUILDDIR} $MESON_OPTIONS - $RUN_CMD $AS_USER meson compile -C ${BUILDDIR} - env: - MESON_OPTIONS: -Dinstalled-tests=true -Dpytest=enabled -Db_sanitize=${{ matrix.sanitizer }} - - - name: Run xdg-desktop-portal tests - run: $RUN_CMD $AS_USER timeout --signal=KILL -v ${TESTS_TIMEOUT}m meson test -C ${BUILDDIR} - env: - TEST_IN_CI: 1 - G_MESSAGES_DEBUG: all - - - name: Install xdg-desktop-portal - run: $RUN_CMD meson install -C ${BUILDDIR} - - - name: Run xdg-desktop-portal installed-tests - run: | - test -n "$($RUN_CMD $AS_USER gnome-desktop-testing-runner -l xdg-desktop-portal)" - $RUN_CMD $AS_USER \ - env TEST_INSTALLED_IN_CI=1 XDG_DATA_DIRS=/src/tests/share/:$XDG_DATA_DIRS \ - gnome-desktop-testing-runner --report-directory installed-test-logs/ \ - -t $((TESTS_TIMEOUT * 60)) xdg-desktop-portal - env: - G_MESSAGES_DEBUG: all - TEST_IN_CI: 1 - XDG_DATA_DIRS: /usr/local/share:/usr/share - - - name: Create dist tarball - run: | - $RUN_CMD $AS_USER ls -la - $RUN_CMD $AS_USER timeout --signal=KILL -v ${TESTS_TIMEOUT}m meson dist -C ${BUILDDIR} - env: - TEST_IN_CI: 1 - G_MESSAGES_DEBUG: all - - - name: Upload test logs - uses: actions/upload-artifact@v3 - if: success() || failure() - with: - name: test logs - path: | - tests/*.log - test-*.log - installed-test-logs/ - builddir/meson-logs/testlog.txt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..ff2fa0e0e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,92 @@ +name: Release new version + +on: + workflow_dispatch: + inputs: + version: + description: "Release version (default: auto)" + preRelease: + description: "Is this a pre-release? (true/false, default: auto)" + dryRun: + description: Dry Run + default: false + +jobs: + release: + name: Build and publish a release + runs-on: ubuntu-latest + container: + image: ghcr.io/${{ github.repository_owner }}/xdg-desktop-portal-build:latest + permissions: + contents: write + steps: + - name: Configure git user + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global --add safe.directory $GITHUB_WORKSPACE + + - name: Checkout the repository + uses: actions/checkout@v4 + + - name: Update translation files + if: ${{ !fromJSON(github.event.inputs.dryRun) }} + run: | + meson setup . _build + meson compile -C _build/ xdg-desktop-portal-update-po + git add po/*po + git commit -m "Update po files" + git push + git clean -fxd + + - name: Build xdg-desktop-portal + if: ${{ !fromJSON(github.event.inputs.dryRun) }} + run: | + meson setup . _build + meson dist -C _build + + - name: Extract release information + env: + releaseVersion: ${{ github.event.inputs.version }} + preRelease: ${{ github.event.inputs.preRelease }} + run: | + # Extract the release version + if [ -z $releaseVersion ]; then + releaseVersion=`perl -0777nE 'print $& if /(?<=Changes in ).*/' NEWS` + fi + echo "releaseVersion=$releaseVersion" | tee -a $GITHUB_ENV + + # Extract the changelog + { + echo "releaseChangelog< - - git push origin main - - meson setup . _build -Ddocbook-docs=enabled - - meson dist -C _build - - git tag - - git push origin refs/tags/ - - upload tarball to github as release - - edit release, copy NEWS section in - - update portal api docs in the gh-pages branch - - bump version in meson.build - - git commit -m "Post-release version bump" - - git push origin main - - Update SECURITY.md if this is a new stable release - - Update .github/ISSUE_TEMPLATE/bug-report.yml if this is a new stable release +- Make sure the version in `meson.build` is correct +- Add your changelog to the `NEWS` file +- Run the "Release new version" GitHub Action + - The options are taken from the last `NEWS` entry by default, you may override them if needed +- Bump version in `meson.build` +- Update `SECURITY.md` if this is a new stable release +- Update `.github/ISSUE_TEMPLATE/bug-report.yml` if this is a new stable release