Repository: beeware/briefcase

Issue:
Modify build image of AppImage template to use Ubuntu 20.04
According to Github, by Dec 1 2022 they will [formally deprecate the Ubuntu-18.04 Actions runner](https://github.com/actions/runner-images/issues/6002). 

This puts Briefcase in an interesting situation. On the one hand Briefcase as used by end users is (mostly) unaffected - we encourage the use of Docker for building AppImages, and the Ubuntu 18.04 image will continue to exist. 

However, as a project, *we* have a problem. Due to historical issues using Docker in CI, we run our CI in `--no-docker` mode - which means that in December, we'll no longer be able to run CI for Linux. 

**Describe the solution you'd like**

Update the template and documentation to use Ubuntu 20.04 as the base image. 

This is something we've done before ([transitioning from 16.04 to 18.04 as the base image](https://github.com/beeware/briefcase-linux-appimage-template/commit/a172088d46f86b0a95799e8f03bc04149741c765). Interestingly, this was almost 2 years ago to the day, which matches the 2 year LTS release cadence of Ubuntu.

In addition to continued CI support, this has the added benefit that we'll have access to more recent packages in the base image.

**Describe alternatives you've considered**

1. Remove AppImages from our testing setup, but otherwise do nothing. Ubuntu 18.04 AppImage images will continue to work; they'll just be untested.
2. Modify CI to use Docker, but continue to use 18.04 as the base image. 
3. Deprecate the AppImage backend in favour of Flatpak.

**Additional context**

This may have some overlap with #824 - a switch of this magnitude requires some sort of control from a user perspective.

Discussion:

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bddbba12..c0ad7731 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -131,27 +131,30 @@ jobs:
       run: |
         tox -e py
 
-  verify-desktop:
-    name: Desktop app verification
-    needs: smoke
+  verify-apps:
+    name: App verification
+    needs: platform-compat
     strategy:
       max-parallel: 4
       matrix:
-        platform: ['macos-latest', 'windows-latest', 'ubuntu-18.04']
+        os_name: ['macOS', 'windows', 'linux']
         framework: ['toga', 'pyside2', 'pyside6', 'ppb']
         include:
-        - platform: macos-latest
+        - os_name: macOS
+          platform: macos-latest
           briefcase-data-dir: ~/Library/Caches/org.beeware.briefcase
           pip-cache-dir: ~/Library/Caches/pip
           docker-cache-dir: ~/Library/Containers/com.docker.docker/Data/vms/0/
-        - platform: windows-latest
+        - os_name: windows
+          platform: windows-latest
           briefcase-data-dir: ~\AppData\Local\BeeWare\briefcase\Cache
           pip-cache-dir: ~\AppData\Local\pip\Cache
           docker-cache-dir: C:\ProgramData\DockerDesktop
-        - platform: ubuntu-18.04
+        - os_name: linux
+          # Need to use at least 22.04 to get the bugfix in flatpak for handling spaces in filenames.
+          platform: ubuntu-22.04
           briefcase-data-dir: ~/.cache/briefcase
           pip-cache-dir: ~/.cache/pip
-          briefcase-args: --no-docker
           # cache action cannot cache docker images (actions/cache#31)
           # docker-cache-dir: /var/lib/docker
     runs-on: ${{ matrix.platform }}
@@ -170,16 +173,57 @@ jobs:
       uses: actions/setup-python@v3.1.2
       with:
         python-version: ${{ env.python_version }}
-    - name: Install dependencies
-      if: matrix.platform == 'ubuntu-18.04'
+    - name: Install system dependencies
+      if: matrix.platform == 'ubuntu-22.04'
       run: |
-        sudo apt-get update -y
-        sudo apt-get install -y python3-gi python3-gi-cairo gir1.2-gtk-3.0 python3-dev libgirepository1.0-dev libcairo2-dev pkg-config
+        sudo apt-get install -y flatpak flatpak-builder
     - name: Install dependencies
       run: |
         pip install --upgrade pip
         pip install --upgrade setuptools
-        pip install tox
-    - name: Test
+        pip install .
+    - name: Create App
+      run: |
+        cd tests/apps
+        cat verify-${{ matrix.framework }}.config | briefcase new
+    - name: Build App
+      run: |
+        cd tests/apps/verify-${{ matrix.framework }}
+        briefcase create
+        briefcase build
+        briefcase package --adhoc-sign
+    - name: Build Xcode project
+      if: matrix.os_name == 'macOS'
+      run: |
+        cd tests/apps/verify-${{ matrix.framework }}
+        briefcase create ${{ matrix.os_name }} Xcode
+        briefcase build ${{ matrix.os_name }} Xcode
+        briefcase package ${{ matrix.os_name }} Xcode --adhoc-sign
+    - name: Build Visual Studio project
+      if: matrix.os_name == 'windows'
+      run: |
+        cd tests/apps/verify-${{ matrix.framework }}
+        briefcase create ${{ matrix.os_name }} VisualStudio
+        briefcase build ${{ matrix.os_name }} VisualStudio
+        briefcase package ${{ matrix.os_name }} VisualStudio --adhoc-sign
+    - name: Build Flatpak project
+      if: matrix.os_name == 'linux' && matrix.framework == 'toga'
+      run: |
+        cd tests/apps/verify-${{ matrix.framework }}
+        briefcase create ${{ matrix.os_name }} flatpak
+        briefcase build ${{ matrix.os_name }} flatpak
+        briefcase package ${{ matrix.os_name }} flatpak --adhoc-sign
+    - name: Build Android App
+      if: matrix.framework == 'toga'
+      run: |
+        cd tests/apps/verify-${{ matrix.framework }}
+        briefcase create android
+        briefcase build android
+        briefcase package android --adhoc-sign
+    - name: Build iOS App
+      if: matrix.platform == 'macos-latest' && matrix.framework == 'toga'
       run: |
-        tox -e verify-${{ matrix.framework }} -- ${{ matrix.briefcase-args }}
+        cd tests/apps/verify-${{ matrix.framework }}
+        briefcase create iOS
+        briefcase build iOS -d "iPhone SE (2nd generation)"
+        briefcase package iOS --adhoc-sign
diff --git a/changes/837.bugfix.rst b/changes/837.bugfix.rst
deleted file mode 100644
index 5473bb5f..00000000
--- a/changes/837.bugfix.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-The RCEdit plugin can now be upgraded.
diff --git a/changes/842.feature.rst b/changes/842.feature.rst
deleted file mode 100644
index 21b07dad..00000000
--- a/changes/842.feature.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-Log messages can now be captured on iOS if they originate from a dynamically loaded module.
diff --git a/changes/844.misc.rst b/changes/844.misc.rst
new file mode 100644
index 00000000..7516c98d
--- /dev/null
+++ b/changes/844.misc.rst
@@ -0,0 +1,1 @@
+Linux tests are now run in Docker on CI; iOS, Android, Linux Flatpak, macOS Xcode, and Windows VisualStudio are tested.
diff --git a/src/briefcase/integrations/rcedit.py b/src/briefcase/integrations/rcedit.py
index 16886a25..375f7320 100644
--- a/src/briefcase/integrations/rcedit.py
+++ b/src/briefcase/integrations/rcedit.py
@@ -3,7 +3,6 @@ from briefcase.exceptions import MissingToolError
 
 class RCEdit:
     name = "rcedit"
-    full_name = "RCEdit"
 
     def __init__(self, command):
         self.command = command
diff --git a/src/briefcase/platforms/iOS/xcode.py b/src/briefcase/platforms/iOS/xcode.py
index a1bdca11..cb248810 100644
--- a/src/briefcase/platforms/iOS/xcode.py
+++ b/src/briefcase/platforms/iOS/xcode.py
@@ -351,19 +351,6 @@ class iOSXcodeRunCommand(iOSXcodeMixin, RunCommand):
             ) from e
 
         # Start log stream for the app.
