initial refactor of ci-cd pipeline

This commit is contained in:
Jason Ross
2026-03-20 19:30:22 -05:00
parent b269fdc11d
commit 3a5d4e7dbd
19 changed files with 1216 additions and 168 deletions
+20 -7
View File
@@ -7,7 +7,7 @@ import sys
import re
from pathlib import Path
from enum import Enum
from typing import List
from typing import List, TypedDict
class DistroType(Enum):
@@ -16,7 +16,19 @@ class DistroType(Enum):
RHEL = "rhel"
def run_command(cmd: list[str], check: bool = True, working_dir: str | None = None) -> subprocess.CompletedProcess:
class DistroConfigDict(TypedDict):
"""Type definition for distro configuration."""
name: str
bin_path: str
pkg_suffix: str
spec_file: str
pkg_type: str
architecture: str
postinstall: str | None
def run_command(cmd: list[str], check: bool = True, working_dir: str | None = None) -> subprocess.CompletedProcess[bytes]:
"""Run command and handle errors."""
print(f"Running: {' '.join(cmd)}")
try:
@@ -29,9 +41,9 @@ def run_command(cmd: list[str], check: bool = True, working_dir: str | None = No
sys.exit(1)
def get_distro_config(distro_type: DistroType) -> dict:
def get_distro_config(distro_type: DistroType) -> DistroConfigDict:
"""Get configuration for specific distro type."""
configs = {
configs: dict[DistroType, DistroConfigDict] = {
DistroType.DEBIAN: {
"name": "Debian",
"bin_path": "usr/local/bin",
@@ -131,10 +143,11 @@ def package_with_fpm(distro_type: DistroType, version: str, project_root: Path)
# Add distro-specific options
if distro_type == DistroType.DEBIAN:
output_file = dist_dir / f"android-file-handler_{version}_{config['architecture']}.deb"
postinstall = config["postinstall"]
fpm_cmd.extend([
"--deb-user", "root",
"--deb-group", "root",
"--after-install", config["postinstall"],
*(["--after-install", postinstall] if postinstall else []),
"-p", str(output_file)
])
elif distro_type == DistroType.ARCH:
@@ -144,9 +157,9 @@ def package_with_fpm(distro_type: DistroType, version: str, project_root: Path)
])
elif distro_type == DistroType.RHEL:
output_file = dist_dir / f"android-file-handler-{version}.{config['architecture']}.rpm"
postinstall = config["postinstall"]
fpm_cmd.extend([
"--prefix", "/usr/bin",
"--after-install", config["postinstall"],
*(["--after-install", postinstall] if postinstall else []),
"-p", str(output_file)
])
+29 -20
View File
@@ -1,52 +1,57 @@
# Docker Build Environment
# Container 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.
This directory contains OCI-compatible Containerfiles (Dockerfiles) for building the Android File Handler on different Linux distributions. These images match exactly the images used in the CI/CD pipeline.
The project uses **Podman** as the container runtime. All commands below use Podman; if you have Docker installed, the Dockerfiles are OCI-compatible and will work with Docker as well.
## Quick Start
### Using Docker Compose (Recommended)
### Using Podman Compose (Recommended)
Build for all distributions:
```bash
docker-compose up --build
podman-compose up --build
```
Build for a specific distribution:
```bash
docker-compose up --build debian
docker-compose up --build arch
docker-compose up --build rhel
podman-compose up --build debian
podman-compose up --build arch
podman-compose up --build rhel
```
Build all distributions in parallel:
### Using Prefect + Dagger (CI Pipeline Locally)
The CI/CD pipeline uses Prefect and Dagger to orchestrate builds. You can run it locally:
```bash
docker-compose up --build --parallel
poetry install --with ci
poetry run python -m ci.prefect_flow build-linux
```
### Manual Docker Build
### Manual Podman Build
Build the image:
```bash
# Debian
docker build -f scripts/docker/Dockerfile.debian -t android-file-handler-debian-builder .
podman 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 .
podman 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 .
podman 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
podman run --rm -v $(pwd):/workspace:Z -w /workspace android-file-handler-debian-builder
# Arch
docker run --rm -v $(pwd):/workspace -w /workspace android-file-handler-arch-builder
podman run --rm -v $(pwd):/workspace:Z -w /workspace android-file-handler-arch-builder
# RHEL/Fedora
docker run --rm -v $(pwd):/workspace -w /workspace android-file-handler-rhel-builder
podman run --rm -v $(pwd):/workspace:Z -w /workspace android-file-handler-rhel-builder
```
## Output
@@ -81,9 +86,13 @@ After building, you'll find:
### 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`.
The Podman 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.
If you encounter virtualenv-related errors, ensure you're using the latest podman-compose.yml configuration.
### SELinux (Fedora/RHEL hosts)
Volume mounts use the `:Z` suffix to apply the correct SELinux labels automatically. If you encounter permission errors, ensure the `:Z` suffix is present on volume mounts.
## Cleaning Up
@@ -92,9 +101,9 @@ Remove build artifacts:
rm -rf dist pkg_dist_* dist_*
```
Remove Docker volumes and containers:
Remove Podman containers and volumes:
```bash
docker compose down -v
podman-compose down -v
```
## Customization
+1 -1
View File
@@ -19,7 +19,7 @@ if command -v gtk-update-icon-cache >/dev/null 2>&1; then
fi
# Ensure installed binary is executable
if [ -f /usr/local/bin/android-file-handler ]; then
if [ -f /usr/bin/android-file-handler ]; then
chmod 0755 /usr/bin/android-file-handler || true
fi
@@ -5,8 +5,7 @@ a = Analysis(
pathex=['../..'],
binaries=[],
datas=[
('../../src/gui', 'gui'),
('../windows/first_run_install.ps1', 'scripts/windows')
('../../src/gui', 'gui'),
],
hiddenimports=[
# GUI modules
@@ -0,0 +1,56 @@
; Inno Setup script for Android File Handler
; Compiles a Windows installer from the PyInstaller one-file executable.
;
; Usage (CI):
; iscc scripts\windows\android-file-handler-setup.iss /DMyAppVersion=1.2.3
;
; Usage (local, from repo root):
; "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" scripts\windows\android-file-handler-setup.iss
#ifndef MyAppVersion
#define MyAppVersion "0.1.1"
#endif
#define MyAppName "Android File Handler"
#define MyAppPublisher "Jason Ross"
#define MyAppURL "https://github.com/JMR-dev/android-file-handler"
#define MyAppExeName "android-file-handler-windows.exe"
[Setup]
AppId={{8F2B3A7E-4D1C-4E8F-9A2B-6C7D8E9F0A1B}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DefaultGroupName={#MyAppName}
LicenseFile=..\..\LICENSE.txt
OutputDir=..\..\dist
OutputBaseFilename=android-file-handler-setup
SetupIconFile=..\..\icon_media\robot_files_256.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma2/ultra64
SolidCompression=yes
WizardStyle=modern
ArchitecturesInstallIn64BitMode=x64compatible
PrivilegesRequired=admin
MinVersion=10.0
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "..\..\dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
-51
View File
@@ -1,51 +0,0 @@
param(
[string]$ExePath = "$PSScriptRoot\..\..\dist\android-file-handler.exe",
[string]$IconPath = "$PSScriptRoot\..\..\assets\icons\android-file-handler.ico",
[string]$AppName = "Android File Handler"
)
function Ensure-Elevated {
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
# Relaunch the script with elevation
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = "powershell.exe"
$psi.Arguments = "-ExecutionPolicy Bypass -File `"$PSCommandPath`""
$psi.Verb = "runas"
try {
[System.Diagnostics.Process]::Start($psi) | Out-Null
Exit 0
} catch {
Write-Error "Elevation required to install to Program Files."
Exit 1
}
}
}
Ensure-Elevated
$destDir = Join-Path ${env:ProgramFiles} $AppName
if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir | Out-Null }
$resolvedExe = Resolve-Path -Path $ExePath -ErrorAction SilentlyContinue
if (-not $resolvedExe) {
Write-Error "Application executable not found at $ExePath"
Exit 1
}
Copy-Item -Path $resolvedExe -Destination (Join-Path $destDir (Split-Path $resolvedExe -Leaf)) -Force
# Create Start Menu shortcut
$programs = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
$appFolder = Join-Path $programs $AppName
if (-not (Test-Path $appFolder)) { New-Item -ItemType Directory -Path $appFolder | Out-Null }
$shortcutPath = Join-Path $appFolder "$AppName.lnk"
$wsh = New-Object -ComObject WScript.Shell
$sc = $wsh.CreateShortcut($shortcutPath)
$sc.TargetPath = (Join-Path $destDir (Split-Path $resolvedExe -Leaf))
$sc.WorkingDirectory = $destDir
if (Test-Path $IconPath) { $sc.IconLocation = Resolve-Path $IconPath }
$sc.Save()
Write-Output "Installed $AppName to $destDir and created Start Menu shortcut."
Exit 0