Skip to content

Commit 4253f14

Browse files
committed
Create installer packages
1 parent ce6eff2 commit 4253f14

5 files changed

Lines changed: 189 additions & 5 deletions

File tree

.devcontainer/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
99
subversion=1.14.1-3+deb11u2 && \
1010
rm -rf /var/lib/apt/lists/*
1111

12+
# Install ruby gem FPM for packaging
13+
RUN apt-get update && \
14+
apt-get install --no-install-recommends -y \
15+
ruby=1:2.7+2 \
16+
ruby-dev=1:2.7+2 \
17+
build-essential \
18+
rpm=4.16.1.2+dfsg1-3 \
19+
git \
20+
curl \
21+
ca-certificates && \
22+
gem install --no-document fpm && \
23+
rm -rf /var/lib/apt/lists/*
24+
1225
WORKDIR /workspaces/dfetch
1326

1427
# Add a non-root user (dev)

.github/workflows/build.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ jobs:
3434
with:
3535
python-version: '3.13'
3636

37+
- name: Set up Ruby
38+
uses: ruby/setup-ruby@v1
39+
with:
40+
ruby-version: 3.2 # or any version you need
41+
42+
- name: Install fpm
43+
run: |
44+
echo "$(ruby -e 'puts File.join(Gem.user_dir, "bin")')" >> $GITHUB_PATH
45+
gem install --no-document fpm
46+
fpm --version
47+
3748
- name: ccache
3849
uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2
3950
if: ${{ matrix.platform != 'windows-latest' }}
@@ -57,8 +68,10 @@ jobs:
5768
NUITKA_CACHE_DIR_CLCACHE: ${{ github.workspace }}\.clcache
5869
NUITKA_CCACHE_BINARY: /usr/bin/ccache
5970
run: |
71+
fpm --version
6072
pip install .[build]
6173
python script/build.py
74+
python script/package.py
6275
6376
- name: Store the distribution packages
6477
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
@@ -85,27 +98,27 @@ jobs:
8598
- name: Prepare binary
8699
if: matrix.platform == 'ubuntu-latest'
87100
run: |
101+
ls -la .
88102
binary=$(ls dfetch-*-x86_64)
89103
ln -sf "$binary" dfetch
90104
chmod +x dfetch
91-
ls -la .
92105
shell: bash
93106

94107
- name: Prepare binary
95108
if: matrix.platform == 'macos-latest'
96109
run: |
110+
ls -la .
97111
binary=$(ls dfetch-*-osx)
98112
ln -sf "$binary" dfetch
99113
chmod +x dfetch
100-
ls -la .
101114
shell: bash
102115

103116
- name: Prepare binary on Windows
104117
if: matrix.platform == 'windows-latest'
105118
run: |
119+
Get-ChildItem
106120
$binary = Get-ChildItem dfetch-*.exe | Select-Object -First 1
107121
Copy-Item $binary -Destination dfetch.exe -Force
108-
Get-ChildItem
109122
shell: pwsh
110123

111124
- run: ./dfetch init

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ reportMissingModuleSource = false
190190
pythonVersion = "3.9"
191191

192192
[tool.nuitka]
193-
mode = "onefile" # Switch this between standalone and onefile as needed
194-
# jobs = "4" # Can be used to reduce memory usage, in case of compilation issues
193+
mode = "standalone" # Switch this between standalone and onefile as needed
194+
jobs = "2" # Can be used to reduce memory usage, in case of compilation issues
195195
# Enable below for debugging
196196
# show-progress = true
197197
assume-yes-for-downloads = true

script/build.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env python3
22
"""This script builds the dfetch executable using Nuitka."""
3+
import os
4+
import shutil
35
import subprocess # nosec
46
import sys
57
import tomllib as toml
@@ -68,5 +70,18 @@ def parse_option(
6870

6971
command.append("dfetch")
7072

73+
print("Python PATH:", os.environ.get("PATH"))
74+
print("Python GITHUB PATH:", os.environ.get("GITHUB_PATH"))
75+
print("Python GITHUB ENV:", os.environ.get("GITHUB_ENV"))
76+
subprocess.check_call(["where" if os.name == "nt" else "which", "fpm"])
77+
78+
fpm_path = shutil.which("fpm")
79+
80+
if not fpm_path:
81+
print("Error: fpm not found in PATH.")
82+
sys.exit(1)
83+
84+
subprocess.check_call([fpm_path, "--version"]) # nosec
85+
7186
print(command)
7287
subprocess.check_call(command) # nosec

script/package.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#!/usr/bin/env python3
2+
"""This script packages the dfetch build directory into OS-specific installers using fpm."""
3+
import subprocess
4+
import sys
5+
from pathlib import Path
6+
7+
from dfetch import __version__
8+
9+
# Configuration
10+
BUILD_DIR = Path("build", "dfetch.dist")
11+
OUTPUT_DIR = Path("build", "dfetch-package")
12+
PACKAGE_NAME = "dfetch"
13+
INSTALL_PREFIX = "/opt/dfetch" # Where the files will be installed on Linux/macOS
14+
MAINTAINER = "DFetch Team <dfetch@spoor.cc>"
15+
DESCRIPTION = (
16+
"DFetch: A vendoring tool for fetching and managing external dependencies."
17+
)
18+
URL = "https://github.com/dfetch-org/dfetch"
19+
LICENSE = "MIT"
20+
21+
22+
def run_command(command):
23+
"""Run a system command and handle errors."""
24+
print("Running:", " ".join(command))
25+
subprocess.check_call(command)
26+
27+
28+
def package_linux() -> None:
29+
"""Package the build directory into .deb and .rpm installers."""
30+
for target in ("deb", "rpm"):
31+
output = f"{OUTPUT_DIR}/{PACKAGE_NAME}_{__version__}.{target}"
32+
cmd = [
33+
"fpm",
34+
"-s",
35+
"dir",
36+
"-t",
37+
target,
38+
"-n",
39+
PACKAGE_NAME,
40+
"-v",
41+
__version__,
42+
"-C",
43+
str(BUILD_DIR),
44+
"--prefix",
45+
INSTALL_PREFIX,
46+
"--description",
47+
DESCRIPTION,
48+
"--maintainer",
49+
MAINTAINER,
50+
"--url",
51+
URL,
52+
"--license",
53+
LICENSE,
54+
"-p",
55+
output,
56+
".",
57+
]
58+
run_command(cmd)
59+
60+
61+
def package_macos() -> None:
62+
"""Package the build directory into a .pkg installer for macOS."""
63+
cmd = [
64+
"fpm",
65+
"-s",
66+
"dir",
67+
"-t",
68+
"osxpkg",
69+
"-n",
70+
PACKAGE_NAME,
71+
"-v",
72+
__version__,
73+
"-C",
74+
str(BUILD_DIR),
75+
"--prefix",
76+
INSTALL_PREFIX,
77+
"--description",
78+
DESCRIPTION,
79+
"--maintainer",
80+
MAINTAINER,
81+
"--url",
82+
URL,
83+
"--license",
84+
LICENSE,
85+
"-p",
86+
f"{OUTPUT_DIR}/{PACKAGE_NAME}_{__version__}.pkg",
87+
".",
88+
]
89+
run_command(cmd)
90+
91+
92+
def package_windows() -> None:
93+
"""Package the build directory into a .msi installer for Windows."""
94+
run_command(["cd"])
95+
run_command(["fpm", "--version"])
96+
cmd = [
97+
"fpm",
98+
"-s",
99+
"dir",
100+
"-t",
101+
"msi",
102+
"--verbose",
103+
"-n",
104+
PACKAGE_NAME,
105+
"-v",
106+
__version__,
107+
"-C",
108+
str(BUILD_DIR),
109+
"--description",
110+
DESCRIPTION,
111+
"--manufacturer",
112+
MAINTAINER,
113+
"--url",
114+
URL,
115+
"--license",
116+
LICENSE,
117+
"-p",
118+
f"{OUTPUT_DIR}/{PACKAGE_NAME}_{__version__}.msi",
119+
".",
120+
]
121+
run_command(cmd)
122+
123+
124+
def main() -> None:
125+
"""Main packaging function."""
126+
if not BUILD_DIR.exists():
127+
print(f"Error: Build directory {BUILD_DIR} does not exist. Run build.py first.")
128+
sys.exit(1)
129+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
130+
131+
if sys.platform.startswith("linux"):
132+
package_linux()
133+
elif sys.platform.startswith("darwin"):
134+
package_macos()
135+
elif sys.platform.startswith("win"):
136+
package_windows()
137+
else:
138+
print(f"Unsupported platform: {sys.platform}")
139+
sys.exit(1)
140+
141+
142+
if __name__ == "__main__":
143+
main()

0 commit comments

Comments
 (0)