-        # The following sets up a log stream filter that looks for:
-        #  1. a log sender that matches that app binary; or,
-        #  2. a log sender of that is a Python extension module,
-        #     and a process that matches the app binary.
-        # Case (1) works when the standard libary is statically linked,
-        #   and for native NSLog() calls in the bootstrap binary
-        # Case (2) works when the standard library is dynamically linked,
-        #   and ctypes (which handles the NSLog integration) is an
-        #   extension module.
-        # It's not enough to filter on *just* the processImagePath,
-        # as the process will generate lots of system-level messages.
-        # We can't filter on *just* the senderImagePath, because other
-        # apps will generate log messages that would be caught by the filter.
         simulator_log_popen = self.subprocess.Popen(
             [
                 "xcrun",
@@ -375,9 +362,7 @@ class iOSXcodeRunCommand(iOSXcodeMixin, RunCommand):
                 "--style",
                 "compact",
                 "--predicate",
-                f'senderImagePath ENDSWITH "/{app.formal_name}"'
-                f' OR (processImagePath ENDSWITH "/{app.formal_name}"'
-                ' AND senderImagePath ENDSWITH "-iphonesimulator.so")',
+                f'senderImagePath ENDSWITH "/{app.formal_name}"',
             ],
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
diff --git a/src/briefcase/platforms/macOS/__init__.py b/src/briefcase/platforms/macOS/__init__.py
index 1f90a3c5..6c9530d7 100644
--- a/src/briefcase/platforms/macOS/__init__.py
+++ b/src/briefcase/platforms/macOS/__init__.py
@@ -47,10 +47,6 @@ class macOSRunMixin:
         #  2. a log sender of libffi, and a process that matches the app binary.
         # Case (1) works for pre-Python 3.9 static linked binaries.
         # Case (2) works for Python 3.9+ dynamic linked binaries.
-        # It's not enough to filter on *just* the processImagePath,
-        # as the process will generate lots of system-level messages.
-        # We can't filter on *just* the senderImagePath, because other
-        # apps will generate log messages that would be caught by the filter.
         sender = os.fsdecode(
             self.binary_path(app) / "Contents" / "MacOS" / app.formal_name
         )
diff --git a/tox.ini b/tox.ini
index e10d2c16..8682bb42 100644
--- a/tox.ini
+++ b/tox.ini
@@ -4,7 +4,7 @@
 # and then run "tox" from this directory.
 
 [tox]
-envlist = towncrier-check,docs,package,py{37,38,39,310,311},verify-{toga,pyside2,pyside6,ppb}
+envlist = towncrier-check,docs,package,py{37,38,39,310,311}
 skip_missing_interpreters = true
 
 [testenv]
@@ -55,17 +55,3 @@ passenv =
     TWINE_PASSWORD
 commands =
     python -m twine upload dist/*
-
-[testenv:verify-{toga,pyside2,pyside6,ppb}]
-setenv = PYTHONPATH = {toxinidir}/src
-changedir = {toxinidir}/tests/apps
-deps =
-allowlist_externals =
-    sh
-    rm
-commands =
-    rm -rf {envname}
-    sh -c 'cat {envname}.config | briefcase new'
-    sh -c 'cd {envname} && briefcase create {posargs}'
-    sh -c 'cd {envname} && briefcase build {posargs}'
-    sh -c 'cd {envname} && briefcase package --adhoc-sign {posargs}'


Test patch:
diff --git a/tests/platforms/iOS/xcode/test_run.py b/tests/platforms/iOS/xcode/test_run.py
index ad7e1e31..7302e9a6 100644
--- a/tests/platforms/iOS/xcode/test_run.py
+++ b/tests/platforms/iOS/xcode/test_run.py
@@ -95,9 +95,7 @@ def test_run_app_simulator_booted(first_app_config, tmp_path):
             "--style",
             "compact",
             "--predicate",
-            'senderImagePath ENDSWITH "/First App"'
-            ' OR (processImagePath ENDSWITH "/First App"'
-            ' AND senderImagePath ENDSWITH "-iphonesimulator.so")',
+            'senderImagePath ENDSWITH "/First App"',
         ],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
@@ -201,9 +199,7 @@ def test_run_app_simulator_shut_down(first_app_config, tmp_path):
             "--style",
             "compact",
             "--predicate",
-            'senderImagePath ENDSWITH "/First App"'
-            ' OR (processImagePath ENDSWITH "/First App"'
-            ' AND senderImagePath ENDSWITH "-iphonesimulator.so")',
+            'senderImagePath ENDSWITH "/First App"',
         ],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
@@ -320,9 +316,7 @@ def test_run_app_simulator_shutting_down(first_app_config, tmp_path):
             "--style",
             "compact",
             "--predicate",
-            'senderImagePath ENDSWITH "/First App"'
-            ' OR (processImagePath ENDSWITH "/First App"'
-            ' AND senderImagePath ENDSWITH "-iphonesimulator.so")',
+            'senderImagePath ENDSWITH "/First App"',
         ],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
@@ -675,9 +669,7 @@ def test_run_app_simulator_launch_failure(first_app_config, tmp_path):
             "--style",
             "compact",
             "--predicate",
-            'senderImagePath ENDSWITH "/First App"'
-            ' OR (processImagePath ENDSWITH "/First App"'
-            ' AND senderImagePath ENDSWITH "-iphonesimulator.so")',
+            'senderImagePath ENDSWITH "/First App"',
         ],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,