Skip to content

Commit 4f82472

Browse files
NewFutureCopilot
andauthored
优化docker镜像 (NewFuture#459)
* 删除重复库, 减少docker体积 * 首次失败自动停止 * python3 优先 * 怎加python2检查 * Nuitka/Nuitka-Action Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 3621736 commit 4f82472

14 files changed

Lines changed: 185 additions & 77 deletions

File tree

.build/Dockerfile

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
1-
FROM alpine:latest
1+
FROM alpine
22
WORKDIR /build
33
COPY . .
4+
RUN find /lib /usr/lib -name '*.so*' | sed 's|.*/||' | awk '{print "--noinclude-dlls="$0}' > nuitka_exclude_so.txt
45
RUN apk add py3-pip python3-dev patchelf build-base libffi-dev
56
RUN pip3 install -U nuitka --break-system-packages
6-
RUN python3 -m nuitka run.py \
7+
RUN python3 .build/remove_python2.py
8+
RUN python3 -O -m nuitka run.py \
79
--mode=onefile\
810
--output-dir=./dist\
911
--no-deployment-flag=self-execution\
1012
--output-filename=ddns\
1113
--remove-output\
1214
--include-module=dns.dnspod --include-module=dns.alidns --include-module=dns.dnspod_com --include-module=dns.dnscom --include-module=dns.cloudflare --include-module=dns.he --include-module=dns.huaweidns --include-module=dns.callback\
1315
--product-name=DDNS\
16+
--lto=yes \
17+
--onefile-tempdir-spec="{TEMP}/{PRODUCT}_{VERSION}" \
1418
--python-flag=no_site,no_asserts,no_docstrings,isolated,static_hashes\
15-
--nofollow-import-to=unittest,pydoc\
16-
--onefile-tempdir-spec="{CACHE_DIR}/{PRODUCT}/{VERSION}"
17-
RUN mkdir bin
18-
RUN cp dist/ddns bin/
19-
RUN cp .build/entrypoint.sh bin/
19+
--nofollow-import-to=tkinter,unittest,pydoc,doctest,distutils,setuptools,lib2to3,test,idlelib,lzma \
20+
--noinclude-dlls=liblzma.so.* \
21+
$(cat nuitka_exclude_so.txt)
22+
RUN mkdir docker-bin && cp dist/ddns docker-bin/ && cp .build/entrypoint.sh docker-bin/
2023

21-
FROM alpine:latest
24+
FROM alpine
2225
LABEL maintainer="NN708, newfuture"
2326
WORKDIR /ddns
24-
COPY --from=0 /build/bin/* /bin/
27+
COPY --from=0 /build/docker-bin/* /bin/
2528
ENTRYPOINT [ "/bin/entrypoint.sh" ]

.build/entrypoint.sh

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
if [ $# -eq 0 ]; then
44
printenv > /etc/environment
55
echo "*/5 * * * * cd /ddns && /bin/ddns" > /etc/crontabs/root
6-
/bin/ddns
7-
echo "Cron daemon will run every 5 minutes..."
8-
exec crond -f
6+
/bin/ddns && echo "Cron daemon will run every 5 minutes..." && exec crond -f
97
else
108
first=`echo $1 | cut -c1`
119
if [ "$first" = "-" ]; then

.build/remove_python2.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
"""
3+
自动将所有 try-except python2/3 兼容导入替换为 python3 only 导入,并显示处理日志
4+
"""
5+
import os
6+
import re
7+
8+
ROOT = '.'
9+
# 匹配 try-except 块,去除导入前缩进,保证import顶格,删除的行用空行代替
10+
PATTERN = re.compile(
11+
r'^[ \t]*try:[^\n]*python 3[^\n]*\n' # try: # python 3
12+
r'((?:[ \t]+[^\n]*\n)+?)' # python3 导入内容
13+
r'^[ \t]*except ImportError:[^\n]*\n' # except ImportError: # python 2
14+
r'((?:[ \t]+from[^\n]*\n|[ \t]+import[^\n]*\n)*)', # except块内容
15+
re.MULTILINE
16+
)
17+
18+
19+
def dedent_imports_with_blank(import_block, try_block, except_block):
20+
"""
21+
保留python3导入并去除缩进,try/except及except内容用空行代替
22+
"""
23+
try_lines = try_block.count('\n')
24+
except_lines = except_block.count('\n')
25+
imports = ''.join(line.lstrip()
26+
for line in import_block.splitlines(keepends=True))
27+
return ('\n' * try_lines) + imports + ('\n' * except_lines)
28+
29+
30+
def main():
31+
"""
32+
遍历所有py文件并替换兼容导入
33+
"""
34+
changed_files = 0
35+
for dirpath, _, filenames in os.walk(ROOT):
36+
for fname in filenames:
37+
if fname.endswith('.py'):
38+
fpath = os.path.join(dirpath, fname)
39+
with open(fpath, 'r', encoding='utf-8') as f:
40+
content = f.read()
41+
42+
def repl(match):
43+
try_block = re.match(
44+
r'^[ \t]*try:[^\n]*python 3[^\n]*\n', match.group(0)
45+
).group(0)
46+
except_block = re.search(
47+
r'^[ \t]*except ImportError:[^\n]*\n((?:[ \t]+from[^\n]*\n|[ \t]+import[^\n]*\n)*)',
48+
match.group(0), re.MULTILINE
49+
)
50+
except_block = except_block.group(
51+
0) if except_block else ''
52+
return dedent_imports_with_blank(match.group(1), try_block, except_block)
53+
54+
new_content, n = PATTERN.subn(repl, content)
55+
if n > 0:
56+
with open(fpath, 'w', encoding='utf-8') as f:
57+
f.write(new_content)
58+
print(f'change: {fpath}')
59+
changed_files += 1
60+
print('done')
61+
print(f'Total changed files: {changed_files}')
62+
63+
64+
if __name__ == '__main__':
65+
main()

.github/workflows/build.yml

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ jobs:
3333
- name: check complexity and length # the GitHub editor is 127 chars wide
3434
run: flake8 . --count --max-complexity=12 --max-line-length=127 --statistics
3535

36+
python:
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
python-version: [ "2.7","3" ]
41+
runs-on: ubuntu-22.04
42+
timeout-minutes: 5
43+
steps:
44+
- uses: actions/checkout@v4
45+
- run: sudo apt-get update && sudo apt-get install -y python${{ matrix.python-version }}
46+
- name: test help command
47+
run: python${{ matrix.python-version }} run.py -h
48+
- name: test config generation
49+
run: python${{ matrix.python-version }} run.py || test -e config.json
50+
- name: test version
51+
run: python${{ matrix.python-version }} run.py --version
52+
3653
pypi:
3754
runs-on: ubuntu-latest
3855
timeout-minutes: 5
@@ -55,6 +72,7 @@ jobs:
5572
path: dist/
5673
retention-days: 5
5774

75+
5876
pyinstaller:
5977
strategy:
6078
# fail-fast: false
@@ -95,6 +113,7 @@ jobs:
95113
retention-days: 3
96114

97115
nuitka:
116+
needs: [ python ]
98117
strategy:
99118
fail-fast: false
100119
matrix:
@@ -122,8 +141,9 @@ jobs:
122141
with:
123142
python-version: 3.x
124143
architecture: ${{ matrix.arch }}
125-
- name: Install dependencies
126-
run: python3 -m pip install -U nuitka
144+
145+
- name: remove python2 code
146+
run: python3 .build/remove_python2.py
127147

128148
# Prepare build version and cert
129149
- name: Replace build version
@@ -133,34 +153,67 @@ jobs:
133153
- name: Set up on Linux
134154
if: runner.os == 'Linux'
135155
run: |
136-
sudo apt-get update
137-
sudo apt-get install -y patchelf
138-
echo " --static-libpython=yes --linux-icon=.build/icon.png" >> .build/nuitka.cmd
156+
sudo apt-get update && sudo apt-get install -y patchelf
139157
cp /etc/ssl/certs/ca-certificates.crt cert.pem && export SSL_CERT_FILE=${PWD}/cert.pem
140158
141159
- name: Set up on macOS
142160
if: runner.os == 'macOS'
143-
run: |
144-
python3 -m pip install imageio
145-
echo " --macos-app-name=DDNS --macos-app-icon=.build/icon.png" >> .build/nuitka.cmd
146-
161+
run: python3 -m pip install imageio
162+
147163
- run: python3 ./run.py -h
148164

149-
- name: Package binary
150-
run: ./.build/nuitka.cmd
165+
- name: Build Executable
166+
uses: Nuitka/Nuitka-Action@main
167+
with:
168+
nuitka-version: main
169+
script-name: run.py
170+
mode: onefile
171+
output-dir: dist
172+
output-file: ddns
173+
no-deployment-flag: self-execution
174+
include-module: |
175+
dns.dnspod
176+
dns.alidns
177+
dns.dnspod_com
178+
dns.dnscom
179+
dns.cloudflare
180+
dns.he
181+
dns.huaweidns
182+
dns.callback
183+
file-description: "DDNS Client 更新域名解析本机IP"
184+
product-name: DDNS
185+
company-name: "New Future"
186+
copyright: "https://ddns.newfuture.cc"
187+
assume-yes-for-downloads: true
188+
lto: auto
189+
python-flag: no_site,no_asserts,no_docstrings,isolated,static_hashes
190+
nofollow-import-to: tkinter,unittest,pydoc,doctest,distutils,setuptools,lib2to3,test,idlelib,lzma
191+
onefile-tempdir-spec: "{CACHE_DIR}/{PRODUCT}_{VERSION}"
192+
windows-icon-from-ico: ${{ runner.os == 'Windows' && 'favicon.ico' || '' }}
193+
linux-icon: ${{ runner.os == 'Linux' && '.build/icon.png' || '' }}
194+
static-libpython: ${{ runner.os == 'yes' || 'auto' }}
195+
macos-app-name: ${{ runner.os == 'macOS' && 'DDNS' || '' }}
196+
macos-app-icon: ${{ runner.os == 'macOS' && '.build/icon.png' || '' }}
197+
151198

152199
- run: ./dist/ddns || test -e config.json
153200
- run: ./dist/ddns -h
154201

155202
# Upload build result
156-
- uses: actions/upload-artifact@v4
203+
- name: Upload Artifacts
204+
uses: actions/upload-artifact@v4
157205
with:
158206
name: ddns-${{ runner.os }}-${{ matrix.arch }}
159-
path: dist/
160-
retention-days: 7
207+
if-no-files-found: error
208+
path: |
209+
dist/*.exe
210+
dist/*.bin
211+
dist/*.app
212+
dist/ddns
161213
162214
docker:
163215
if: github.event_name == 'pull_request'
216+
needs: [ python ]
164217
strategy:
165218
matrix:
166219
platforms: [ linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/riscv64,linux/s390x ]
@@ -189,7 +242,7 @@ jobs:
189242
preview-pypi:
190243
runs-on: ubuntu-latest
191244
if: github.event_name == 'push'
192-
needs: [lint, pypi]
245+
needs: [lint, pypi, python]
193246
timeout-minutes: 3
194247
environment:
195248
name: preview
@@ -207,8 +260,9 @@ jobs:
207260
print-hash: true
208261

209262
preview-docker:
210-
runs-on: ubuntu-latest
211263
if: github.event_name == 'push'
264+
needs: [lint, python]
265+
runs-on: ubuntu-latest
212266
timeout-minutes: 120
213267
environment:
214268
name: preview

.github/workflows/publish.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ jobs:
9999
run: sed -i.tmp -e "s#\${BUILD_VERSION}#${{ github.ref_name }}#" -e "s/\${BUILD_DATE}/$(date --iso-8601=seconds)/" run.py && rm run.py.tmp
100100
shell: bash
101101

102+
- name: remove python2 code
103+
run: python3 .build/remove_python2.py
104+
102105
- name: setup on Linux
103106
if: runner.os == 'Linux'
104107
run: |

dns/alidns.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
from logging import debug, info, warning
1515
from datetime import datetime
1616

17-
try:
18-
# python 2
19-
from httplib import HTTPSConnection
20-
from urllib import urlencode, quote_plus, quote
21-
except ImportError:
22-
# python 3
17+
try: # python 3
2318
from http.client import HTTPSConnection
2419
from urllib.parse import urlencode, quote_plus, quote
20+
except ImportError: # python 2
21+
from httplib import HTTPSConnection
22+
from urllib import urlencode, quote_plus, quote
2523

2624
__author__ = 'New Future'
2725
# __all__ = ["request", "ID", "TOKEN", "PROXY"]

dns/callback.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010
from logging import debug, info, warning
1111
from time import time
1212

13-
try:
14-
# python 2
13+
try: # python 3
14+
from http.client import HTTPSConnection, HTTPConnection
15+
from urllib.parse import urlencode, urlparse, parse_qsl
16+
except ImportError: # python 2
1517
from httplib import HTTPSConnection, HTTPConnection
1618
from urlparse import urlparse, parse_qsl
1719
from urllib import urlencode
18-
except ImportError:
19-
# python 3
20-
from http.client import HTTPSConnection, HTTPConnection
21-
from urllib.parse import urlencode, urlparse, parse_qsl
2220

2321
__author__ = '老周部落'
2422

dns/cloudflare.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
from json import loads as jsondecode, dumps as jsonencode
1010
from logging import debug, info, warning
1111

12-
try:
13-
# python 2
14-
from httplib import HTTPSConnection
15-
from urllib import urlencode
16-
except ImportError:
17-
# python 3
12+
try: # python 3
1813
from http.client import HTTPSConnection
1914
from urllib.parse import urlencode
15+
except ImportError: # python 2
16+
from httplib import HTTPSConnection
17+
from urllib import urlencode
2018

2119
__author__ = 'TongYifan'
2220

dns/dnscom.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,12 @@
1313
from time import mktime
1414
from datetime import datetime
1515

16-
try:
17-
# python 2
18-
from httplib import HTTPSConnection
19-
from urllib import urlencode
20-
except ImportError:
21-
# python 3
16+
try: # python 3
2217
from http.client import HTTPSConnection
2318
from urllib.parse import urlencode
24-
19+
except ImportError: # python 2
20+
from httplib import HTTPSConnection
21+
from urllib import urlencode
2522

2623
__author__ = 'Bigjin'
2724
# __all__ = ["request", "ID", "TOKEN", "PROXY"]

dns/dnspod.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
from json import loads as jsondecode
1010
from logging import debug, info, warning
1111
from os import environ
12-
try:
13-
# python 2
14-
from httplib import HTTPSConnection
15-
from urllib import urlencode
16-
except ImportError:
17-
# python 3
12+
13+
try: # python 3
1814
from http.client import HTTPSConnection
1915
from urllib.parse import urlencode
16+
except ImportError: # python 2
17+
from httplib import HTTPSConnection
18+
from urllib import urlencode
2019

2120
__author__ = 'New Future'
2221

0 commit comments

Comments
 (0)