forked from JackMcKew/pyinstaller-action-windows
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathentrypoint.sh
More file actions
278 lines (238 loc) · 10.1 KB
/
entrypoint.sh
File metadata and controls
278 lines (238 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/bin/bash
# Fail on errors.
set -e
# Make sure .bashrc is sourced when the base image provides one.
if [ -f /root/.bashrc ]; then
. /root/.bashrc
fi
# Set default values from action.yaml
WORKDIR=${1:-/src} # Default to "src" if no argument is provided
PYPI_URL=${2:-"https://pypi.python.org/"} # Default PyPI URL
PYPI_INDEX_URL=${3:-"https://pypi.python.org/simple"} # Default PyPI Index URL
SPEC_FILE=${4:-*.spec} # Default to an empty string for .spec file path
REQUIREMENTS=${5:-"requirements.txt"} # Default requirements file
CYTHON_OUT=$6 # Default prec folder
ZIP_NAME=${7:-}
ZIP_PATHS=${8:-}
ZIP_METHOD=${9:-deflate}
ZIP_LEVEL=${10:-9}
SEVENZIP_NAME=${11:-}
WINDOWS_PYTHON='C:\python\python.exe'
archive_stem() {
local archive_name="$1"
archive_name="${archive_name##*/}"
printf '%s\n' "${archive_name%.*}"
}
find_upwards() {
local name="$1"
local dir="$PWD"
while true; do
if [ -f "$dir/$name" ]; then
printf '%s\n' "$dir"
return 0
fi
if [ "$dir" = "/" ]; then
return 1
fi
dir="$(dirname "$dir")"
done
}
read_first_line() {
local file="$1"
sed -n '1{s/[[:space:]]*$//;p;q;}' "$file"
}
read_requires_python() {
local pyproject="$1"
sed -n 's/^[[:space:]]*requires-python[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$pyproject" | head -n 1
}
write_pyproject_requirements() {
local pyproject="$1"
local requirements_out="$2"
python - "$pyproject" "$requirements_out" <<'PY'
import pathlib
import sys
import tomllib
pyproject = pathlib.Path(sys.argv[1])
requirements_out = pathlib.Path(sys.argv[2])
with pyproject.open("rb") as handle:
data = tomllib.load(handle)
dependencies = data.get("project", {}).get("dependencies", [])
if not isinstance(dependencies, list):
raise SystemExit(f"{pyproject}: [project].dependencies must be a list")
with requirements_out.open("w", encoding="utf-8", newline="\n") as handle:
for dependency in dependencies:
if not isinstance(dependency, str):
raise SystemExit(f"{pyproject}: dependency entries must be strings")
handle.write(dependency)
handle.write("\n")
PY
}
python_request_from_requires() {
local requirement="$1"
case "$requirement" in
==[0-9].[0-9].\*)
printf '%s\n' "${requirement#==}" | sed 's/[.][*]$//'
;;
==[0-9].[0-9][0-9].\*)
printf '%s\n' "${requirement#==}" | sed 's/[.][*]$//'
;;
==[0-9].[0-9].[0-9]*)
printf '%s\n' "${requirement#==}"
;;
==[0-9].[0-9][0-9].[0-9]*)
printf '%s\n' "${requirement#==}"
;;
esac
}
normalize_python_request() {
local request="$1"
printf '%s\n' "$request" | sed 's/^==//; s/[.][*]$//'
}
container_python_version() {
python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"
}
python_request_matches_container() {
local request="$1"
local actual="$2"
[[ "$actual" == "$request" || "$actual" == "$request".* ]]
}
# In case the user specified a custom URL for PYPI, then use
# that one, instead of the default one.
UV_INDEX_ARGS=()
REPO_ROOT="$(pwd -P)"
if [[ "$PYPI_URL" != "https://pypi.python.org/" ]] || \
[[ "$PYPI_INDEX_URL" != "https://pypi.python.org/simple" ]]; then
# the funky looking regexp just extracts the hostname, excluding port
# to be used as a trusted-host.
mkdir -p /wine/drive_c/users/root/pip
echo "[global]" > /wine/drive_c/users/root/pip/pip.ini
echo "index = $PYPI_URL" >> /wine/drive_c/users/root/pip/pip.ini
echo "index-url = $PYPI_INDEX_URL" >> /wine/drive_c/users/root/pip/pip.ini
echo "trusted-host = $(echo $PYPI_URL | perl -pe 's|^.*?://(.*?)(:.*?)?/.*$|$1|')" >> /wine/drive_c/users/root/pip/pip.ini
echo "Using custom pip.ini: "
cat /wine/drive_c/users/root/pip/pip.ini
UV_INDEX_ARGS=(--index-url "$PYPI_INDEX_URL")
fi
cd "$WORKDIR"
WORKDIR="$(pwd -P)"
echo "Working directory: $WORKDIR"
UV_PROJECT_ROOT=""
PYTHON_VERSION_ROOT=""
CONTAINER_PYTHON_VERSION="$(container_python_version)"
if UV_PROJECT_ROOT="$(find_upwards pyproject.toml)"; then
echo "Detected pyproject.toml at: $UV_PROJECT_ROOT"
if PYTHON_VERSION_ROOT="$(find_upwards .python-version)"; then
PROJECT_PYTHON_REQUEST="$(read_first_line "$PYTHON_VERSION_ROOT/.python-version")"
PROJECT_PYTHON_REQUEST="$(normalize_python_request "$PROJECT_PYTHON_REQUEST")"
if [ -n "$PROJECT_PYTHON_REQUEST" ]; then
if ! python_request_matches_container "$PROJECT_PYTHON_REQUEST" "$CONTAINER_PYTHON_VERSION"; then
echo "Error: project .python-version requests Python $PROJECT_PYTHON_REQUEST, but this image provides Windows Python $CONTAINER_PYTHON_VERSION."
echo "Use a matching zamkorus/cythinst64 builder tag, or update the project Python pin."
exit 2
fi
echo "Project .python-version matches image Python: $PROJECT_PYTHON_REQUEST"
fi
else
PROJECT_REQUIRES_PYTHON="$(read_requires_python "$UV_PROJECT_ROOT/pyproject.toml")"
PROJECT_PYTHON_REQUEST="$(python_request_from_requires "$PROJECT_REQUIRES_PYTHON")"
if [ -n "$PROJECT_PYTHON_REQUEST" ]; then
if ! python_request_matches_container "$PROJECT_PYTHON_REQUEST" "$CONTAINER_PYTHON_VERSION"; then
echo "Error: pyproject.toml requires Python $PROJECT_REQUIRES_PYTHON, but this image provides Windows Python $CONTAINER_PYTHON_VERSION."
echo "Use a matching zamkorus/cythinst64 builder tag, or update the project Python requirement."
exit 2
fi
echo "Project Python requirement matches image Python: $PROJECT_REQUIRES_PYTHON"
elif [ -n "$PROJECT_REQUIRES_PYTHON" ]; then
echo "Project requires Python: $PROJECT_REQUIRES_PYTHON; using image Windows Python $CONTAINER_PYTHON_VERSION"
else
echo "No project Python pin found; using image Windows Python $CONTAINER_PYTHON_VERSION"
fi
fi
elif [ -f "$REQUIREMENTS" ]; then
if PYTHON_VERSION_ROOT="$(find_upwards .python-version)"; then
REQUIREMENTS_PYTHON_REQUEST="$(read_first_line "$PYTHON_VERSION_ROOT/.python-version")"
REQUIREMENTS_PYTHON_REQUEST="$(normalize_python_request "$REQUIREMENTS_PYTHON_REQUEST")"
if [ -n "$REQUIREMENTS_PYTHON_REQUEST" ] && ! python_request_matches_container "$REQUIREMENTS_PYTHON_REQUEST" "$CONTAINER_PYTHON_VERSION"; then
echo "Error: requirements.txt mode installs into the image Windows Python ($CONTAINER_PYTHON_VERSION), but .python-version requests $REQUIREMENTS_PYTHON_REQUEST."
echo "Use a matching zamkorus/cythinst64 builder tag, or update the project Python pin."
exit 2
fi
fi
fi
if [ -f "$REQUIREMENTS" ]; then
echo "Installing requirements into image Windows Python with uv pip: $REQUIREMENTS"
uv pip install --system --python "$WINDOWS_PYTHON" "${UV_INDEX_ARGS[@]}" -r "$REQUIREMENTS"
elif [ -n "$UV_PROJECT_ROOT" ]; then
PYPROJECT_REQUIREMENTS=/tmp/cythinst-pyproject-requirements.txt
write_pyproject_requirements "$UV_PROJECT_ROOT/pyproject.toml" "$PYPROJECT_REQUIREMENTS"
if [ -s "$PYPROJECT_REQUIREMENTS" ]; then
if [ -f "$UV_PROJECT_ROOT/uv.lock" ]; then
echo "uv.lock found, but this action installs into the image Windows Python instead of creating a uv venv; using pyproject dependencies directly."
fi
echo "Installing pyproject dependencies into image Windows Python with uv pip"
uv pip install --system --python "$WINDOWS_PYTHON" "${UV_INDEX_ARGS[@]}" -r "$PYPROJECT_REQUIREMENTS"
else
echo "pyproject.toml has no [project].dependencies; continuing with image defaults"
fi
else
echo "No pyproject.toml or requirements file found; continuing with image defaults"
fi
if [ -n "$CYTHON_OUT" ]; then
cd "$WORKDIR/.."
mkdir -p ./build
wine reg add "HKEY_CURRENT_USER\Environment" /v PATH /t REG_SZ /d "C:\\mingw64\bin;%PATH%" /f
echo "gcc --version"| wine cmd
python /cython_build.py
cd "$WORKDIR"
mkdir -p "$CYTHON_OUT"
mapfile -t CYTHON_ARTIFACTS < <(find "$WORKDIR" "$WORKDIR/.." -maxdepth 1 -type f -name '*.pyd' -print)
if [ "${#CYTHON_ARTIFACTS[@]}" -eq 0 ]; then
echo "Error: Cython build completed, but no .pyd files were found in $WORKDIR or $WORKDIR/.."
exit 1
fi
for artifact in "${CYTHON_ARTIFACTS[@]}"; do
mv "$artifact" "$CYTHON_OUT/"
done
fi
pyinstaller --clean -y --dist ./dist/windows --workpath /tmp $SPEC_FILE
chown -R --reference=. ./dist/windows
ARCHIVE_EXCLUDES=""
if [ -n "$ZIP_NAME" ]; then
ARCHIVE_EXCLUDES="${ARCHIVE_EXCLUDES}${ZIP_NAME}"$'\n'
fi
if [ -n "$SEVENZIP_NAME" ]; then
ARCHIVE_EXCLUDES="${ARCHIVE_EXCLUDES}${SEVENZIP_NAME}"$'\n'
fi
if [ -n "$ZIP_NAME" ] && [ -n "$SEVENZIP_NAME" ]; then
if [ "$(archive_stem "$ZIP_NAME")" != "$(archive_stem "$SEVENZIP_NAME")" ]; then
echo "Error: zip_name and sevenzip_name must use the same base name, for example app.zip and app.7z."
exit 2
fi
fi
if [ -n "$ZIP_NAME" ]; then
cd "$REPO_ROOT"
echo "Creating zip package: $ZIP_NAME"
/archive.sh zip "$ZIP_NAME" "$ZIP_PATHS" "$ZIP_METHOD" "$ZIP_LEVEL" "$ARCHIVE_EXCLUDES"
chown --reference=. "$ZIP_NAME"
fi
if [ -n "$SEVENZIP_NAME" ]; then
cd "$REPO_ROOT"
echo "Creating 7z package: $SEVENZIP_NAME"
/archive.sh 7z "$SEVENZIP_NAME" "$ZIP_PATHS" "$ARCHIVE_EXCLUDES"
chown --reference=. "$SEVENZIP_NAME"
fi
if [ -n "${GITHUB_OUTPUT:-}" ]; then
echo "dist=$WORKDIR/dist/windows" >> "$GITHUB_OUTPUT"
if [ -n "$ZIP_NAME" ]; then
echo "zip_output=$ZIP_NAME" >> "$GITHUB_OUTPUT"
echo "output=$ZIP_NAME" >> "$GITHUB_OUTPUT"
elif [ -n "$SEVENZIP_NAME" ]; then
echo "sevenzip_output=$SEVENZIP_NAME" >> "$GITHUB_OUTPUT"
echo "output=$SEVENZIP_NAME" >> "$GITHUB_OUTPUT"
else
echo "output=$WORKDIR/dist/windows" >> "$GITHUB_OUTPUT"
fi
if [ -n "$SEVENZIP_NAME" ] && [ -n "$ZIP_NAME" ]; then
echo "sevenzip_output=$SEVENZIP_NAME" >> "$GITHUB_OUTPUT"
fi
fi