Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e472a9bb46 | ||
|
|
b6b3292407 | ||
|
|
c14d3dd04a |
@@ -0,0 +1,56 @@
|
|||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
dist_*/
|
||||||
|
pkg_dist_*/
|
||||||
|
*.deb
|
||||||
|
*.rpm
|
||||||
|
*.pkg.tar.zst
|
||||||
|
*.exe
|
||||||
|
*.asc
|
||||||
|
*.sha256
|
||||||
|
|
||||||
|
# Platform tools (downloaded at runtime)
|
||||||
|
src/platform-tools/
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
*.md
|
||||||
|
!CLAUDE.md
|
||||||
|
|
||||||
|
# CI/CD
|
||||||
|
.github/
|
||||||
|
|
||||||
|
# Other
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
@@ -32,15 +32,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
status-checks:
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: read
|
|
||||||
|
|
||||||
uses: ./.github/workflows/status-checks.yml
|
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
needs: status-checks
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -126,7 +118,6 @@ jobs:
|
|||||||
dist/android-file-handler-windows.sha256
|
dist/android-file-handler-windows.sha256
|
||||||
|
|
||||||
build-debian:
|
build-debian:
|
||||||
needs: status-checks
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: read
|
packages: read
|
||||||
@@ -144,50 +135,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Build executable
|
- name: Build and package Debian
|
||||||
run: |
|
run: |
|
||||||
# Container has Poetry and all dependencies pre-installed
|
# Container has Poetry and all dependencies pre-installed
|
||||||
|
# Python build script handles both PyInstaller build and fpm packaging
|
||||||
poetry install --no-interaction
|
poetry install --no-interaction
|
||||||
poetry run python scripts/build_package_linux.py
|
poetry run python scripts/build_package_linux.py
|
||||||
|
|
||||||
- name: Package .deb (fpm)
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if ! VERSION="$(poetry version -s 2>&1)"; then
|
|
||||||
echo "::error::Failed to read version from pyproject.toml"
|
|
||||||
echo "::error::Poetry output: $VERSION"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "$VERSION" ]; then
|
|
||||||
echo "::error::Version is empty in pyproject.toml"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Validate semver format
|
|
||||||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$ ]]; then
|
|
||||||
echo "::error::Invalid version format in pyproject.toml: $VERSION"
|
|
||||||
echo "::error::Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Using version: $VERSION"
|
|
||||||
PKG_DIR="pkg_dist_debian"
|
|
||||||
mkdir -p dist
|
|
||||||
echo "Packaging from $PKG_DIR"
|
|
||||||
ls -la "$PKG_DIR" || true
|
|
||||||
|
|
||||||
ICON_PATH="$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
|
||||||
PKG_ITEMS=( "usr/local/bin/android-file-handler" "usr/share/applications/android-file-handler.desktop" )
|
|
||||||
if [ -f "$ICON_PATH" ]; then
|
|
||||||
PKG_ITEMS+=( "usr/share/icons/hicolor/256x256/apps/android-file-handler.png" )
|
|
||||||
else
|
|
||||||
echo "Note: icon not present, packaging without icon"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fpm -s dir -t deb -n android-file-handler -v "$VERSION" \
|
|
||||||
--architecture amd64 --deb-user root --deb-group root \
|
|
||||||
--after-install scripts/debian_postinst.sh \
|
|
||||||
-p "dist/android-file-handler_${VERSION}_amd64.deb" -C "$PKG_DIR" "${PKG_ITEMS[@]}"
|
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -234,7 +188,6 @@ jobs:
|
|||||||
pkg_dist_debian/**
|
pkg_dist_debian/**
|
||||||
|
|
||||||
build-arch:
|
build-arch:
|
||||||
needs: status-checks
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: read
|
packages: read
|
||||||
@@ -257,7 +210,7 @@ jobs:
|
|||||||
- name: Pull Docker image
|
- name: Pull Docker image
|
||||||
run: docker pull ghcr.io/jmr-dev/android-file-handler-arch-builder:latest
|
run: docker pull ghcr.io/jmr-dev/android-file-handler-arch-builder:latest
|
||||||
|
|
||||||
- name: Build executable inside container
|
- name: Build and package Arch
|
||||||
run: |
|
run: |
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v ${{ github.workspace }}:/workspace \
|
-v ${{ github.workspace }}:/workspace \
|
||||||
@@ -268,52 +221,6 @@ jobs:
|
|||||||
ghcr.io/jmr-dev/android-file-handler-arch-builder:latest \
|
ghcr.io/jmr-dev/android-file-handler-arch-builder:latest \
|
||||||
sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
||||||
|
|
||||||
- name: Package pacman (fpm) inside container
|
|
||||||
run: |
|
|
||||||
docker run --rm \
|
|
||||||
-v ${{ github.workspace }}:/workspace \
|
|
||||||
-w /workspace \
|
|
||||||
-e FPM_VERSION=${{ env.FPM_VERSION }} \
|
|
||||||
ghcr.io/jmr-dev/android-file-handler-arch-builder:latest \
|
|
||||||
sh -c 'set -euo pipefail && \
|
|
||||||
if ! VERSION="$(poetry version -s 2>&1)"; then \
|
|
||||||
echo "::error::Failed to read version from pyproject.toml" >&2 && \
|
|
||||||
echo "::error::Poetry output: $VERSION" >&2 && \
|
|
||||||
exit 1; \
|
|
||||||
fi && \
|
|
||||||
if [ -z "$VERSION" ]; then \
|
|
||||||
echo "::error::Version is empty in pyproject.toml" >&2 && \
|
|
||||||
exit 1; \
|
|
||||||
fi && \
|
|
||||||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$ ]]; then \
|
|
||||||
echo "::error::Invalid version format in pyproject.toml: $VERSION" >&2 && \
|
|
||||||
echo "::error::Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)" >&2 && \
|
|
||||||
exit 1; \
|
|
||||||
fi && \
|
|
||||||
echo "Using version: $VERSION" && \
|
|
||||||
PKG_DIR="pkg_dist_arch" && \
|
|
||||||
mkdir -p dist && \
|
|
||||||
echo "Packaging from $PKG_DIR" && \
|
|
||||||
ls -la "$PKG_DIR" || true && \
|
|
||||||
ICON_PATH="$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png" && \
|
|
||||||
if [ -f "$ICON_PATH" ]; then \
|
|
||||||
fpm -s dir -t pacman -n android-file-handler -v "$VERSION" \
|
|
||||||
--architecture x86_64 \
|
|
||||||
-p "dist/android-file-handler-${VERSION}-1-x86_64.pkg.tar.zst" \
|
|
||||||
-C "$PKG_DIR" \
|
|
||||||
"usr/bin/android-file-handler" \
|
|
||||||
"usr/share/applications/android-file-handler.desktop" \
|
|
||||||
"usr/share/icons/hicolor/256x256/apps/android-file-handler.png"; \
|
|
||||||
else \
|
|
||||||
echo "Note: icon not present, packaging without icon" && \
|
|
||||||
fpm -s dir -t pacman -n android-file-handler -v "$VERSION" \
|
|
||||||
--architecture x86_64 \
|
|
||||||
-p "dist/android-file-handler-${VERSION}-1-x86_64.pkg.tar.zst" \
|
|
||||||
-C "$PKG_DIR" \
|
|
||||||
"usr/bin/android-file-handler" \
|
|
||||||
"usr/share/applications/android-file-handler.desktop"; \
|
|
||||||
fi'
|
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -360,7 +267,6 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
build-rhel:
|
build-rhel:
|
||||||
needs: status-checks
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: read
|
packages: read
|
||||||
@@ -378,48 +284,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Build RHEL package
|
- name: Build and package RHEL
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
|
||||||
# Container has Poetry and all dependencies pre-installed
|
# Container has Poetry and all dependencies pre-installed
|
||||||
|
# Python build script handles both PyInstaller build and fpm packaging
|
||||||
poetry install --no-interaction
|
poetry install --no-interaction
|
||||||
poetry run python scripts/build_package_linux.py
|
poetry run python scripts/build_package_linux.py
|
||||||
|
|
||||||
- name: Package RHEL (fpm)
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if ! VERSION="$(poetry version -s 2>&1)"; then
|
|
||||||
echo "::error::Failed to read version from pyproject.toml"
|
|
||||||
echo "::error::Poetry output: $VERSION"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "$VERSION" ]; then
|
|
||||||
echo "::error::Version is empty in pyproject.toml"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Validate semver format
|
|
||||||
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$ ]]; then
|
|
||||||
echo "::error::Invalid version format in pyproject.toml: $VERSION"
|
|
||||||
echo "::error::Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Using version: $VERSION"
|
|
||||||
PKG_DIR="pkg_dist_rhel"
|
|
||||||
mkdir -p dist
|
|
||||||
echo "Packaging from $PKG_DIR (version=$VERSION)"
|
|
||||||
ls -la "$PKG_DIR" || true
|
|
||||||
|
|
||||||
ICON_PATH="$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
|
||||||
PKG_ITEMS=( "usr/bin/android-file-handler" "usr/share/applications/android-file-handler.desktop" )
|
|
||||||
if [ -f "$ICON_PATH" ]; then
|
|
||||||
PKG_ITEMS+=( "usr/share/icons/hicolor/256x256/apps/android-file-handler.png" )
|
|
||||||
else
|
|
||||||
echo "Note: icon not present, packaging without icon"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fpm -s dir -t rpm -n android-file-handler -v "$VERSION" --architecture x86_64 --prefix /usr/bin --after-install scripts/rhel_postinst.sh -p "dist/android-file-handler-${VERSION}.x86_64.rpm" -C "$PKG_DIR" "${PKG_ITEMS[@]}"
|
|
||||||
|
|
||||||
- name: Import GPG key
|
- name: Import GPG key
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -468,7 +339,6 @@ jobs:
|
|||||||
|
|
||||||
do-release:
|
do-release:
|
||||||
needs:
|
needs:
|
||||||
- status-checks
|
|
||||||
- build-windows
|
- build-windows
|
||||||
- build-debian
|
- build-debian
|
||||||
- build-arch
|
- build-arch
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
poetry run pytest tests/ -v
|
poetry run pytest tests/ -v
|
||||||
|
|
||||||
build-windows:
|
build-and-package-windows:
|
||||||
needs: [run-unit-tests-windows]
|
needs: [run-unit-tests-windows]
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -155,7 +155,7 @@ jobs:
|
|||||||
dist/**/android-file-handler*.exe
|
dist/**/android-file-handler*.exe
|
||||||
dist/android-file-handler.exe
|
dist/android-file-handler.exe
|
||||||
|
|
||||||
build-debian:
|
build-and-package-debian:
|
||||||
needs: [run-unit-tests-debian, run-unit-tests-arch, run-unit-tests-rhel]
|
needs: [run-unit-tests-debian, run-unit-tests-arch, run-unit-tests-rhel]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -172,35 +172,13 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build executable
|
- name: Build and package Debian
|
||||||
run: |
|
run: |
|
||||||
# Container has Poetry and all dependencies pre-installed
|
# Container has Poetry and all dependencies pre-installed
|
||||||
|
# Python build script handles both PyInstaller build and fpm packaging
|
||||||
poetry install --no-interaction
|
poetry install --no-interaction
|
||||||
poetry run python scripts/build_package_linux.py
|
poetry run python scripts/build_package_linux.py
|
||||||
|
|
||||||
- name: Package .deb (fpm)
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
VERSION="$(poetry version -s)"
|
|
||||||
PKG_DIR="pkg_dist_debian"
|
|
||||||
mkdir -p dist
|
|
||||||
echo "Packaging from $PKG_DIR"
|
|
||||||
ls -la "$PKG_DIR" || true
|
|
||||||
|
|
||||||
ICON_PATH="$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
|
||||||
PKG_ITEMS=( "usr/local/bin/android-file-handler" "usr/share/applications/android-file-handler.desktop" )
|
|
||||||
if [ -f "$ICON_PATH" ]; then
|
|
||||||
PKG_ITEMS+=( "usr/share/icons/hicolor/256x256/apps/android-file-handler.png" )
|
|
||||||
else
|
|
||||||
echo "Note: icon not present, packaging without icon"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fpm -s dir -t deb -n android-file-handler -v "$VERSION" \
|
|
||||||
--architecture amd64 --deb-user root --deb-group root \
|
|
||||||
--after-install scripts/debian_postinst.sh \
|
|
||||||
-p "dist/android-file-handler_${VERSION}_amd64.deb" -C "$PKG_DIR" "${PKG_ITEMS[@]}"
|
|
||||||
|
|
||||||
- name: Upload Debian .deb
|
- name: Upload Debian .deb
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -209,7 +187,7 @@ jobs:
|
|||||||
dist/android-file-handler_*.deb
|
dist/android-file-handler_*.deb
|
||||||
pkg_dist_debian/**
|
pkg_dist_debian/**
|
||||||
|
|
||||||
build-arch:
|
build-and-package-arch:
|
||||||
needs: [run-unit-tests-debian, run-unit-tests-arch, run-unit-tests-rhel]
|
needs: [run-unit-tests-debian, run-unit-tests-arch, run-unit-tests-rhel]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -219,20 +197,19 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.CI_CD_PAT }} # Personal Access Token with read:packages
|
password: ${{ secrets.CI_CD_PAT }}
|
||||||
|
|
||||||
|
|
||||||
- name: Pull Docker image
|
- name: Pull Docker image
|
||||||
run: docker pull ghcr.io/jmr-dev/android-file-handler-arch-builder:latest
|
run: docker pull ghcr.io/jmr-dev/android-file-handler-arch-builder:latest
|
||||||
|
|
||||||
- name: Build executable inside container
|
- name: Build and package Arch
|
||||||
run: |
|
run: |
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v ${{ github.workspace }}:/workspace \
|
-v ${{ github.workspace }}:/workspace \
|
||||||
@@ -243,38 +220,6 @@ jobs:
|
|||||||
ghcr.io/jmr-dev/android-file-handler-arch-builder:latest \
|
ghcr.io/jmr-dev/android-file-handler-arch-builder:latest \
|
||||||
sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
||||||
|
|
||||||
- name: Package pacman (fpm) inside container
|
|
||||||
run: |
|
|
||||||
docker run --rm \
|
|
||||||
-v ${{ github.workspace }}:/workspace \
|
|
||||||
-w /workspace \
|
|
||||||
-e FPM_VERSION=${{ env.FPM_VERSION }} \
|
|
||||||
ghcr.io/jmr-dev/android-file-handler-arch-builder:latest \
|
|
||||||
sh -c 'set -euo pipefail && \
|
|
||||||
VERSION="$(poetry version -s)" && \
|
|
||||||
PKG_DIR="pkg_dist_arch" && \
|
|
||||||
mkdir -p dist && \
|
|
||||||
echo "Packaging from $PKG_DIR" && \
|
|
||||||
ls -la "$PKG_DIR" || true && \
|
|
||||||
ICON_PATH="$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png" && \
|
|
||||||
if [ -f "$ICON_PATH" ]; then \
|
|
||||||
fpm -s dir -t pacman -n android-file-handler -v "$VERSION" \
|
|
||||||
--architecture x86_64 \
|
|
||||||
-p "dist/android-file-handler-${VERSION}-1-x86_64.pkg.tar.zst" \
|
|
||||||
-C "$PKG_DIR" \
|
|
||||||
"usr/bin/android-file-handler" \
|
|
||||||
"usr/share/applications/android-file-handler.desktop" \
|
|
||||||
"usr/share/icons/hicolor/256x256/apps/android-file-handler.png"; \
|
|
||||||
else \
|
|
||||||
echo "Note: icon not present, packaging without icon" && \
|
|
||||||
fpm -s dir -t pacman -n android-file-handler -v "$VERSION" \
|
|
||||||
--architecture x86_64 \
|
|
||||||
-p "dist/android-file-handler-${VERSION}-1-x86_64.pkg.tar.zst" \
|
|
||||||
-C "$PKG_DIR" \
|
|
||||||
"usr/bin/android-file-handler" \
|
|
||||||
"usr/share/applications/android-file-handler.desktop"; \
|
|
||||||
fi'
|
|
||||||
|
|
||||||
- name: Upload Arch package
|
- name: Upload Arch package
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -283,7 +228,7 @@ jobs:
|
|||||||
dist/*.pkg.tar.*
|
dist/*.pkg.tar.*
|
||||||
pkg_dist_arch/**
|
pkg_dist_arch/**
|
||||||
|
|
||||||
build-rhel:
|
build-and-package-rhel:
|
||||||
needs: [run-unit-tests-debian, run-unit-tests-arch, run-unit-tests-rhel]
|
needs: [run-unit-tests-debian, run-unit-tests-arch, run-unit-tests-rhel]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -300,33 +245,13 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build RHEL package
|
- name: Build and package RHEL
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
|
||||||
# Container has Poetry and all dependencies pre-installed
|
# Container has Poetry and all dependencies pre-installed
|
||||||
|
# Python build script handles both PyInstaller build and fpm packaging
|
||||||
poetry install --no-interaction
|
poetry install --no-interaction
|
||||||
poetry run python scripts/build_package_linux.py
|
poetry run python scripts/build_package_linux.py
|
||||||
|
|
||||||
- name: Package RHEL (fpm)
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
VERSION="$(poetry version -s)"
|
|
||||||
PKG_DIR="pkg_dist_rhel"
|
|
||||||
mkdir -p dist
|
|
||||||
echo "Packaging from $PKG_DIR (version=$VERSION)"
|
|
||||||
ls -la "$PKG_DIR" || true
|
|
||||||
|
|
||||||
ICON_PATH="$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
|
||||||
PKG_ITEMS=( "usr/bin/android-file-handler" "usr/share/applications/android-file-handler.desktop" )
|
|
||||||
if [ -f "$ICON_PATH" ]; then
|
|
||||||
PKG_ITEMS+=( "usr/share/icons/hicolor/256x256/apps/android-file-handler.png" )
|
|
||||||
else
|
|
||||||
echo "Note: icon not present, packaging without icon"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fpm -s dir -t rpm -n android-file-handler -v "$VERSION" --architecture x86_64 --prefix /usr/bin --after-install scripts/rhel_postinst.sh -p "dist/android-file-handler-${VERSION}.x86_64.rpm" -C "$PKG_DIR" "${PKG_ITEMS[@]}"
|
|
||||||
|
|
||||||
- name: Upload RHEL artifacts
|
- name: Upload RHEL artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -54,9 +54,29 @@ poetry run mypy src/
|
|||||||
|
|
||||||
#### Local Development Build (Linux)
|
#### Local Development Build (Linux)
|
||||||
```sh
|
```sh
|
||||||
|
# Interactive build (prompts for distro selection)
|
||||||
poetry run python scripts/build_package_linux.py
|
poetry run python scripts/build_package_linux.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Docker Compose Build (Recommended for Linux)
|
||||||
|
```sh
|
||||||
|
# Build all distributions (Debian, Arch, RHEL)
|
||||||
|
docker compose up --build
|
||||||
|
|
||||||
|
# Build specific distribution
|
||||||
|
docker compose up --build debian
|
||||||
|
docker compose up --build arch
|
||||||
|
docker compose up --build rhel
|
||||||
|
|
||||||
|
# Build all in parallel
|
||||||
|
docker compose up --build --parallel
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
docker compose down -v && rm -rf dist pkg_dist_* dist_*
|
||||||
|
```
|
||||||
|
|
||||||
|
See [scripts/docker/README.md](scripts/docker/README.md) for detailed Docker build documentation.
|
||||||
|
|
||||||
#### Platform-Specific Builds
|
#### Platform-Specific Builds
|
||||||
```sh
|
```sh
|
||||||
# Windows executable (PyInstaller)
|
# Windows executable (PyInstaller)
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Docker Compose configuration for local multi-platform builds
|
||||||
|
# Matches the exact images and configurations from .github/workflows/release.yml
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# Build all distributions: docker-compose up --build
|
||||||
|
# Build specific distro: docker-compose up --build debian
|
||||||
|
#
|
||||||
|
# Each service builds a distribution package and outputs to:
|
||||||
|
# - dist/ - Final packaged files (.deb, .rpm, .pkg.tar.zst)
|
||||||
|
# - pkg_dist_{distro}/ - Staging directory for package contents
|
||||||
|
# - dist_{distro}/ - PyInstaller build output
|
||||||
|
|
||||||
|
services:
|
||||||
|
debian:
|
||||||
|
image: ghcr.io/jmr-dev/android-file-handler-debian-builder:debian13-trixie
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: scripts/docker/Dockerfile.debian
|
||||||
|
args:
|
||||||
|
FPM_VERSION: "1.16.0"
|
||||||
|
volumes:
|
||||||
|
- .:/workspace
|
||||||
|
# Exclude host .venv to prevent conflicts with container Python
|
||||||
|
- /workspace/.venv
|
||||||
|
working_dir: /workspace
|
||||||
|
environment:
|
||||||
|
- CI_CD=true
|
||||||
|
- DISTRO_TYPE=debian
|
||||||
|
- FPM_VERSION=1.16.0
|
||||||
|
- POETRY_VIRTUALENVS_IN_PROJECT=false
|
||||||
|
- POETRY_VIRTUALENVS_PATH=/tmp/poetry-cache
|
||||||
|
command: sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
||||||
|
|
||||||
|
arch:
|
||||||
|
image: ghcr.io/jmr-dev/android-file-handler-arch-builder:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: scripts/docker/Dockerfile.arch
|
||||||
|
args:
|
||||||
|
FPM_VERSION: "1.16.0"
|
||||||
|
volumes:
|
||||||
|
- .:/workspace
|
||||||
|
# Exclude host .venv to prevent conflicts with container Python
|
||||||
|
- /workspace/.venv
|
||||||
|
working_dir: /workspace
|
||||||
|
environment:
|
||||||
|
- CI_CD=true
|
||||||
|
- DISTRO_TYPE=arch
|
||||||
|
- FPM_VERSION=1.16.0
|
||||||
|
- POETRY_VIRTUALENVS_IN_PROJECT=false
|
||||||
|
- POETRY_VIRTUALENVS_PATH=/tmp/poetry-cache
|
||||||
|
command: sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
||||||
|
|
||||||
|
rhel:
|
||||||
|
image: ghcr.io/jmr-dev/android-file-handler-rhel-builder:fedora42
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: scripts/docker/Dockerfile.rhel
|
||||||
|
args:
|
||||||
|
FPM_VERSION: "1.16.0"
|
||||||
|
volumes:
|
||||||
|
- .:/workspace
|
||||||
|
# Exclude host .venv to prevent conflicts with container Python
|
||||||
|
- /workspace/.venv
|
||||||
|
working_dir: /workspace
|
||||||
|
environment:
|
||||||
|
- CI_CD=true
|
||||||
|
- DISTRO_TYPE=rhel
|
||||||
|
- FPM_VERSION=1.16.0
|
||||||
|
- POETRY_VIRTUALENVS_IN_PROJECT=false
|
||||||
|
- POETRY_VIRTUALENVS_PATH=/tmp/poetry-cache
|
||||||
|
command: sh -c "poetry install --no-interaction && poetry run python scripts/build_package_linux.py"
|
||||||
+116
-12
@@ -4,6 +4,7 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List
|
from typing import List
|
||||||
@@ -35,24 +36,130 @@ def get_distro_config(distro_type: DistroType) -> dict:
|
|||||||
"name": "Debian",
|
"name": "Debian",
|
||||||
"bin_path": "usr/local/bin",
|
"bin_path": "usr/local/bin",
|
||||||
"pkg_suffix": "debian",
|
"pkg_suffix": "debian",
|
||||||
"spec_file": "scripts/spec_scripts/android-file-handler-debian.spec"
|
"spec_file": "scripts/spec_scripts/android-file-handler-debian.spec",
|
||||||
|
"pkg_type": "deb",
|
||||||
|
"architecture": "amd64",
|
||||||
|
"postinstall": "scripts/debian_postinst.sh"
|
||||||
},
|
},
|
||||||
DistroType.ARCH: {
|
DistroType.ARCH: {
|
||||||
"name": "Arch",
|
"name": "Arch",
|
||||||
"bin_path": "usr/bin",
|
"bin_path": "usr/bin",
|
||||||
"pkg_suffix": "arch",
|
"pkg_suffix": "arch",
|
||||||
"spec_file": "scripts/spec_scripts/android-file-handler-arch.spec"
|
"spec_file": "scripts/spec_scripts/android-file-handler-arch.spec",
|
||||||
|
"pkg_type": "pacman",
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"postinstall": None
|
||||||
},
|
},
|
||||||
DistroType.RHEL: {
|
DistroType.RHEL: {
|
||||||
"name": "RHEL",
|
"name": "RHEL",
|
||||||
"bin_path": "usr/bin",
|
"bin_path": "usr/bin",
|
||||||
"pkg_suffix": "rhel",
|
"pkg_suffix": "rhel",
|
||||||
"spec_file": "scripts/spec_scripts/android-file-handler-rhel.spec"
|
"spec_file": "scripts/spec_scripts/android-file-handler-rhel.spec",
|
||||||
|
"pkg_type": "rpm",
|
||||||
|
"architecture": "x86_64",
|
||||||
|
"postinstall": "scripts/rhel_postinst.sh"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return configs[distro_type]
|
return configs[distro_type]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_version(version: str) -> bool:
|
||||||
|
"""Validate semantic version format."""
|
||||||
|
semver_pattern = r'^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$'
|
||||||
|
return bool(re.match(semver_pattern, version))
|
||||||
|
|
||||||
|
|
||||||
|
def get_version() -> str:
|
||||||
|
"""Get version from Poetry and validate it."""
|
||||||
|
result = subprocess.run(
|
||||||
|
["poetry", "version", "-s"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
version = result.stdout.strip()
|
||||||
|
|
||||||
|
if not version:
|
||||||
|
print("ERROR: Version is empty in pyproject.toml")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not validate_version(version):
|
||||||
|
print(f"ERROR: Invalid version format in pyproject.toml: {version}")
|
||||||
|
print("Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
def package_with_fpm(distro_type: DistroType, version: str, project_root: Path) -> None:
|
||||||
|
"""Package the built application using fpm."""
|
||||||
|
config = get_distro_config(distro_type)
|
||||||
|
pkg_dir = project_root / f"pkg_dist_{config['pkg_suffix']}"
|
||||||
|
dist_dir = project_root / "dist"
|
||||||
|
|
||||||
|
print(f"\n=== Packaging {config['name']} with fpm ===")
|
||||||
|
|
||||||
|
# Ensure dist directory exists
|
||||||
|
dist_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# Check if icon exists
|
||||||
|
icon_path = pkg_dir / "usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
||||||
|
icon_included = icon_path.exists()
|
||||||
|
|
||||||
|
# Build package items list
|
||||||
|
pkg_items = [
|
||||||
|
f"{config['bin_path']}/android-file-handler",
|
||||||
|
"usr/share/applications/android-file-handler.desktop"
|
||||||
|
]
|
||||||
|
|
||||||
|
if icon_included:
|
||||||
|
pkg_items.append("usr/share/icons/hicolor/256x256/apps/android-file-handler.png")
|
||||||
|
else:
|
||||||
|
print("Note: icon not present, packaging without icon")
|
||||||
|
|
||||||
|
# Build fpm command based on distro type
|
||||||
|
fpm_cmd = [
|
||||||
|
"fpm",
|
||||||
|
"-s", "dir",
|
||||||
|
"-t", config["pkg_type"],
|
||||||
|
"-n", "android-file-handler",
|
||||||
|
"-v", version,
|
||||||
|
"--architecture", config["architecture"],
|
||||||
|
"-C", str(pkg_dir)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add distro-specific options
|
||||||
|
if distro_type == DistroType.DEBIAN:
|
||||||
|
output_file = dist_dir / f"android-file-handler_{version}_{config['architecture']}.deb"
|
||||||
|
fpm_cmd.extend([
|
||||||
|
"--deb-user", "root",
|
||||||
|
"--deb-group", "root",
|
||||||
|
"--after-install", config["postinstall"],
|
||||||
|
"-p", str(output_file)
|
||||||
|
])
|
||||||
|
elif distro_type == DistroType.ARCH:
|
||||||
|
output_file = dist_dir / f"android-file-handler-{version}-1-{config['architecture']}.pkg.tar.zst"
|
||||||
|
fpm_cmd.extend([
|
||||||
|
"-p", str(output_file)
|
||||||
|
])
|
||||||
|
elif distro_type == DistroType.RHEL:
|
||||||
|
output_file = dist_dir / f"android-file-handler-{version}.{config['architecture']}.rpm"
|
||||||
|
fpm_cmd.extend([
|
||||||
|
"--prefix", "/usr/bin",
|
||||||
|
"--after-install", config["postinstall"],
|
||||||
|
"-p", str(output_file)
|
||||||
|
])
|
||||||
|
|
||||||
|
# Add package items
|
||||||
|
fpm_cmd.extend(pkg_items)
|
||||||
|
|
||||||
|
# Run fpm
|
||||||
|
print(f"Creating package: {output_file}")
|
||||||
|
run_command(fpm_cmd, working_dir=str(project_root))
|
||||||
|
|
||||||
|
print(f"Package created successfully: {output_file}")
|
||||||
|
|
||||||
|
|
||||||
def prompt_distro_selection() -> List[DistroType]:
|
def prompt_distro_selection() -> List[DistroType]:
|
||||||
"""Prompt user for distro selection."""
|
"""Prompt user for distro selection."""
|
||||||
print("Select distribution(s) to build for:")
|
print("Select distribution(s) to build for:")
|
||||||
@@ -161,7 +268,7 @@ StartupNotify=true
|
|||||||
pkg_items.append("usr/share/icons/hicolor/256x256/apps/android-file-handler.png")
|
pkg_items.append("usr/share/icons/hicolor/256x256/apps/android-file-handler.png")
|
||||||
|
|
||||||
# Debug listing
|
# Debug listing
|
||||||
print(f"Packaging the following items for {config['name']} (relative to {pkg_dir}):")
|
print(f"Prepared items for {config['name']} (relative to {pkg_dir}):")
|
||||||
for item in pkg_items:
|
for item in pkg_items:
|
||||||
print(f" - {item}")
|
print(f" - {item}")
|
||||||
item_path = pkg_dir / item
|
item_path = pkg_dir / item
|
||||||
@@ -171,6 +278,9 @@ StartupNotify=true
|
|||||||
else:
|
else:
|
||||||
print(f" (missing) {item_path}")
|
print(f" (missing) {item_path}")
|
||||||
|
|
||||||
|
# Package with fpm
|
||||||
|
package_with_fpm(distro_type, version, project_root)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
# Get project root (parent of scripts directory)
|
# Get project root (parent of scripts directory)
|
||||||
@@ -211,14 +321,8 @@ def main() -> None:
|
|||||||
run_command(["poetry", "lock"])
|
run_command(["poetry", "lock"])
|
||||||
run_command(["poetry", "install"])
|
run_command(["poetry", "install"])
|
||||||
|
|
||||||
# Get version from Poetry
|
# Get and validate version from Poetry
|
||||||
result = subprocess.run(
|
version = get_version()
|
||||||
["poetry", "version", "-s"],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
check=True
|
|
||||||
)
|
|
||||||
version = result.stdout.strip()
|
|
||||||
print(f"Version: {version}")
|
print(f"Version: {version}")
|
||||||
|
|
||||||
# Build for selected distributions
|
# Build for selected distributions
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ ENV PATH="$PYENV_ROOT/bin:$PATH"
|
|||||||
|
|
||||||
RUN git clone https://github.com/pyenv/pyenv.git /root/.pyenv
|
RUN git clone https://github.com/pyenv/pyenv.git /root/.pyenv
|
||||||
|
|
||||||
# Install Python 3.12 via pyenv with tkinter support
|
# Install Python 3.13 via pyenv with tkinter support
|
||||||
# The tk8-devel package must be installed before this step for _tkinter to be compiled
|
# The tk8-devel package must be installed before this step for _tkinter to be compiled
|
||||||
RUN eval "$(pyenv init -)" && \
|
RUN eval "$(pyenv init -)" && \
|
||||||
LDFLAGS="-L/usr/lib64" \
|
LDFLAGS="-L/usr/lib64" \
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
# Docker Build Environment
|
||||||
|
|
||||||
|
This directory contains Dockerfiles for building the Android File Handler on different Linux distributions. These images match exactly the images used in the CI/CD pipeline.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Using Docker Compose (Recommended)
|
||||||
|
|
||||||
|
Build for all distributions:
|
||||||
|
```bash
|
||||||
|
docker-compose up --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Build for a specific distribution:
|
||||||
|
```bash
|
||||||
|
docker-compose up --build debian
|
||||||
|
docker-compose up --build arch
|
||||||
|
docker-compose up --build rhel
|
||||||
|
```
|
||||||
|
|
||||||
|
Build all distributions in parallel:
|
||||||
|
```bash
|
||||||
|
docker-compose up --build --parallel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Docker Build
|
||||||
|
|
||||||
|
Build the image:
|
||||||
|
```bash
|
||||||
|
# Debian
|
||||||
|
docker build -f scripts/docker/Dockerfile.debian -t android-file-handler-debian-builder .
|
||||||
|
|
||||||
|
# Arch
|
||||||
|
docker build -f scripts/docker/Dockerfile.arch -t android-file-handler-arch-builder .
|
||||||
|
|
||||||
|
# RHEL/Fedora
|
||||||
|
docker build -f scripts/docker/Dockerfile.rhel -t android-file-handler-rhel-builder .
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the build:
|
||||||
|
```bash
|
||||||
|
# Debian
|
||||||
|
docker run --rm -v $(pwd):/workspace -w /workspace android-file-handler-debian-builder
|
||||||
|
|
||||||
|
# Arch
|
||||||
|
docker run --rm -v $(pwd):/workspace -w /workspace android-file-handler-arch-builder
|
||||||
|
|
||||||
|
# RHEL/Fedora
|
||||||
|
docker run --rm -v $(pwd):/workspace -w /workspace android-file-handler-rhel-builder
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
After building, you'll find:
|
||||||
|
|
||||||
|
- `dist/` - Final packaged files (.deb, .rpm, .pkg.tar.zst)
|
||||||
|
- `pkg_dist_{distro}/` - Staging directories for package contents
|
||||||
|
- `dist_{distro}/` - PyInstaller build outputs
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
### Debian Builder
|
||||||
|
- **Image**: `ghcr.io/jmr-dev/android-file-handler-debian-builder:debian13-trixie`
|
||||||
|
- **Base**: `debian:13`
|
||||||
|
- **Python**: 3.13 (via pyenv)
|
||||||
|
- **Tools**: Poetry, fpm, PyInstaller
|
||||||
|
|
||||||
|
### Arch Builder
|
||||||
|
- **Image**: `ghcr.io/jmr-dev/android-file-handler-arch-builder:latest`
|
||||||
|
- **Base**: `archlinux:latest`
|
||||||
|
- **Python**: 3.13 (via pyenv)
|
||||||
|
- **Tools**: Poetry, fpm, PyInstaller
|
||||||
|
|
||||||
|
### RHEL Builder
|
||||||
|
- **Image**: `ghcr.io/jmr-dev/android-file-handler-rhel-builder:fedora42`
|
||||||
|
- **Base**: `fedora:42`
|
||||||
|
- **Python**: 3.13 (via pyenv)
|
||||||
|
- **Tools**: Poetry, fpm, PyInstaller
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Virtualenv Conflicts
|
||||||
|
|
||||||
|
The Docker Compose configuration automatically excludes the host's `.venv` directory to prevent conflicts between the host Python environment and the container Python environment. Each container creates its own virtualenv in `/tmp/poetry-cache`.
|
||||||
|
|
||||||
|
If you encounter virtualenv-related errors, ensure you're using the latest docker-compose.yml configuration.
|
||||||
|
|
||||||
|
## Cleaning Up
|
||||||
|
|
||||||
|
Remove build artifacts:
|
||||||
|
```bash
|
||||||
|
rm -rf dist pkg_dist_* dist_*
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove Docker volumes and containers:
|
||||||
|
```bash
|
||||||
|
docker compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
### Override FPM Version
|
||||||
|
|
||||||
|
Build with a specific fpm version:
|
||||||
|
```bash
|
||||||
|
docker-compose build --build-arg FPM_VERSION=1.15.0 debian
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
All builds use these environment variables:
|
||||||
|
- `CI_CD=true` - Runs in CI/CD mode
|
||||||
|
- `DISTRO_TYPE` - Set to `debian`, `arch`, or `rhel`
|
||||||
|
- `FPM_VERSION` - Version of fpm to use (default: 1.16.0)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- All images use Python 3.13 built from source with tkinter support
|
||||||
|
- The builds are identical to what runs in GitHub Actions
|
||||||
|
- Poetry and fpm are pre-installed in all images
|
||||||
|
- The Python build script handles both PyInstaller and fpm packaging
|
||||||
Reference in New Issue
Block a user