From c8a316f15231550475b7627eabf80f3877fc4ceb Mon Sep 17 00:00:00 2001 From: Andrew Dorton Date: Fri, 2 Oct 2026 11:18:50 -0600 Subject: [PATCH 1/3] Update CI for current GitHub runners - Replace retired ubuntu-20.04 runner (build: ubuntu-24.04; package: focal -> noble) - Bump actions to Node 24 versions (checkout/setup-python v7, upload v7, download v8) - Drop python-dbus apt package (not available on 22.04+), use Python 3.12 in build - Replace battila7/get-version-action with github.ref_name - Replace archived actions/create-release + upload-release-assets with softprops/action-gh-release Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build.yml | 25 +++--- .github/workflows/package.yml | 158 +++++++++++----------------------- 2 files changed, 64 insertions(+), 119 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6cf31a14..fa5d6b8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,25 +11,24 @@ jobs: runs-on: ${{matrix.os}} strategy: + fail-fast: false matrix: - os: [ubuntu-20.04, windows-latest] + os: [ubuntu-24.04, windows-latest] extension_support: [0, 1] - + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 - - name: Set up Python - uses: actions/setup-python@v2 + - name: Set up Python + uses: actions/setup-python@v7 with: - python-version: '3.9' - - name: Ubuntu - if: matrix.os == 'ubuntu-20.04' - run: | + python-version: '3.12' + - name: Ubuntu dependencies + if: runner.os == 'Linux' + run: | sudo apt-get update - sudo apt-get install -y software-properties-common - sudo apt-get install -y build-essential - sudo apt-get install -y python3-dev python3-pip python3-virtualenv - sudo apt-get install -y pkg-config libssl-dev libdbus-1-dev libdbus-glib-1-dev python-dbus libffi-dev libkrb5-dev + sudo apt-get install -y build-essential pkg-config \ + libssl-dev libdbus-1-dev libdbus-glib-1-dev libffi-dev libkrb5-dev - name: Install dependencies run: | diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 4a17602d..f2158599 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -6,35 +6,36 @@ on: - 'v*' jobs: - ubuntu-jammy: - runs-on: ubuntu-22.04 + ubuntu: + runs-on: ${{ matrix.runner }} strategy: matrix: + runner: [ubuntu-22.04, ubuntu-24.04] + extension_support: [0, 1] include: + - runner: ubuntu-22.04 + label: ubuntu-jammy + asset_label: ubuntu_jammy + - runner: ubuntu-24.04 + label: ubuntu-noble + asset_label: ubuntu_noble - extension_support: 0 variant_tag: "-noext" - extension_support: 1 variant_tag: "" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.12' - - name: Get version tag - id: get_version - uses: battila7/get-version-action@v2 - name: Ubuntu dependencies run: | - sudo apt update - sudo apt-get install -y software-properties-common \ - build-essential \ - python3-dev python3-pip python3.11-venv \ - pkg-config libssl-dev libdbus-1-dev libdbus-glib-1-dev libffi-dev libkrb5-dev + sudo apt-get update + sudo apt-get install -y build-essential pkg-config \ + libssl-dev libdbus-1-dev libdbus-glib-1-dev libffi-dev libkrb5-dev - name: Build the executable run: | - python3 -m venv venv - source venv/bin/activate pip install ./sign_client pip install external/okta-0.0.3.1-py2.py3-none-any.whl pip install -e . @@ -46,66 +47,18 @@ jobs: - name: Get build run: | cd dist - tar czf "user-sync-${UST_VERSION}${UST_VARIANT_TAG}-ubuntu_jammy.tar.gz" user-sync + tar czf "user-sync-${UST_VERSION}${UST_VARIANT_TAG}-${ASSET_LABEL}.tar.gz" user-sync env: UST_VARIANT_TAG: ${{matrix.variant_tag}} - UST_VERSION: ${{ steps.get_version.outputs.version }} + UST_VERSION: ${{ github.ref_name }} + ASSET_LABEL: ${{ matrix.asset_label }} - name: Make artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: - name: "ubuntu-jammy${{matrix.variant_tag}}" + name: "${{matrix.label}}${{matrix.variant_tag}}" path: dist/*.tar.gz retention-days: 30 - ubuntu-focal: - runs-on: ubuntu-20.04 - strategy: - matrix: - include: - - extension_support: 0 - variant_tag: "-noext" - - extension_support: 1 - variant_tag: "" - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Get version tag - id: get_version - uses: battila7/get-version-action@v2 - - name: Ubuntu-Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y software-properties-common - sudo apt-get install -y build-essential - sudo apt-get install -y python3-dev python3-pip python3-virtualenv - sudo apt-get install -y pkg-config libssl-dev libdbus-1-dev libdbus-glib-1-dev python-dbus libffi-dev libkrb5-dev - - run: | - pip install ./sign_client - pip install external/okta-0.0.3.1-py2.py3-none-any.whl - pip install -e . - pip install -e .[test] - pip install -e .[setup] - - name: Build executable - run: make - env: - UST_EXTENSION: ${{matrix.extension_support}} - - name: Get build - run: | - cd dist - tar czf "user-sync-${UST_VERSION}${UST_VARIANT_TAG}-ubuntu_focal.tar.gz" user-sync - env: - UST_VARIANT_TAG: ${{matrix.variant_tag}} - UST_VERSION: ${{ steps.get_version.outputs.version }} - - name: Make artifacts - uses: actions/upload-artifact@v4 - with: - name: "ubuntu-focal${{matrix.variant_tag}}" - path: dist/*.tar.gz - retention-days: 30 - windows: runs-on: windows-latest strategy: @@ -116,13 +69,10 @@ jobs: - extension_support: 1 variant_tag: "" steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 with: python-version: '3.12' - - name: Get version tag - id: get_version - uses: battila7/get-version-action@v2 - run: | pip install ./sign_client pip install external/okta-0.0.3.1-py2.py3-none-any.whl @@ -135,9 +85,9 @@ jobs: - name: Get build run: | cd dist - 7z a user-sync-${{ steps.get_version.outputs.version }}${{matrix.variant_tag}}-win64.zip user-sync.exe + 7z a user-sync-${{ github.ref_name }}${{matrix.variant_tag}}-win64.zip user-sync.exe - name: Make artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: "windows${{matrix.variant_tag}}" path: dist/*.zip @@ -170,10 +120,7 @@ jobs: extension_support: 1 variant_tag: "" steps: - - uses: actions/checkout@v4 - - name: Get version tag - id: get_version - uses: battila7/get-version-action@v2 + - uses: actions/checkout@v7 - name: Update yum dependencies run: yum update -y - name: Rocky 8 dependencies @@ -217,10 +164,10 @@ jobs: tar czf "user-sync-${UST_VERSION}${UST_VARIANT_TAG}-${LABEL}.tar.gz" user-sync env: UST_VARIANT_TAG: ${{matrix.variant_tag}} - UST_VERSION: ${{ steps.get_version.outputs.version }} + UST_VERSION: ${{ github.ref_name }} CONTAINER: ${{ matrix.container }} - name: Make artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: "${{matrix.label}}${{matrix.variant_tag}}" path: dist/*.tar.gz @@ -229,12 +176,12 @@ jobs: examples: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: user-sync-examples-zip run: | cd examples zip -r user-sync-examples.zip . - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: examples path: examples/*.zip @@ -242,7 +189,7 @@ jobs: - name: user-sync.tar.gz run: | tar -czvf "user-sync-examples.tar.gz" examples - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: examples-tar path: user-sync-examples.tar.gz @@ -250,31 +197,30 @@ jobs: release: runs-on: ubuntu-latest - needs: [rocky, ubuntu-jammy, ubuntu-focal, examples, windows] + needs: [rocky, ubuntu, examples, windows] + permissions: + contents: write steps: - - uses: actions/checkout@v4 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v7 + - uses: actions/download-artifact@v8 + with: + path: artifacts + - name: Create release and upload assets + uses: softprops/action-gh-release@v3 with: - tag_name: ${{ github.ref }} - release_name: User Sync Tool ${{ github.ref }} + tag_name: ${{ github.ref_name }} + name: User Sync Tool ${{ github.ref_name }} body_path: .changelog/latest.md draft: false prerelease: false - - upload-assets: - needs: release - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@v4 - - name: Get version tag - id: get_version - uses: battila7/get-version-action@v2 - - uses: AButler/upload-release-assets@v2.0 - with: - files: 'examples/*;examples-tar/*;ubuntu-jammy/*;ubuntu-jammy-noext/*;ubuntu-focal/*;ubuntu-focal-noext/*;rocky8/*;rocky8-noext/*;rocky9/*;rocky9-noext/*' - repo-token: ${{ secrets.GITHUB_TOKEN }} - release-tag: ${{ steps.get_version.outputs.version }} + files: | + artifacts/examples/* + artifacts/examples-tar/* + artifacts/ubuntu-jammy/* + artifacts/ubuntu-jammy-noext/* + artifacts/ubuntu-noble/* + artifacts/ubuntu-noble-noext/* + artifacts/rocky8/* + artifacts/rocky8-noext/* + artifacts/rocky9/* + artifacts/rocky9-noext/* From 7e6b4be696ac88d5b162460051f4eac596d764ab Mon Sep 17 00:00:00 2001 From: Andrew Dorton Date: Fri, 2 Oct 2026 11:21:08 -0600 Subject: [PATCH 2/3] Document why Windows artifacts aren't attached to releases Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index f2158599..40d12925 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -213,6 +213,7 @@ jobs: body_path: .changelog/latest.md draft: false prerelease: false + # Windows builds are intentionally excluded; they're signed and uploaded by an internal build server files: | artifacts/examples/* artifacts/examples-tar/* From db1b423f39dde0d6fb9def42518118ccd74f20af Mon Sep 17 00:00:00 2001 From: Andrew Dorton Date: Fri, 2 Oct 2026 11:37:44 -0600 Subject: [PATCH 3/3] Remove dependency on pkg_resources and distutils Recent setuptools releases no longer ship pkg_resources, and the Makefile upgrades setuptools before building, which broke pre_build.py in CI. - pre_build.py: use importlib.metadata for package metadata - resource.py: use importlib.resources for package-mode resource paths - Makefile: use shutil.which instead of distutils (removed in Python 3.12) - user-sync.spec: drop obsolete pkg_resources.py2_warn hidden import Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .build/pre_build.py | 4 ++-- Makefile | 2 +- tests/test_resource.py | 11 +++-------- user-sync.spec | 2 +- user_sync/resource.py | 12 ++++++++---- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.build/pre_build.py b/.build/pre_build.py index 8c011658..307d1ec3 100644 --- a/.build/pre_build.py +++ b/.build/pre_build.py @@ -21,7 +21,7 @@ import os import shutil import json -import pkg_resources +from importlib import metadata from pathlib import Path import configparser @@ -67,7 +67,7 @@ def bundle_feature_flag_config(): def pkg_meta(): pkg_meta_path = Path('user_sync', 'resources', 'pkg_meta.json') with open(pkg_meta_path, 'w') as f: - json.dump({p.project_name: p.version for p in pkg_resources.working_set}, f) + json.dump({d.metadata['Name']: d.version for d in metadata.distributions()}, f) if __name__ == '__main__': diff --git a/Makefile b/Makefile index 74c9169a..1ee034ef 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ output_filename = user-sync prebuilt_dir = external ifeq ($(OS),Windows_NT) - rm_path := $(shell python -c "import distutils.spawn; print(distutils.spawn.find_executable('rm'))") + rm_path := $(shell python -c "import shutil; print(shutil.which('rm'))") ifeq ($(rm_path),None) RM := rmdir /S /Q else diff --git a/tests/test_resource.py b/tests/test_resource.py index 9d10cbb2..b3d29a1d 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -22,7 +22,6 @@ import os import sys import pytest -import pkg_resources from user_sync import resource @@ -45,7 +44,7 @@ def test_resource_file_package(resource_file, tmpdir, monkeypatch): tmpdir = str(tmpdir) with monkeypatch.context() as m: resfile = "test.txt" - m.setattr(pkg_resources, "resource_filename", lambda *args: os.path.join(tmpdir, resfile)) + m.setattr(resource, "_package_path", lambda *args: os.path.join(tmpdir, resfile)) assert resource_file(tmpdir, resfile) == resource.get_resource(resfile) @@ -68,7 +67,7 @@ def test_resource_invalid_file_package(tmpdir, monkeypatch): """test for non-existent resource file in a package""" tmpdir = str(tmpdir) with monkeypatch.context() as m: - m.setattr(pkg_resources, "resource_filename", lambda *args: os.path.join('invalid', 'file', 'path')) + m.setattr(resource, "_package_path", lambda *args: os.path.join('invalid', 'file', 'path')) resfile = os.path.join('invalid', 'file', 'path') assert resource.get_resource(resfile) is None @@ -101,14 +100,10 @@ def test_resource_dir_package(resource_file, tmpdir, monkeypatch): test_dir = os.path.join(tmpdir, "test") os.mkdir(test_dir) - m.setattr(pkg_resources, "resource_filename", lambda *args: os.path.join(tmpdir, test_dir)) + m.setattr(resource, "_package_path", lambda *args: test_dir) resfile = "test_{}.txt" - res_test_files = [resfile.format(n + 1) for n in range(3)] - - m.setattr(pkg_resources, "resource_listdir", lambda *args: res_test_files) - res_paths = [resource_file(test_dir, resfile.format(n + 1)) for n in range(3)] assert sorted(res_paths) == sorted(resource.get_resource_dir('test')) diff --git a/user-sync.spec b/user-sync.spec index e1448280..8c595bbc 100644 --- a/user-sync.spec +++ b/user-sync.spec @@ -13,7 +13,7 @@ a = Analysis(['user_sync/app.py'], ('user_sync/resources/shell_scripts/win', 'resources/shell_scripts/win'), ('user_sync/resources/shell_scripts/linux', 'resources/shell_scripts/linux'), ], - hiddenimports=['win32timezone', 'pkg_resources.py2_warn', 'keyring'], + hiddenimports=['win32timezone', 'keyring'], hookspath=['.build'], runtime_hooks=[], excludes=[], diff --git a/user_sync/resource.py b/user_sync/resource.py index 884a7a9b..efa26c75 100644 --- a/user_sync/resource.py +++ b/user_sync/resource.py @@ -21,7 +21,7 @@ import os import sys import enum -import pkg_resources +from importlib import resources _BUNDLE_DIR = "resources" @@ -30,6 +30,10 @@ _run_context = None +def _package_path(resource): + return str(resources.files(_PKG).joinpath(resource)) + + class RunContext(enum.Enum): EXEBundle = 'exe' Package = 'package' @@ -55,7 +59,7 @@ def get_resource(resource): assert getattr(sys, '_MEIPASS', False), "Bundle root dir is not set" resource_path = os.path.join(getattr(sys, '_MEIPASS'), "resources", resource) else: - resource_path = pkg_resources.resource_filename(_PKG, resource) + resource_path = _package_path(resource) if os.path.exists(resource_path) and os.path.isfile(resource_path): return resource_path return None @@ -81,6 +85,6 @@ def get_resource_dir(resource_dir): return [os.path.join(resource_path, f) for f in os.listdir(resource_path) if os.path.isfile(os.path.join(resource_path, f))] else: - resource_path = pkg_resources.resource_filename(_PKG, resource_dir) - return [os.path.join(resource_path, f) for f in pkg_resources.resource_listdir(_PKG, resource_dir) + resource_path = _package_path(resource_dir) + return [os.path.join(resource_path, f) for f in os.listdir(resource_path) if os.path.isfile(os.path.join(resource_path, f))]