mirror of
https://github.com/NewFuture/DDNS.git
synced 2026-10-20 08:17:43 +00:00
Bumps [Nuitka/Nuitka-Action](https://github.com/nuitka/nuitka-action) from 1.4 to 1.5. - [Release notes](https://github.com/nuitka/nuitka-action/releases) - [Commits](https://github.com/nuitka/nuitka-action/compare/v1.4...v1.5) --- updated-dependencies: - dependency-name: Nuitka/Nuitka-Action dependency-version: '1.5' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
656 lines
25 KiB
YAML
656 lines
25 KiB
YAML
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
|
|
|
name: Build
|
|
|
|
on:
|
|
push:
|
|
branches: ["master", "main"]
|
|
pull_request:
|
|
branches: ["master", "main", "abc"]
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
|
|
# This allows a subsequently queued workflow run to interrupt previous runs
|
|
concurrency:
|
|
group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
|
cancel-in-progress: true
|
|
env:
|
|
DOCKER_IMG: "ghcr.io/newfuture/ddns"
|
|
jobs:
|
|
lint:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 3
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: astral-sh/ruff-action@v3
|
|
with:
|
|
src: "."
|
|
args: "check --output-format=github"
|
|
|
|
python:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
version: [ "2.7", "3", "3.8", "3.10", "3.12", "3.13", "3.14-dev"]
|
|
env:
|
|
PY: python${{ matrix.version == '3.14-dev' && '3.14' || matrix.version }}
|
|
runs-on: ubuntu-22.04
|
|
timeout-minutes: 5
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: sudo apt-get update && sudo apt-get install -y python${{ matrix.version }}
|
|
if: matrix.version == '2.7' || matrix.version == '3'
|
|
- uses: actions/setup-python@v7
|
|
if: matrix.version != '2.7' && matrix.version != '3'
|
|
with:
|
|
python-version: ${{ matrix.version }}
|
|
- name: test help command
|
|
run: ${{env.PY}} run.py -h
|
|
- name: test config generation
|
|
run: ${{env.PY}} run.py || test -f config.json
|
|
- name: test version
|
|
run: ${{env.PY}} run.py --version
|
|
- name: test run module
|
|
run: ${{env.PY}} -m "ddns" -h
|
|
|
|
- name: install mock for Python 2.7
|
|
if: ${{ matrix.version == '2.7' }}
|
|
run: |
|
|
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
|
|
sudo ${{env.PY}} get-pip.py
|
|
${{env.PY}} -m pip install mock==3.0.5
|
|
working-directory: /tmp
|
|
- run: rm config.json -f
|
|
- name: run unit tests
|
|
run: ${{env.PY}} -m unittest discover tests -v
|
|
env:
|
|
PYTHONIOENCODING: utf-8
|
|
|
|
- name: test patch
|
|
if: ${{ matrix.version != '2.7' }}
|
|
run: python3 .github/patch.py
|
|
- name: test help
|
|
if: ${{ matrix.version != '2.7' }}
|
|
run: python3 run.py -h
|
|
- name: test run
|
|
if: ${{ matrix.version != '2.7' }}
|
|
run: python3 run.py || test -f config.json
|
|
- name: test version
|
|
run: ${{env.PY}} run.py --version
|
|
- name: test run module
|
|
if: ${{ matrix.version != '2.7' }}
|
|
run: ${{env.PY}} -m "ddns" -h
|
|
|
|
e2e:
|
|
name: E2E (CLI, Web, systemd)
|
|
runs-on: ubuntu-22.04
|
|
timeout-minutes: 8
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-python@v7
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Run offline CLI and Web E2E tests
|
|
run: python -m unittest tests.e2e -v
|
|
env:
|
|
PYTHONIOENCODING: utf-8
|
|
- name: Run systemd task lifecycle
|
|
run: bash tests/scripts/test-task-systemd.sh "$(which python) -m ddns"
|
|
|
|
pypi:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- uses: actions/setup-python@v7
|
|
with:
|
|
python-version: "3.x"
|
|
- name: Install dependencies
|
|
run: pip install build
|
|
- run: python3 .github/patch.py version
|
|
- name: Replace url in Readme
|
|
run: sed -i'' -E 's#([("'\''`])(/docs/[^)"'\''`]+)\.md([)"'\''`])#\1https://ddns.newfuture.cc\2.html\3#g; s#([("'\''`])/docs/#\1https://ddns.newfuture.cc/docs/#g' README.md
|
|
- name: Build package
|
|
run: python -m build --sdist --wheel --outdir dist/
|
|
|
|
- name: Test pip install from wheel
|
|
run: |
|
|
wheel_file=$(find dist -name "*.whl" -type f)
|
|
test -n "$wheel_file" || { echo "No wheel file found"; exit 1; }
|
|
python3 -m pip install --force-reinstall "$wheel_file"
|
|
ddns --version
|
|
ddns --help
|
|
ddns --new-config /tmp/test-pip-config.json
|
|
test -f /tmp/test-pip-config.json
|
|
python3 -m ddns --version
|
|
(cd /tmp && python3 -c 'from ddns.web.server import _resource_bytes; assert b"<html" in _resource_bytes("index.html")')
|
|
|
|
- name: Test pip install from source distribution
|
|
run: |
|
|
python3 -m pip uninstall -y ddns
|
|
tarball_file=$(find dist -name "*.tar.gz" -type f)
|
|
test -n "$tarball_file" || { echo "No tarball file found"; exit 1; }
|
|
python3 -m pip install --force-reinstall "$tarball_file"
|
|
ddns --version
|
|
python3 -m ddns --help
|
|
(cd /tmp && python3 -c 'from ddns.web.server import _resource_bytes; assert b"<html" in _resource_bytes("index.html")')
|
|
|
|
- name: run unit tests
|
|
run: python3 -m unittest -v
|
|
|
|
- name: Test task management functionality
|
|
run: tests/scripts/test-task-systemd.sh "python3 -m ddns"
|
|
|
|
- run: python3 -m ddns -c tests/config/callback.json
|
|
- run: python3 -m ddns -c tests/config/multi-provider.json
|
|
- run: python3 -m ddns -c tests/config/debug.json -c tests/config/noip.json
|
|
- name: Verify deployed debug configuration
|
|
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
run: python3 -m ddns -c https://ddns.newfuture.cc/tests/config/debug.json
|
|
- run: python3 -m ddns -c tests/config/he-proxies.json --debug
|
|
|
|
- uses: actions/upload-artifact@v7
|
|
with:
|
|
name: pypi
|
|
path: dist/
|
|
retention-days: ${{ github.event_name == 'push' && 14 || 3 }}
|
|
|
|
nuitka:
|
|
needs: [ python, lint ]
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- os: windows-latest
|
|
arch: x64
|
|
- os: windows-latest
|
|
arch: x86
|
|
- os: windows-11-arm
|
|
arch: arm64
|
|
- os: ubuntu-latest
|
|
arch: x64
|
|
- os: ubuntu-24.04-arm
|
|
arch: arm64
|
|
- os: macos-15-intel
|
|
arch: x64
|
|
- os: macos-latest
|
|
arch: arm64
|
|
runs-on: ${{ matrix.os }}
|
|
env:
|
|
OS_NAME: ${{ contains(matrix.os,'ubuntu') && 'ubuntu' || contains(matrix.os, 'mac') && 'mac' || 'windows' }}
|
|
timeout-minutes: ${{ matrix.arch == 'x86' && 25 || contains(matrix.os, 'windows') && 16 || 12 }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Set up Python 3.12
|
|
uses: actions/setup-python@v7
|
|
with:
|
|
python-version: 3.12
|
|
architecture: ${{ matrix.arch }}
|
|
|
|
- run: python3 .github/patch.py
|
|
|
|
- name: Set up on Linux
|
|
if: runner.os == 'Linux'
|
|
run: sudo apt-get install -y --no-install-recommends patchelf
|
|
|
|
- name: Set up on macOS
|
|
if: runner.os == 'macOS'
|
|
run: python3 -m pip install imageio
|
|
|
|
- run: python3 ./run.py -h
|
|
- name: run unit tests
|
|
run: python3 -m unittest -v
|
|
|
|
- name: Build Executable
|
|
uses: Nuitka/Nuitka-Action@v1.5
|
|
with:
|
|
caching-key: ${{ matrix.arch }}
|
|
nuitka-version: 4.2
|
|
script-name: run.py
|
|
mode: onefile
|
|
output-dir: dist
|
|
lto: yes
|
|
file-description: "DDNS客户端[测试版 Alpha]"
|
|
windows-console-mode: ${{ runner.os == 'Windows' && 'attach' || '' }}
|
|
windows-icon-from-ico: ${{ runner.os == 'Windows' && 'docs/public/favicon.ico' || '' }}
|
|
linux-icon: ${{ runner.os == 'Linux' && 'docs/public/img/ddns.svg' || '' }}
|
|
static-libpython: ${{ runner.os == 'Linux' && 'yes' || 'auto' }}
|
|
macos-app-name: ${{ runner.os == 'macOS' && 'DDNS' || '' }}
|
|
macos-app-icon: ${{ runner.os == 'macOS' && 'docs/public/img/ddns.png' || '' }}
|
|
|
|
- run: ./dist/ddns -v
|
|
- run: ./dist/ddns -h
|
|
- run: ./dist/ddns || test -f config.json
|
|
- name: Run onefile binary E2E tests
|
|
run: python -m unittest tests.e2e -v
|
|
env:
|
|
DDNS_E2E_EXECUTABLE: ./dist/ddns${{ runner.os == 'Windows' && '.exe' || '' }}
|
|
PYTHONIOENCODING: utf-8
|
|
|
|
- run: ./dist/ddns task --status
|
|
- name: Test Windows schtasks task
|
|
if: runner.os == 'Windows'
|
|
run: tests/scripts/test-task-windows.bat .\\dist\\ddns.exe
|
|
- name: Test Linux systemd service
|
|
if: runner.os == 'Linux'
|
|
run: tests/scripts/test-task-systemd.sh "./dist/ddns"
|
|
- name: Test macOS launchd service
|
|
if: runner.os == 'macOS'
|
|
run: tests/scripts/test-task-macos.sh "./dist/ddns"
|
|
|
|
- name: Build Windows App (standalone)
|
|
if: runner.os == 'Windows'
|
|
uses: Nuitka/Nuitka-Action@v1.5
|
|
with:
|
|
caching-key: ${{ matrix.arch }}
|
|
nuitka-version: 4.2
|
|
script-name: run.py
|
|
mode: standalone
|
|
output-dir: dist-app
|
|
lto: yes
|
|
file-description: "DDNS客户端[测试版 Alpha]"
|
|
windows-console-mode: attach
|
|
windows-icon-from-ico: docs/public/favicon.ico
|
|
|
|
- name: Run Windows standalone E2E tests
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
$appDir = Get-ChildItem -Path 'dist-app' -Directory -Filter '*.dist' | Select-Object -First 1
|
|
if (-not $appDir) { throw 'Standalone app folder (*.dist) not found' }
|
|
$executable = Join-Path $appDir.FullName 'ddns.exe'
|
|
if (-not (Test-Path $executable)) { throw 'Standalone ddns.exe not found' }
|
|
$env:DDNS_E2E_EXECUTABLE = $executable
|
|
$env:PYTHONIOENCODING = 'utf-8'
|
|
python -m unittest tests.e2e -v
|
|
|
|
- name: Package Windows app as ddns.zip
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
$ErrorActionPreference = 'Stop'
|
|
$appDir = Get-ChildItem -Path 'dist-app' -Directory -Filter '*.dist' | Select-Object -First 1
|
|
if (-not $appDir) { throw 'Standalone app folder (*.dist) not found in dist' }
|
|
if (Test-Path 'dist/ddns.zip') { Remove-Item 'dist/ddns.zip' -Force }
|
|
# Package the contents of the .dist folder directly, not the folder itself
|
|
Compress-Archive -Path "$($appDir.FullName)\*" -DestinationPath 'dist/ddns.zip'
|
|
# Clean up temporary output directory
|
|
Remove-Item 'dist-app' -Recurse -Force
|
|
|
|
# Upload build result
|
|
- name: Upload Artifacts
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: ddns-${{ env.OS_NAME }}-${{ matrix.arch }}
|
|
if-no-files-found: error
|
|
path: |
|
|
dist/*.exe
|
|
dist/*.bin
|
|
dist/*.app
|
|
dist/ddns
|
|
dist/*.zip
|
|
retention-days: ${{ github.event_name == 'push' && 30 || 3 }}
|
|
|
|
linux-binary:
|
|
needs: [ python ]
|
|
strategy:
|
|
matrix:
|
|
host: [ amd, arm ]
|
|
libc: [ musl, glibc ]
|
|
runs-on: ubuntu-${{ matrix.host == 'arm' && '24.04-arm' || 'latest' }}
|
|
env:
|
|
# glibc: 基于 debian linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8
|
|
# musl: 基于 alpine linux/386,linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6
|
|
platforms: ${{ matrix.host == 'amd' && 'linux/386,linux/amd64' || matrix.libc == 'glibc' && 'linux/arm/v7,linux/arm64/v8' || 'linux/arm/v6,linux/arm/v7,linux/arm64/v8' }}
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- uses: docker/setup-buildx-action@v4
|
|
- uses: docker/build-push-action@v7
|
|
with:
|
|
context: .
|
|
file: docker/${{ matrix.libc }}.Dockerfile
|
|
platforms: ${{ env.platforms }}
|
|
push: false
|
|
tags: ddnsbin
|
|
target: export
|
|
outputs: type=local,dest=./output
|
|
build-args: |
|
|
BUILDER=ghcr.io/newfuture/nuitka-builder:${{matrix.libc}}-master
|
|
GITHUB_REF_NAME=${{ github.ref_name }}
|
|
# 测试构建的二进制文件
|
|
- name: Test built binaries
|
|
run: |
|
|
set -ex
|
|
for f in output/*/ddns; do
|
|
platform=$(basename $(dirname "$f") | tr '_' '/')
|
|
tests/scripts/test-in-docker.sh $platform ${{matrix.libc}} "$f"
|
|
done
|
|
|
|
# 输出目录结构扁平化
|
|
- name: Flatten output directory structure
|
|
run: |
|
|
set -e
|
|
mkdir -p dist
|
|
for f in output/*/ddns; do
|
|
name=$(basename "$(dirname "$f")")
|
|
mv "$f" "dist/ddns-${{ matrix.libc }}-$name"
|
|
done
|
|
- name: Upload build result
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: ddns-${{ matrix.libc }}-${{ matrix.host}}
|
|
path: dist/*
|
|
if-no-files-found: error
|
|
retention-days: ${{ github.event_name == 'push' && 14 || 3 }}
|
|
|
|
prepare-docker:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 3
|
|
outputs:
|
|
builder_ref: ${{ steps.builder.outputs.builder_ref }}
|
|
builder_hash: ${{ steps.builder.outputs.builder_hash }}
|
|
steps:
|
|
- name: Resolve Nuitka builder image
|
|
id: builder
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
builder_tag=ghcr.io/newfuture/nuitka-builder:master
|
|
builder_info="$(docker buildx imagetools inspect "$builder_tag")"
|
|
digest="$(printf '%s\n' "$builder_info" | awk '$1 == "Digest:" { print $2; exit }')"
|
|
media_type="$(printf '%s\n' "$builder_info" | awk '$1 == "MediaType:" { print $2; exit }')"
|
|
builder_hash="${digest#sha256:}"
|
|
|
|
case "$media_type" in
|
|
application/vnd.docker.distribution.manifest.list.v2+json|application/vnd.oci.image.index.v1+json) ;;
|
|
*) echo "Expected a multi-platform image index, got: $media_type" >&2; exit 1 ;;
|
|
esac
|
|
test "${#builder_hash}" -eq 64
|
|
case "$builder_hash" in
|
|
*[!0-9a-f]*) echo "Invalid builder digest: $digest" >&2; exit 1 ;;
|
|
esac
|
|
|
|
echo "builder_ref=${builder_tag}@${digest}" >> "$GITHUB_OUTPUT"
|
|
echo "builder_hash=${builder_hash}" >> "$GITHUB_OUTPUT"
|
|
|
|
docker:
|
|
needs: [ python, prepare-docker ]
|
|
timeout-minutes: ${{ matrix.host == 'qemu' && 90 || 30 }}
|
|
strategy:
|
|
matrix:
|
|
host: ['amd','arm','qemu']
|
|
event:
|
|
- ${{github.event_name}}
|
|
|
|
exclude:
|
|
# PR 时不构建 QEMU 镜像
|
|
- host: qemu
|
|
event: pull_request
|
|
|
|
runs-on: ubuntu-${{ matrix.host == 'arm' && '24.04-arm' || 'latest' }}
|
|
env:
|
|
CACHE_WRITE: ${{
|
|
github.event_name == 'push' &&
|
|
(github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')
|
|
}}
|
|
platforms: ${{
|
|
matrix.host == 'amd' && 'linux/386,linux/amd64' ||
|
|
matrix.host == 'arm' && 'linux/arm/v6,linux/arm/v7,linux/arm64/v8'||
|
|
'linux/ppc64le,linux/riscv64,linux/s390x'
|
|
}}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: python3 .github/patch.py docker
|
|
env:
|
|
GITHUB_REF_NAME: ${{ github.ref_name }}
|
|
- uses: docker/setup-qemu-action@v4 # 仅仅在需要时启用 QEMU 支持
|
|
if: matrix.host == 'qemu'
|
|
with:
|
|
platforms: ${{ env.platforms }}
|
|
- uses: docker/setup-buildx-action@v4
|
|
id: buildx
|
|
|
|
- name: Restore ccache
|
|
if: env.CACHE_WRITE == 'true'
|
|
id: ccache
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: .build-cache/ccache
|
|
key: ddns-ccache-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ddns-ccache-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-
|
|
|
|
- name: Restore ccache (read-only)
|
|
if: env.CACHE_WRITE != 'true'
|
|
id: ccache_readonly
|
|
uses: actions/cache/restore@v6
|
|
with:
|
|
path: .build-cache/ccache
|
|
key: ddns-ccache-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ddns-ccache-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-
|
|
|
|
- name: Restore Nuitka module cache
|
|
if: env.CACHE_WRITE == 'true'
|
|
id: nuitka_module
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: .build-cache/nuitka-module
|
|
key: ddns-nuitka-module-mc7-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ddns-nuitka-module-mc7-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-
|
|
|
|
- name: Restore Nuitka module cache (read-only)
|
|
if: env.CACHE_WRITE != 'true'
|
|
id: nuitka_module_readonly
|
|
uses: actions/cache/restore@v6
|
|
with:
|
|
path: .build-cache/nuitka-module
|
|
key: ddns-nuitka-module-mc7-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ddns-nuitka-module-mc7-v2-${{ matrix.host }}-${{ needs.prepare-docker.outputs.builder_hash }}-
|
|
|
|
- name: Prepare compiler cache directories
|
|
run: mkdir -p .build-cache/ccache .build-cache/nuitka-module
|
|
|
|
- name: Restore Docker compiler cache mounts
|
|
uses: reproducible-containers/buildkit-cache-dance@5422eac04292c961a382e0f584ea0f03ad9da723
|
|
with:
|
|
builder: ${{ steps.buildx.outputs.name }}
|
|
cache-map: |
|
|
{
|
|
".build-cache/ccache": "/root/.cache/ccache",
|
|
".build-cache/nuitka-module": "/root/.cache/nuitka-module"
|
|
}
|
|
skip-extraction: ${{
|
|
(steps.ccache.outputs.cache-hit == 'true' ||
|
|
steps.ccache_readonly.outputs.cache-hit == 'true') &&
|
|
(steps.nuitka_module.outputs.cache-hit == 'true' ||
|
|
steps.nuitka_module_readonly.outputs.cache-hit == 'true')
|
|
}}
|
|
|
|
- uses: docker/build-push-action@v7
|
|
with:
|
|
context: .
|
|
file: docker/Dockerfile
|
|
platforms: ${{ env.platforms }}
|
|
push: false
|
|
tags: ddns:test
|
|
outputs: type=oci,dest=./multi-platform-image.tar
|
|
build-args: |
|
|
BUILDER=${{ needs.prepare-docker.outputs.builder_ref }}
|
|
GITHUB_REF_NAME=${{ github.ref_name }}
|
|
|
|
# 准备测试环境
|
|
- name: Prepare test environment
|
|
run: mkdir -p oci-image && tar -vxf multi-platform-image.tar -C oci-image
|
|
# 使用 skopeo 批量提取所有平台镜像,正确处理变体
|
|
- name: Extract platform images with skopeo
|
|
uses: addnab/docker-run-action@v3
|
|
with:
|
|
image: quay.io/skopeo/stable:latest
|
|
options: |
|
|
-v ${{ github.workspace }}/:/oci
|
|
-e PLATFORMS=${{ env.platforms }}
|
|
run: |
|
|
IFS="," read -ra PLATFORMS <<< "$PLATFORMS"
|
|
for platform in "${PLATFORMS[@]}"; do
|
|
echo "=== Extracting image for: $platform ==="
|
|
tag=$(echo "${{ github.ref_name || 'test' }}-$platform" | tr '/' '_')
|
|
arch=$(echo $platform | cut -d'/' -f2)
|
|
variant=$(echo $platform | cut -d'/' -f3)
|
|
variantFlag=""
|
|
if [ -n "$variant" ]; then
|
|
variantFlag="--override-variant $variant"
|
|
fi
|
|
skopeo copy --override-os linux --override-arch $arch $variantFlag \
|
|
oci:/oci/oci-image:test docker-archive:/oci/ddns-oci-${tag}.tar:${{ env.DOCKER_IMG }}:${tag}
|
|
done
|
|
# 测试各个平台的镜像
|
|
- name: Test platform images
|
|
run: |
|
|
set -e
|
|
# 解析平台列表
|
|
IFS=',' read -ra PLATFORMS <<< "${{ env.platforms }}"
|
|
|
|
# 测试每个平台的镜像
|
|
for platform in "${PLATFORMS[@]}"; do
|
|
echo "=== Testing platform: $platform ==="
|
|
# 将平台标识符转换为有效的文件名
|
|
tag=$(echo "${{ github.ref_name || 'test' }}-$platform" | tr '/' '_')
|
|
echo "Loading image for $platform..."
|
|
docker load < ddns-oci-${tag}.tar
|
|
echo "Running test..."
|
|
docker run --platform $platform --rm ${{ env.DOCKER_IMG }}:$tag -v
|
|
docker run --platform $platform --rm ${{ env.DOCKER_IMG }}:$tag -h
|
|
docker run --platform $platform --rm -v "$(pwd):/ddns/" ${{ env.DOCKER_IMG }}:$tag || test -e "config.json"
|
|
sudo rm -f config.json
|
|
echo "Testing with config files..."
|
|
docker run --platform $platform --rm -v "$(pwd):/ddns/" ${{ env.DOCKER_IMG }}:$tag -c /ddns/tests/config/callback.json
|
|
docker run --platform $platform --rm -v "$(pwd):/ddns/" ${{ env.DOCKER_IMG }}:$tag -c /ddns/tests/config/multi-provider.json
|
|
docker run --platform $platform --rm -v "$(pwd):/ddns/" ${{ env.DOCKER_IMG }}:$tag -c /ddns/tests/config/debug.json -c /ddns/tests/config/noip.json
|
|
case "${{ github.event_name }}" in
|
|
push|schedule|workflow_dispatch)
|
|
docker run --platform $platform --rm -v "$(pwd):/ddns/" ${{ env.DOCKER_IMG }}:$tag -c https://ddns.newfuture.cc/tests/config/debug.json
|
|
;;
|
|
*) echo "Skipping deployed-site configuration check for an untrusted event" ;;
|
|
esac
|
|
docker run --platform $platform --rm -v "$(pwd):/ddns/" ${{ env.DOCKER_IMG }}:$tag -c /ddns/tests/config/he-proxies.json
|
|
if [ "$platform" = "linux/amd64" ]; then
|
|
sh tests/scripts/test-docker-zombies.sh "${{ env.DOCKER_IMG }}:$tag" "$platform"
|
|
fi
|
|
done
|
|
|
|
# 上传测试结果和镜像
|
|
- name: Upload images
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: docker-${{ matrix.host }}
|
|
path: ddns-oci-*.tar
|
|
if-no-files-found: error
|
|
retention-days: ${{ github.event_name == 'push' && 7 || 3 }}
|
|
|
|
preview-pypi:
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'push'
|
|
needs: [lint, pypi, python]
|
|
timeout-minutes: 3
|
|
environment:
|
|
name: preview
|
|
url: https://test.pypi.org/project/ddns/
|
|
permissions:
|
|
id-token: write
|
|
steps:
|
|
- uses: actions/download-artifact@v8
|
|
with:
|
|
name: pypi
|
|
path: dist
|
|
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
with:
|
|
repository-url: https://test.pypi.org/legacy/
|
|
print-hash: true
|
|
verbose: true
|
|
|
|
preview-docker:
|
|
if: github.event_name == 'push'
|
|
needs: [lint, docker, python]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
environment:
|
|
name: preview
|
|
url: https://github.com/NewFuture/DDNS/pkgs/container/ddns/?tag=master
|
|
permissions:
|
|
packages: write
|
|
steps:
|
|
- uses: actions/download-artifact@v8
|
|
with:
|
|
name: docker-amd
|
|
- uses: actions/download-artifact@v8
|
|
with:
|
|
name: docker-arm
|
|
- uses: actions/download-artifact@v8
|
|
with:
|
|
name: docker-qemu
|
|
|
|
- uses: docker/login-action@v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
- uses: docker/login-action@v4
|
|
with:
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
- uses: docker/setup-buildx-action@v4
|
|
- name: docker load and push
|
|
run: |
|
|
set -ex
|
|
for f in $(ls ddns-oci-*.tar); do
|
|
docker load -i "$f"
|
|
docker push ${{ env.DOCKER_IMG }}:$(basename "$f" | sed 's/ddns-oci-//;s/.tar//')
|
|
done
|
|
- name: merge and push images
|
|
run: |
|
|
set -ex
|
|
docker buildx imagetools create \
|
|
-t ${{ env.DOCKER_IMG }}:${{ github.ref_name }} \
|
|
-t newfuture/ddns:${{ github.ref_name }} \
|
|
${{ github.ref_name=='master' && '-t $DOCKER_IMG:edge' }} \
|
|
${{ github.ref_name=='master' && '-t newfuture/ddns:edge' }} \
|
|
$(docker images --format "{{.Repository}}:{{.Tag}}" ${{ env.DOCKER_IMG }}:*) \
|
|
--annotation "index,manifest:org.opencontainers.image.url=https://ddns.newfuture.cc" \
|
|
--annotation "index,manifest:org.opencontainers.image.description=DDNS docker ${{ github.ref_name }} CI build (unstable version),集成测试(非稳定版)" \
|
|
--annotation "index,manifest:org.opencontainers.image.authors=NewFuture,NN708"
|
|
|
|
merge-gate:
|
|
name: Merge gate (advisory)
|
|
if: always()
|
|
needs: [lint, python, e2e, pypi, nuitka, linux-binary, prepare-docker, docker, preview-pypi, preview-docker]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Enforce required job policy
|
|
env:
|
|
NEEDS_JSON: ${{ toJSON(needs) }}
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
run: |
|
|
python3 - <<'PY'
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, "tools")
|
|
import check
|
|
|
|
failures = check.merge_gate_failures(json.loads(os.environ["NEEDS_JSON"]), os.environ["EVENT_NAME"])
|
|
if failures:
|
|
for failure in failures:
|
|
print("ERROR: {}".format(failure))
|
|
raise SystemExit(1)
|
|
print("Required job policy satisfied.")
|
|
PY
|