Skip to content

Commit 08c391f

Browse files
committed
Create installer packages
1 parent ce6eff2 commit 08c391f

4 files changed

Lines changed: 175 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: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ 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+
gem install --no-document fpm
45+
fpm --version
46+
3747
- name: ccache
3848
uses: hendrikmuhs/ccache-action@5ebbd400eff9e74630f759d94ddd7b6c26299639 # v1.2
3949
if: ${{ matrix.platform != 'windows-latest' }}
@@ -59,6 +69,7 @@ jobs:
5969
run: |
6070
pip install .[build]
6171
python script/build.py
72+
python script/package.py
6273
6374
- name: Store the distribution packages
6475
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
@@ -85,27 +96,27 @@ jobs:
8596
- name: Prepare binary
8697
if: matrix.platform == 'ubuntu-latest'
8798
run: |
99+
ls -la .
88100
binary=$(ls dfetch-*-x86_64)
89101
ln -sf "$binary" dfetch
90102
chmod +x dfetch
91-
ls -la .
92103
shell: bash
93104

94105
- name: Prepare binary
95106
if: matrix.platform == 'macos-latest'
96107
run: |
108+
ls -la .
97109
binary=$(ls dfetch-*-osx)
98110
ln -sf "$binary" dfetch
99111
chmod +x dfetch
100-
ls -la .
101112
shell: bash
102113

103114
- name: Prepare binary on Windows
104115
if: matrix.platform == 'windows-latest'
105116
run: |
117+
Get-ChildItem
106118
$binary = Get-ChildItem dfetch-*.exe | Select-Object -First 1
107119
Copy-Item $binary -Destination dfetch.exe -Force
108-
Get-ChildItem
109120
shell: pwsh
110121

111122
- 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/package.py

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

0 commit comments

Comments
 (0)