Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .copier-answers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# available at https://github.com/gt-csse/copier-UvScaffolding.
#

_commit: v0.5.11
_commit: v0.5.13
_src_path: .
author_email: github@DavidBrownell.com
author_name: David Brownell
Expand All @@ -19,5 +19,5 @@ install_ty: false
license: MIT
project_name: FileBackup
python_package_name: FileBackup
python_versions: 3.10, 3.11, 3.12, 3.13
python_versions: 3.11, 3.12, 3.13
sign_artifacts_question: true
4 changes: 2 additions & 2 deletions .github/workflows/CICD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ jobs:
with:
# These values can be customized for your specific requirements.
operating_system_json_string: "[ 'macos-latest', 'ubuntu-latest', 'windows-latest' ]"
python_version_json_string: "['3.13', '3.12', '3.11', '3.10']"
python_package_version: "3.10"
python_version_json_string: "['3.13', '3.12', '3.11']"
python_package_version: "3.11"
coverage_badge_gist_id: "f15146b1b8fdc0a5d45ac0eb786a84f7"
coverage_badge_gist_username: "davidbrownell"

Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/CICD_impl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ jobs:

steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Install uv and python
uses: astral-sh/setup-uv@v7
Expand All @@ -259,6 +261,9 @@ jobs:
- name: Install Dependencies
run: uv sync --frozen

- name: Update Version
run: uv run python -m AutoGitSemVer.scripts.UpdatePythonVersion ./pyproject.toml ./src --verbose

- name: Build Binary
run: uv run python BuildBinary.py Build --verbose

Expand Down Expand Up @@ -317,7 +322,13 @@ jobs:

# ----------------------------------------------------------------------
release:
needs: [package_coverage, python_package, validate_python_package, validate_binary]
needs:
[
package_coverage,
python_package,
validate_python_package,
validate_binary,
]

name: Release
runs-on: ubuntu-latest
Expand All @@ -329,7 +340,7 @@ jobs:
GH_TOKEN: ${{ github.token }}

permissions:
contents: write # To tag the repository and create the release
contents: write # To tag the repository and create the release

steps:
- uses: actions/checkout@v6
Expand Down
53 changes: 51 additions & 2 deletions BuildBinary.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import textwrap
import uuid

from importlib import metadata
from pathlib import Path
from typing import Annotated, Optional

Expand Down Expand Up @@ -59,7 +60,49 @@ def Build(
# means that typeguard can't determine the types of the arguments, which causes it to raise
# an exception. The fix is to include the inflect source code in the built binary so that
# typeguard can find the function's types and not raise an exception.
inflect_filename = Path(inflect.__file__).relative_to(Path(__file__).parent)
#
# Note that this must be an absolute path; inflect is installed into the virtual
# environment, which is not guaranteed to live under this file's directory (for example,
# when UV_PROJECT_ENVIRONMENT points elsewhere).
inflect_filename = Path(inflect.__file__).resolve()

# FileBackup/__init__.py invokes importlib.metadata.version to determine the version of the
# application, which requires that this distribution's metadata is included in the binary.
#
# cx_Freeze automatically includes the metadata of the distributions that it detects, but it
# does not reliably detect this one. cx_Freeze (>= 8.7.0) maps import names to distributions
# using importlib.metadata.packages_distributions, and that function cannot map the
# "FileBackup" import name back to the "FileBackup" distribution because the distribution is
# installed in editable mode (its top_level.txt is empty and the package is made available
# via a .pth file). cx_Freeze then falls back to including any distribution that it did not
# map, which happens to work on a development machine but not on a build machine.
#
# Without this metadata, the binary terminates during startup with
# "importlib.metadata.PackageNotFoundError: No package metadata was found for FileBackup".
#
# Include the metadata explicitly so that it is found at runtime regardless of the above.
distribution = metadata.distribution("FileBackup")

dist_info_path = Path(str(distribution.locate_file(""))).resolve() / "{}-{}.dist-info".format(
distribution.name.replace("-", "_").lower(),
distribution.version,
)

if not dist_info_path.is_dir():
dm.WriteError(f"The distribution metadata at '{dist_info_path}' was not found.\n")
return

# Note that this is written as a single line so that it does not interfere with the
# textwrap.dedent invocation below.
zip_includes = ", ".join(
'(r"{}", "{}/{}")'.format(
filename,
dist_info_path.name,
filename.relative_to(dist_info_path).as_posix(),
)
for filename in sorted(dist_info_path.rglob("*"))
if filename.is_file()
)

configuration_filename = Path("setup{}.py".format(str(uuid.uuid4()).replace("-", "")))

Expand All @@ -76,7 +119,13 @@ def Build(
"include_files": [
(r"{inflect_filename}", "lib/inflect/{inflect_filename.name}"),
],
"packages": []
# This distribution's metadata is placed within the zip file because that is
# where importlib.metadata looks for it at runtime.
"zip_includes": [{zip_includes}],
# typeguard (pulled in by inflect) imports unittest.mock at module scope,
# but cx_Freeze does not detect unittest as a dependency. Without it, the
# binary fails at startup with "No module named 'unittest'".
"packages": ["unittest"],
}},
}},
executables = [
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ readme = "README.md"
authors = [
{ name = "David Brownell", email = "github@DavidBrownell.com" }
]
requires-python = ">= 3.10"
requires-python = ">= 3.11"
dependencies = [
"dbrownell-common>=0.15.0",
"paramiko>=3.5.1",
Expand All @@ -37,7 +37,6 @@ classifiers = [
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand Down
Loading