ci(release): deduplicate Linux build workflow
This commit is contained in:
502
.github/workflows/build-dockerhub.yml
vendored
502
.github/workflows/build-dockerhub.yml
vendored
@@ -3,9 +3,9 @@ name: Build and Push Docker image
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev # Only dev branch triggers on push
|
||||
- dev
|
||||
tags:
|
||||
- 'v*.*.*' # Tag push triggers master build
|
||||
- 'v*.*.*'
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
- 'README-*.md'
|
||||
@@ -16,13 +16,13 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
workflow_dispatch: {} # Manual trigger
|
||||
workflow_dispatch: {}
|
||||
schedule:
|
||||
- cron: '0 19 * * 0' # Run at 03:00 (UTC+8) on Mondays
|
||||
- cron: '0 19 * * 0'
|
||||
|
||||
permissions:
|
||||
contents: write # Changed from 'read' to enable auto-commit and release creation
|
||||
packages: write # Required for GHCR push
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: dockerhub-${{ github.ref_name }}
|
||||
@@ -32,7 +32,6 @@ jobs:
|
||||
prepare:
|
||||
name: "🔧 Prepare Metadata"
|
||||
runs-on: ubuntu-latest
|
||||
# Skip if [skip ci] and ensure schedule only runs on master
|
||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]') && (github.event_name != 'schedule' || github.ref == 'refs/heads/master')
|
||||
outputs:
|
||||
sha_short: ${{ steps.vars.outputs.sha_short }}
|
||||
@@ -40,6 +39,7 @@ jobs:
|
||||
is_release: ${{ steps.version.outputs.is_release }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
build_date: ${{ steps.build_date.outputs.build_date }}
|
||||
linux_matrix: ${{ steps.linux_matrix.outputs.matrix }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -55,19 +55,16 @@ jobs:
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||
# Tag triggered: use tag as version
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "is_release=true" >> $GITHUB_OUTPUT
|
||||
echo "Detected version from tag: $VERSION"
|
||||
elif [[ "${{ github.ref }}" == refs/heads/master ]] && ([[ "${{ github.event_name }}" == "workflow_dispatch" ]] || [[ "${{ github.event_name }}" == "schedule" ]]); then
|
||||
# Master manual/schedule trigger: use latest tag as version
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
if [ -n "$LATEST_TAG" ]; then
|
||||
VERSION="$LATEST_TAG"
|
||||
echo "Detected latest tag: $VERSION"
|
||||
else
|
||||
# Fallback if no tags exist
|
||||
VERSION="master-$(git rev-parse --short HEAD)"
|
||||
echo "No tags found, using commit hash: $VERSION"
|
||||
fi
|
||||
@@ -75,7 +72,6 @@ jobs:
|
||||
echo "is_release=true" >> $GITHUB_OUTPUT
|
||||
echo "Master branch build (manual/schedule): $VERSION"
|
||||
else
|
||||
# Dev branch: use "dev"
|
||||
echo "version=dev" >> $GITHUB_OUTPUT
|
||||
echo "is_release=false" >> $GITHUB_OUTPUT
|
||||
echo "Using dev version"
|
||||
@@ -85,6 +81,28 @@ jobs:
|
||||
id: build_date
|
||||
run: echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Linux matrix
|
||||
id: linux_matrix
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "${{ steps.version.outputs.is_release }}" = "true" ]; then
|
||||
{
|
||||
echo 'matrix<<JSON'
|
||||
cat <<'JSON'
|
||||
{"include":[{"arch":"amd64","runner":"ubuntu-latest","dockerfile":"./Dockerfile","builder_platform":"linux/amd64","image_platform":"linux/amd64","builder_tag":"subconverter-temp:amd64-builder","threads":"16","cache_scope":"subconverter-alpine","extract_mode":"shared","extract_generated":"true","openwrt_arches":"x86_64","qemu_platforms":""},{"arch":"arm64","runner":"ubuntu-24.04-arm","dockerfile":"./Dockerfile","builder_platform":"linux/arm64","image_platform":"linux/arm64","builder_tag":"subconverter-temp:arm64-builder","threads":"4","cache_scope":"subconverter-alpine-arm64","extract_mode":"shared","extract_generated":"false","openwrt_arches":"aarch64_generic,aarch64_cortex-a53,aarch64_cortex-a72","qemu_platforms":""},{"arch":"armv7","runner":"ubuntu-latest","dockerfile":"./docker/Dockerfile.armv7-cross","builder_platform":"linux/amd64","image_platform":"linux/arm/v7","builder_tag":"subconverter-temp:armv7-builder","threads":"2","cache_scope":"subconverter-armv7-cross","extract_mode":"root","extract_generated":"false","openwrt_arches":"arm_cortex-a5_vfpv4,arm_cortex-a7,arm_cortex-a7_vfpv4,arm_cortex-a7_neon-vfpv4,arm_cortex-a8_vfpv3,arm_cortex-a9,arm_cortex-a9_neon,arm_cortex-a9_vfpv3-d16,arm_cortex-a15_neon-vfpv4","qemu_platforms":"arm"}]}
|
||||
JSON
|
||||
echo 'JSON'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
{
|
||||
echo 'matrix<<JSON'
|
||||
cat <<'JSON'
|
||||
{"include":[{"arch":"amd64","runner":"ubuntu-latest","dockerfile":"./Dockerfile","builder_platform":"linux/amd64","image_platform":"linux/amd64","builder_tag":"subconverter-temp:amd64-builder","threads":"16","cache_scope":"subconverter-alpine","extract_mode":"shared","extract_generated":"true","openwrt_arches":"x86_64","qemu_platforms":""}]}
|
||||
JSON
|
||||
echo 'JSON'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
||||
@@ -97,12 +115,13 @@ jobs:
|
||||
type=raw,value=${{ steps.version.outputs.version }},enable=${{ steps.version.outputs.is_release == 'true' }}
|
||||
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
|
||||
|
||||
build-amd64:
|
||||
name: "🐳 Docker Build (amd64)"
|
||||
runs-on: ubuntu-latest
|
||||
build-linux:
|
||||
name: "🐳 Docker Build (${{ matrix.arch }})"
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: prepare
|
||||
outputs:
|
||||
digest: ${{ steps.build_image.outputs.digest }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.linux_matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -128,109 +147,93 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Build ONCE to builder stage and load to Docker (using Alpine for smaller image)
|
||||
- name: Build to builder stage (single build)
|
||||
- name: Prepare Docker build args
|
||||
id: docker_args
|
||||
shell: bash
|
||||
env:
|
||||
THREADS: ${{ matrix.threads }}
|
||||
SHA: ${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE: ${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF: Meta
|
||||
MIHOMO_CACHE_BUST: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && matrix.extract_generated == 'true' && github.run_id || 'stable' }}
|
||||
SOURCE_DEPS_CACHE_BUST: ${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && matrix.extract_generated == 'true' }}
|
||||
REFRESH_HEADERS: ${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && matrix.extract_generated == 'true' }}
|
||||
run: |
|
||||
{
|
||||
echo 'args<<EOF'
|
||||
bash scripts/ci/docker-build-args.sh
|
||||
echo 'EOF'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build to builder stage
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
file: ${{ matrix.dockerfile }}
|
||||
load: true
|
||||
tags: subconverter-temp:builder
|
||||
tags: ${{ matrix.builder_tag }}
|
||||
target: builder
|
||||
build-args: |
|
||||
THREADS=16
|
||||
SHA=${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION=${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE=${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF=Meta
|
||||
MIHOMO_CACHE_BUST=${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && github.run_id || 'stable' }}
|
||||
SOURCE_DEPS_CACHE_BUST=${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS=${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }}
|
||||
REFRESH_HEADERS=${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }}
|
||||
cache-from: type=gha,scope=subconverter-alpine
|
||||
cache-to: type=gha,mode=max,scope=subconverter-alpine
|
||||
platforms: ${{ matrix.builder_platform }}
|
||||
build-args: ${{ steps.docker_args.outputs.args }}
|
||||
cache-from: type=gha,scope=${{ matrix.cache_scope }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.cache_scope }}
|
||||
|
||||
# Extract ALL generated files from the single build
|
||||
- name: Extract generated files
|
||||
run: |
|
||||
CONTAINER_ID=$(docker create subconverter-temp:builder)
|
||||
|
||||
# Go module files
|
||||
docker cp $CONTAINER_ID:/src/bridge/go.mod bridge/go.mod
|
||||
docker cp $CONTAINER_ID:/src/bridge/go.sum bridge/go.sum
|
||||
docker cp $CONTAINER_ID:/src/bridge/libmihomo.h bridge/libmihomo.h
|
||||
|
||||
# Go-generated header files
|
||||
docker cp $CONTAINER_ID:/src/src/parser/mihomo_schemes.h src/parser/mihomo_schemes.h
|
||||
docker cp $CONTAINER_ID:/src/src/parser/param_compat.h src/parser/param_compat.h
|
||||
|
||||
# Header-only libraries
|
||||
docker cp $CONTAINER_ID:/src/include/httplib.h include/httplib.h
|
||||
docker cp $CONTAINER_ID:/src/include/nlohmann/json.hpp include/nlohmann/json.hpp
|
||||
docker cp $CONTAINER_ID:/src/include/inja.hpp include/inja.hpp
|
||||
docker cp $CONTAINER_ID:/src/include/jpcre2.hpp include/jpcre2.hpp
|
||||
docker cp $CONTAINER_ID:/src/include/quickjspp.hpp include/quickjspp.hpp
|
||||
rm -rf include/libcron include/date include/toml11 include/toml.hpp
|
||||
docker cp $CONTAINER_ID:/src/include/libcron include/
|
||||
docker cp $CONTAINER_ID:/src/include/date include/
|
||||
docker cp $CONTAINER_ID:/src/include/toml.hpp include/toml.hpp
|
||||
docker cp $CONTAINER_ID:/src/include/toml11 include/
|
||||
find include/libcron include/date include/toml11 -type f -print0 | xargs -0 sed -i 's/[[:blank:]]\+$//'
|
||||
sed -i 's/[[:blank:]]\+$//' include/toml.hpp
|
||||
|
||||
# [NEW] Extract Binary for Release (if needed)
|
||||
if [ "${{ needs.prepare.outputs.is_release }}" == "true" ]; then
|
||||
echo "Extracting binary for release..."
|
||||
docker cp $CONTAINER_ID:/src/subconverter ./subconverter
|
||||
docker cp $CONTAINER_ID:/runtime-libs ./runtime-libs
|
||||
docker cp $CONTAINER_ID:/usr/lib/libmihomo.so ./libmihomo.so
|
||||
fi
|
||||
- name: Extract builder output
|
||||
shell: bash
|
||||
env:
|
||||
EXTRACT_GENERATED: ${{ matrix.extract_generated }}
|
||||
run: bash scripts/ci/extract-builder-output.sh "${{ matrix.builder_tag }}" "${{ matrix.extract_mode }}"
|
||||
|
||||
docker rm $CONTAINER_ID
|
||||
|
||||
# Package Release Binary
|
||||
- name: Package Release Artifact (Linux amd64)
|
||||
- name: Package Release Artifacts (Linux ${{ matrix.arch }})
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
run: |
|
||||
bash scripts/package-linux-portable.sh "${{ needs.prepare.outputs.version }}" amd64
|
||||
bash scripts/package-openwrt-apk.sh "${{ needs.prepare.outputs.version }}" amd64 "x86_64"
|
||||
rm -rf runtime-libs libmihomo.so
|
||||
shell: bash
|
||||
run: bash scripts/ci/build-linux-release.sh "${{ needs.prepare.outputs.version }}" "${{ matrix.arch }}" "${{ matrix.openwrt_arches }}"
|
||||
|
||||
- name: Smoke test artifact (Linux amd64)
|
||||
- name: Set up QEMU for smoke test
|
||||
if: needs.prepare.outputs.is_release == 'true' && matrix.qemu_platforms != ''
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
with:
|
||||
platforms: ${{ matrix.qemu_platforms }}
|
||||
|
||||
- name: Smoke test artifact (Linux ${{ matrix.arch }})
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-linux-artifact
|
||||
with:
|
||||
artifact: SubConverter-Extended-${{ needs.prepare.outputs.version }}-linux-amd64.tar.gz
|
||||
artifact: SubConverter-Extended-${{ needs.prepare.outputs.version }}-linux-${{ matrix.arch }}.tar.gz
|
||||
|
||||
- name: Smoke test APKs (OpenWrt amd64)
|
||||
- name: Smoke test APKs (OpenWrt ${{ matrix.arch }})
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-openwrt-apk
|
||||
with:
|
||||
artifacts: SubConverter-Extended-${{ needs.prepare.outputs.version }}-openwrt-*.apk
|
||||
|
||||
# Upload Release Artifact
|
||||
- name: Upload Artifact (Linux amd64)
|
||||
- name: Upload Artifact (Linux ${{ matrix.arch }})
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-amd64
|
||||
name: linux-${{ matrix.arch }}
|
||||
path: SubConverter-Extended-*.tar.gz
|
||||
|
||||
- name: Upload Artifact (OpenWrt amd64)
|
||||
- name: Upload Artifact (OpenWrt ${{ matrix.arch }})
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: openwrt-amd64
|
||||
name: openwrt-${{ matrix.arch }}
|
||||
path: SubConverter-Extended-*-openwrt-*.apk
|
||||
|
||||
- name: Clean release packaging files
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
run: rm -rf SubConverter-Extended SubConverter-Extended-*.tar.gz SubConverter-Extended-*.apk subconverter runtime-libs libmihomo.so build/openwrt-apk
|
||||
shell: bash
|
||||
run: rm -rf SubConverter-Extended SubConverter-Extended-*.tar.gz SubConverter-Extended-*.apk subconverter runtime-libs runtime-root libmihomo.so build/openwrt-apk
|
||||
|
||||
- name: Check for file changes
|
||||
id: check_changes
|
||||
if: matrix.extract_generated == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain -- bridge/go.mod bridge/go.sum bridge/libmihomo.h src/parser/mihomo_schemes.h src/parser/param_compat.h include/httplib.h include/nlohmann/json.hpp include/inja.hpp include/jpcre2.hpp include/quickjspp.hpp include/libcron include/date include/toml.hpp include/toml11)" ]; then
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
@@ -238,21 +241,23 @@ jobs:
|
||||
|
||||
- name: Commit and push generated files
|
||||
if: steps.check_changes.outputs.changed == 'true' && github.event_name != 'pull_request' && github.ref == 'refs/heads/dev'
|
||||
shell: bash
|
||||
run: |
|
||||
BRANCH_NAME="dev"
|
||||
echo "Dev branch build, pushing generated files to: $BRANCH_NAME"
|
||||
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add bridge/go.mod bridge/go.sum bridge/libmihomo.h src/parser/mihomo_schemes.h src/parser/param_compat.h include/httplib.h include/nlohmann/json.hpp include/inja.hpp include/jpcre2.hpp include/quickjspp.hpp include/libcron include/date include/toml.hpp include/toml11
|
||||
git commit -m "chore: update auto-generated files and header libraries from build [skip ci]"
|
||||
|
||||
|
||||
git fetch --no-tags origin "refs/heads/$BRANCH_NAME:refs/remotes/origin/$BRANCH_NAME"
|
||||
git rebase "origin/$BRANCH_NAME"
|
||||
git push origin "HEAD:refs/heads/$BRANCH_NAME"
|
||||
|
||||
- name: Report generated file changes outside dev
|
||||
if: steps.check_changes.outputs.changed == 'true' && github.event_name != 'pull_request' && github.ref != 'refs/heads/dev'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::warning::Generated file changes were detected on ${GITHUB_REF}, but source updates are committed only to dev."
|
||||
|
||||
@@ -261,288 +266,35 @@ jobs:
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
file: ${{ matrix.dockerfile }}
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/amd64
|
||||
platforms: ${{ matrix.image_platform }}
|
||||
tags: ${{ needs.prepare.outputs.tags }}
|
||||
build-args: |
|
||||
THREADS=16
|
||||
SHA=${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION=${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE=${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF=Meta
|
||||
MIHOMO_CACHE_BUST=${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' && github.run_id || 'stable' }}
|
||||
SOURCE_DEPS_CACHE_BUST=${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS=${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }}
|
||||
REFRESH_HEADERS=${{ github.event_name == 'push' && github.ref == 'refs/heads/dev' }}
|
||||
cache-from: type=gha,scope=subconverter-alpine
|
||||
cache-to: type=gha,mode=max,scope=subconverter-alpine
|
||||
build-args: ${{ steps.docker_args.outputs.args }}
|
||||
cache-from: type=gha,scope=${{ matrix.cache_scope }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.cache_scope }}
|
||||
|
||||
- name: Smoke test image (amd64)
|
||||
- name: Save image digest
|
||||
if: needs.prepare.outputs.is_release == 'true' && github.event_name != 'pull_request'
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p docker-digests
|
||||
printf '%s\n' "${{ steps.build_image.outputs.digest }}" > "docker-digests/${{ matrix.arch }}.txt"
|
||||
|
||||
- name: Upload image digest
|
||||
if: needs.prepare.outputs.is_release == 'true' && github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: docker-digest-${{ matrix.arch }}
|
||||
path: docker-digests/${{ matrix.arch }}.txt
|
||||
|
||||
- name: Smoke test image (${{ matrix.arch }})
|
||||
if: github.event_name != 'pull_request' && needs.prepare.outputs.is_release == 'true'
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-docker-image
|
||||
with:
|
||||
image: ghcr.io/aethersailor/subconverter-extended@${{ steps.build_image.outputs.digest }}
|
||||
platform: linux/amd64
|
||||
|
||||
build-arm64:
|
||||
name: "🐳 Docker Build (arm64)"
|
||||
runs-on: ubuntu-24.04-arm
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
outputs:
|
||||
digest: ${{ steps.build_image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ github.event_name == 'pull_request' && github.token || secrets.PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Build to builder stage FIRST to extract binary (using Alpine for smaller image)
|
||||
- name: Build to builder stage (extract binary)
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
load: true
|
||||
tags: subconverter-temp:arm64-builder
|
||||
target: builder
|
||||
platforms: linux/arm64
|
||||
build-args: |
|
||||
THREADS=4
|
||||
SHA=${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION=${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE=${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF=Meta
|
||||
MIHOMO_CACHE_BUST=stable
|
||||
SOURCE_DEPS_CACHE_BUST=${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS=false
|
||||
REFRESH_HEADERS=false
|
||||
cache-from: type=gha,scope=subconverter-alpine-arm64
|
||||
cache-to: type=gha,mode=max,scope=subconverter-alpine-arm64
|
||||
|
||||
- name: Extract binaries
|
||||
run: |
|
||||
CONTAINER_ID=$(docker create subconverter-temp:arm64-builder)
|
||||
docker cp $CONTAINER_ID:/src/subconverter ./subconverter
|
||||
docker cp $CONTAINER_ID:/runtime-libs ./runtime-libs
|
||||
docker cp $CONTAINER_ID:/usr/lib/libmihomo.so ./libmihomo.so
|
||||
docker rm $CONTAINER_ID
|
||||
chmod +x subconverter
|
||||
|
||||
- name: Package Release Artifact (Linux arm64)
|
||||
run: |
|
||||
bash scripts/package-linux-portable.sh "${{ needs.prepare.outputs.version }}" arm64
|
||||
bash scripts/package-openwrt-apk.sh "${{ needs.prepare.outputs.version }}" arm64 "aarch64_generic,aarch64_cortex-a53,aarch64_cortex-a72"
|
||||
rm -rf runtime-libs libmihomo.so
|
||||
|
||||
- name: Smoke test artifact (Linux arm64)
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-linux-artifact
|
||||
with:
|
||||
artifact: SubConverter-Extended-${{ needs.prepare.outputs.version }}-linux-arm64.tar.gz
|
||||
|
||||
- name: Smoke test APKs (OpenWrt arm64)
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-openwrt-apk
|
||||
with:
|
||||
artifacts: SubConverter-Extended-${{ needs.prepare.outputs.version }}-openwrt-*.apk
|
||||
|
||||
- name: Upload Artifact (Linux arm64)
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-arm64
|
||||
path: SubConverter-Extended-*.tar.gz
|
||||
|
||||
- name: Upload Artifact (OpenWrt arm64)
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: openwrt-arm64
|
||||
path: SubConverter-Extended-*-openwrt-*.apk
|
||||
|
||||
- name: Clean release packaging files
|
||||
run: rm -rf SubConverter-Extended SubConverter-Extended-*.tar.gz SubConverter-Extended-*.apk subconverter runtime-libs libmihomo.so build/openwrt-apk
|
||||
|
||||
- name: Build and push final image
|
||||
id: build_image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/arm64
|
||||
tags: ${{ needs.prepare.outputs.tags }}
|
||||
build-args: |
|
||||
THREADS=4
|
||||
SHA=${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION=${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE=${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF=Meta
|
||||
MIHOMO_CACHE_BUST=stable
|
||||
SOURCE_DEPS_CACHE_BUST=${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS=false
|
||||
REFRESH_HEADERS=false
|
||||
cache-from: type=gha,scope=subconverter-alpine-arm64
|
||||
cache-to: type=gha,mode=max,scope=subconverter-alpine-arm64
|
||||
|
||||
- name: Smoke test image (arm64)
|
||||
if: github.event_name != 'pull_request'
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-docker-image
|
||||
with:
|
||||
image: ghcr.io/aethersailor/subconverter-extended@${{ steps.build_image.outputs.digest }}
|
||||
platform: linux/arm64
|
||||
|
||||
build-armv7:
|
||||
name: "🐳 Docker Build (armv7)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.is_release == 'true'
|
||||
timeout-minutes: 25
|
||||
outputs:
|
||||
digest: ${{ steps.build_image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ github.event_name == 'pull_request' && github.token || secrets.PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to GHCR
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build to builder stage (extract binary)
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.armv7-cross
|
||||
load: true
|
||||
tags: subconverter-temp:armv7-builder
|
||||
target: builder
|
||||
build-args: |
|
||||
THREADS=2
|
||||
SHA=${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION=${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE=${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF=Meta
|
||||
MIHOMO_CACHE_BUST=stable
|
||||
SOURCE_DEPS_CACHE_BUST=${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS=false
|
||||
REFRESH_HEADERS=false
|
||||
cache-from: type=gha,scope=subconverter-armv7-cross
|
||||
cache-to: type=gha,mode=max,scope=subconverter-armv7-cross
|
||||
|
||||
- name: Extract binaries
|
||||
run: |
|
||||
CONTAINER_ID=$(docker create subconverter-temp:armv7-builder)
|
||||
docker cp $CONTAINER_ID:/src/subconverter ./subconverter
|
||||
docker cp $CONTAINER_ID:/runtime-root ./runtime-root
|
||||
docker rm $CONTAINER_ID
|
||||
chmod +x subconverter
|
||||
|
||||
- name: Package Release Artifact (Linux armv7)
|
||||
run: |
|
||||
bash scripts/package-linux-portable.sh "${{ needs.prepare.outputs.version }}" armv7
|
||||
bash scripts/package-openwrt-apk.sh "${{ needs.prepare.outputs.version }}" armv7 "arm_cortex-a5_vfpv4,arm_cortex-a7,arm_cortex-a7_vfpv4,arm_cortex-a7_neon-vfpv4,arm_cortex-a8_vfpv3,arm_cortex-a9,arm_cortex-a9_neon,arm_cortex-a9_vfpv3-d16,arm_cortex-a15_neon-vfpv4"
|
||||
rm -rf runtime-root
|
||||
|
||||
- name: Set up QEMU for smoke test
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
with:
|
||||
platforms: arm
|
||||
|
||||
- name: Smoke test artifact (Linux armv7)
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-linux-artifact
|
||||
with:
|
||||
artifact: SubConverter-Extended-${{ needs.prepare.outputs.version }}-linux-armv7.tar.gz
|
||||
|
||||
- name: Smoke test APKs (OpenWrt armv7)
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-openwrt-apk
|
||||
with:
|
||||
artifacts: SubConverter-Extended-${{ needs.prepare.outputs.version }}-openwrt-*.apk
|
||||
|
||||
- name: Upload Artifact (Linux armv7)
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: linux-armv7
|
||||
path: SubConverter-Extended-*.tar.gz
|
||||
|
||||
- name: Upload Artifact (OpenWrt armv7)
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: openwrt-armv7
|
||||
path: SubConverter-Extended-*-openwrt-*.apk
|
||||
|
||||
- name: Clean release packaging files
|
||||
run: rm -rf SubConverter-Extended SubConverter-Extended-*.tar.gz SubConverter-Extended-*.apk subconverter runtime-root build/openwrt-apk
|
||||
|
||||
- name: Build and push final image
|
||||
id: build_image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.armv7-cross
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/arm/v7
|
||||
tags: ${{ needs.prepare.outputs.tags }}
|
||||
build-args: |
|
||||
THREADS=2
|
||||
SHA=${{ needs.prepare.outputs.sha_short }}
|
||||
VERSION=${{ needs.prepare.outputs.version }}
|
||||
BUILD_DATE=${{ needs.prepare.outputs.build_date }}
|
||||
MIHOMO_REF=Meta
|
||||
MIHOMO_CACHE_BUST=stable
|
||||
SOURCE_DEPS_CACHE_BUST=${{ github.event_name != 'pull_request' && github.run_id || 'stable' }}
|
||||
REFRESH_GO_DEPS=false
|
||||
REFRESH_HEADERS=false
|
||||
cache-from: type=gha,scope=subconverter-armv7-cross
|
||||
cache-to: type=gha,mode=max,scope=subconverter-armv7-cross
|
||||
|
||||
- name: Smoke test image (armv7)
|
||||
if: github.event_name != 'pull_request'
|
||||
timeout-minutes: 3
|
||||
uses: ./.github/actions/smoke-docker-image
|
||||
with:
|
||||
image: ghcr.io/aethersailor/subconverter-extended@${{ steps.build_image.outputs.digest }}
|
||||
platform: linux/arm/v7
|
||||
platform: ${{ matrix.image_platform }}
|
||||
|
||||
build-windows-amd64:
|
||||
name: "🪟 Windows Build (amd64)"
|
||||
@@ -606,7 +358,7 @@ jobs:
|
||||
merge-manifest:
|
||||
name: "🔗 Docker Manifest"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare, build-amd64, build-arm64, build-armv7]
|
||||
needs: [prepare, build-linux]
|
||||
if: needs.prepare.outputs.is_release == 'true' && github.event_name != 'pull_request'
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
@@ -625,26 +377,45 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Download image digests
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: digests
|
||||
pattern: docker-digest-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create and push manifest list
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for arch in amd64 arm64 armv7; do
|
||||
test -s "digests/${arch}.txt"
|
||||
done
|
||||
|
||||
AMD64_DIGEST="$(cat digests/amd64.txt)"
|
||||
ARM64_DIGEST="$(cat digests/arm64.txt)"
|
||||
ARMV7_DIGEST="$(cat digests/armv7.txt)"
|
||||
TAGS="${{ needs.prepare.outputs.tags }}"
|
||||
IMAGE_NAMES=(
|
||||
"aethersailor/subconverter-extended"
|
||||
"ghcr.io/aethersailor/subconverter-extended"
|
||||
)
|
||||
|
||||
for IMAGE_NAME in "${IMAGE_NAMES[@]}"; do
|
||||
for TAG in $TAGS; do
|
||||
if [[ "$TAG" == "$IMAGE_NAME:"* ]]; then
|
||||
docker buildx imagetools create \
|
||||
-t "$TAG" \
|
||||
"${IMAGE_NAME}@${{ needs.build-amd64.outputs.digest }}" \
|
||||
"${IMAGE_NAME}@${{ needs.build-arm64.outputs.digest }}" \
|
||||
"${IMAGE_NAME}@${{ needs.build-armv7.outputs.digest }}"
|
||||
"${IMAGE_NAME}@${AMD64_DIGEST}" \
|
||||
"${IMAGE_NAME}@${ARM64_DIGEST}" \
|
||||
"${IMAGE_NAME}@${ARMV7_DIGEST}"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
- name: Verify manifest platforms
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -677,11 +448,10 @@ jobs:
|
||||
done
|
||||
done
|
||||
|
||||
# Release Creation Job (Only on Tag)
|
||||
create-release:
|
||||
name: "🚀 Release Artifacts"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare, build-amd64, build-arm64, build-armv7, build-windows-amd64, merge-manifest]
|
||||
needs: [prepare, build-linux, build-windows-amd64, merge-manifest]
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
2
.github/workflows/sync-upstream-parser.yml
vendored
2
.github/workflows/sync-upstream-parser.yml
vendored
@@ -270,7 +270,7 @@ jobs:
|
||||
fi
|
||||
|
||||
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
||||
if printf '%s\n' "$changed_files" | grep -Eq '^(src|bridge|include|CMakeLists\.txt|Dockerfile)'; then
|
||||
if printf '%s\n' "$changed_files" | grep -Eq '^(src|bridge|include|CMakeLists\.txt|Dockerfile|docker/Dockerfile)'; then
|
||||
echo "has_source_changes=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_source_changes=false" >> "$GITHUB_OUTPUT"
|
||||
|
||||
@@ -29,7 +29,8 @@ the shared object, avoiding the musl initialization crash seen with static
|
||||
|
||||
### Debian Docker image
|
||||
|
||||
`Dockerfile.debian` builds `libmihomo.a` with `go build -buildmode=c-archive`.
|
||||
`docker/Dockerfile.debian` builds `libmihomo.a` with `go build
|
||||
-buildmode=c-archive`.
|
||||
|
||||
This path is kept for glibc-based binary builds where static archive linking is
|
||||
stable and easier to package.
|
||||
|
||||
9
scripts/ci/build-linux-release.sh
Normal file
9
scripts/ci/build-linux-release.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:?version is required}"
|
||||
ARCH="${2:?linux arch is required}"
|
||||
OPENWRT_ARCHES="${3:?OpenWrt apk arch list is required}"
|
||||
|
||||
bash scripts/package-linux-portable.sh "${VERSION}" "${ARCH}"
|
||||
bash scripts/package-openwrt-apk.sh "${VERSION}" "${ARCH}" "${OPENWRT_ARCHES}"
|
||||
25
scripts/ci/docker-build-args.sh
Normal file
25
scripts/ci/docker-build-args.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
: "${THREADS:?THREADS is required}"
|
||||
: "${SHA:?SHA is required}"
|
||||
: "${VERSION:?VERSION is required}"
|
||||
: "${BUILD_DATE:?BUILD_DATE is required}"
|
||||
|
||||
MIHOMO_REF="${MIHOMO_REF:-Meta}"
|
||||
MIHOMO_CACHE_BUST="${MIHOMO_CACHE_BUST:-stable}"
|
||||
SOURCE_DEPS_CACHE_BUST="${SOURCE_DEPS_CACHE_BUST:-stable}"
|
||||
REFRESH_GO_DEPS="${REFRESH_GO_DEPS:-false}"
|
||||
REFRESH_HEADERS="${REFRESH_HEADERS:-false}"
|
||||
|
||||
cat <<EOF
|
||||
THREADS=${THREADS}
|
||||
SHA=${SHA}
|
||||
VERSION=${VERSION}
|
||||
BUILD_DATE=${BUILD_DATE}
|
||||
MIHOMO_REF=${MIHOMO_REF}
|
||||
MIHOMO_CACHE_BUST=${MIHOMO_CACHE_BUST}
|
||||
SOURCE_DEPS_CACHE_BUST=${SOURCE_DEPS_CACHE_BUST}
|
||||
REFRESH_GO_DEPS=${REFRESH_GO_DEPS}
|
||||
REFRESH_HEADERS=${REFRESH_HEADERS}
|
||||
EOF
|
||||
57
scripts/ci/extract-builder-output.sh
Normal file
57
scripts/ci/extract-builder-output.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
IMAGE="${1:?builder image tag is required}"
|
||||
MODE="${2:?extract mode is required: shared or root}"
|
||||
EXTRACT_GENERATED="${EXTRACT_GENERATED:-false}"
|
||||
|
||||
case "${MODE}" in
|
||||
shared|root) ;;
|
||||
*)
|
||||
echo "Unsupported extract mode: ${MODE}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
CID="$(docker create "${IMAGE}")"
|
||||
cleanup() {
|
||||
docker rm "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
docker cp "${CID}:/src/subconverter" ./subconverter
|
||||
chmod +x ./subconverter
|
||||
|
||||
case "${MODE}" in
|
||||
shared)
|
||||
docker cp "${CID}:/runtime-libs" ./runtime-libs
|
||||
docker cp "${CID}:/usr/lib/libmihomo.so" ./libmihomo.so
|
||||
;;
|
||||
root)
|
||||
docker cp "${CID}:/runtime-root" ./runtime-root
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "${EXTRACT_GENERATED}" = "true" ]; then
|
||||
docker cp "${CID}:/src/bridge/go.mod" bridge/go.mod
|
||||
docker cp "${CID}:/src/bridge/go.sum" bridge/go.sum
|
||||
docker cp "${CID}:/src/bridge/libmihomo.h" bridge/libmihomo.h
|
||||
|
||||
docker cp "${CID}:/src/src/parser/mihomo_schemes.h" src/parser/mihomo_schemes.h
|
||||
docker cp "${CID}:/src/src/parser/param_compat.h" src/parser/param_compat.h
|
||||
|
||||
docker cp "${CID}:/src/include/httplib.h" include/httplib.h
|
||||
docker cp "${CID}:/src/include/nlohmann/json.hpp" include/nlohmann/json.hpp
|
||||
docker cp "${CID}:/src/include/inja.hpp" include/inja.hpp
|
||||
docker cp "${CID}:/src/include/jpcre2.hpp" include/jpcre2.hpp
|
||||
docker cp "${CID}:/src/include/quickjspp.hpp" include/quickjspp.hpp
|
||||
|
||||
rm -rf include/libcron include/date include/toml11 include/toml.hpp
|
||||
docker cp "${CID}:/src/include/libcron" include/
|
||||
docker cp "${CID}:/src/include/date" include/
|
||||
docker cp "${CID}:/src/include/toml.hpp" include/toml.hpp
|
||||
docker cp "${CID}:/src/include/toml11" include/
|
||||
|
||||
find include/libcron include/date include/toml11 -type f -print0 | xargs -0 sed -i 's/[[:blank:]]\+$//'
|
||||
sed -i 's/[[:blank:]]\+$//' include/toml.hpp
|
||||
fi
|
||||
30
scripts/ci/render-linux-launcher.sh
Normal file
30
scripts/ci/render-linux-launcher.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OUTPUT="${1:?output path is required}"
|
||||
MODE="${2:?mode is required}"
|
||||
ROOT_VALUE="${3:?root value is required}"
|
||||
CONFIG_DIR_VALUE="${4:?config dir value is required}"
|
||||
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEMPLATE="${SCRIPT_DIR}/../templates/linux-launcher.sh"
|
||||
|
||||
case "${MODE}" in
|
||||
portable|openwrt) ;;
|
||||
*)
|
||||
echo "Unsupported launcher mode: ${MODE}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -f "${TEMPLATE}" ]; then
|
||||
echo "Launcher template not found: ${TEMPLATE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed \
|
||||
-e "s|__CONFIG_MODE__|${MODE}|g" \
|
||||
-e "s|__ROOT__|${ROOT_VALUE}|g" \
|
||||
-e "s|__CONFIG_DIR__|${CONFIG_DIR_VALUE}|g" \
|
||||
"${TEMPLATE}" > "${OUTPUT}"
|
||||
chmod 0755 "${OUTPUT}"
|
||||
@@ -4,6 +4,8 @@ set -euo pipefail
|
||||
VERSION="${1:?version is required}"
|
||||
ARCH="${2:?arch is required}"
|
||||
PACKAGE_DIR="SubConverter-Extended"
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RENDER_LAUNCHER="${SCRIPT_DIR}/ci/render-linux-launcher.sh"
|
||||
|
||||
copy_dir_contents() {
|
||||
local source_dir="$1"
|
||||
@@ -33,47 +35,5 @@ fi
|
||||
copy_dir_contents runtime-libs
|
||||
copy_dir_contents runtime-root
|
||||
|
||||
cat > "${PACKAGE_DIR}/start.sh" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
CONF="${PREF_PATH:-$ROOT/base/pref.toml}"
|
||||
CONF_DIR="$(dirname "$CONF")"
|
||||
mkdir -p "$CONF_DIR"
|
||||
|
||||
if [ ! -f "$CONF" ] && [ -f "$ROOT/base/pref.example.toml" ]; then
|
||||
cp "$ROOT/base/pref.example.toml" "$CONF"
|
||||
fi
|
||||
|
||||
if [ -x "$ROOT/lib64/ld-linux-x86-64.so.2" ]; then
|
||||
LOADER="$ROOT/lib64/ld-linux-x86-64.so.2"
|
||||
LIB_PATH="$ROOT/lib/x86_64-linux-gnu:$ROOT/usr/lib/x86_64-linux-gnu:$ROOT/lib64:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" ]; then
|
||||
LOADER="$ROOT/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
|
||||
LIB_PATH="$ROOT/lib/x86_64-linux-gnu:$ROOT/usr/lib/x86_64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/ld-linux-aarch64.so.1" ]; then
|
||||
LOADER="$ROOT/lib/ld-linux-aarch64.so.1"
|
||||
LIB_PATH="$ROOT/lib/aarch64-linux-gnu:$ROOT/usr/lib/aarch64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1" ]; then
|
||||
LOADER="$ROOT/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1"
|
||||
LIB_PATH="$ROOT/lib/aarch64-linux-gnu:$ROOT/usr/lib/aarch64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/lib/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
else
|
||||
echo "glibc loader not found in package."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec "$LOADER" --library-path "$LIB_PATH" "$ROOT/subconverter" -f "$CONF"
|
||||
EOF
|
||||
|
||||
chmod +x "${PACKAGE_DIR}/start.sh"
|
||||
bash "${RENDER_LAUNCHER}" "${PACKAGE_DIR}/start.sh" portable "__PORTABLE_ROOT__" "__ROOT_BASE__"
|
||||
tar -czf "SubConverter-Extended-${VERSION}-linux-${ARCH}.tar.gz" "${PACKAGE_DIR}"
|
||||
|
||||
@@ -14,6 +14,8 @@ WORK_DIR="build/openwrt-apk/${LINUX_ARCH}"
|
||||
APK_VERSION="${VERSION#v}-r0"
|
||||
BUILD_TIME="${BUILD_TIME:-$(date +%s)}"
|
||||
REPO_COMMIT="${GITHUB_SHA:-${SHA:-unknown}}"
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RENDER_LAUNCHER="${SCRIPT_DIR}/ci/render-linux-launcher.sh"
|
||||
|
||||
if [ ! -d "${SOURCE_DIR}" ]; then
|
||||
echo "Package source directory not found: ${SOURCE_DIR}" >&2
|
||||
@@ -52,113 +54,7 @@ run_mkpkg() {
|
||||
|
||||
create_launcher() {
|
||||
local path="$1"
|
||||
cat > "${path}" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
ROOT="/opt/subconverter-extended"
|
||||
CONFIG_DIR="/etc/subconverter"
|
||||
|
||||
join_config_path() {
|
||||
case "$1" in
|
||||
/*) printf '%s\n' "$1" ;;
|
||||
*) printf '%s\n' "$CONFIG_DIR/$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
create_config() {
|
||||
target="$(join_config_path "$1")"
|
||||
target_dir="$(dirname "$target")"
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
case "$target" in
|
||||
*.yml|*.yaml) example="$ROOT/base/pref.example.yml" ;;
|
||||
*.ini) example="$ROOT/base/pref.example.ini" ;;
|
||||
*) example="$ROOT/base/pref.example.toml" ;;
|
||||
esac
|
||||
|
||||
if [ ! -f "$example" ]; then
|
||||
echo "Cannot create configuration file: $target" >&2
|
||||
echo "Missing example file: $example" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$example" "$target"
|
||||
chmod 0600 "$target"
|
||||
printf '%s\n' "$target"
|
||||
}
|
||||
|
||||
resolve_config() {
|
||||
if [ -n "${PREF_PATH:-}" ]; then
|
||||
conf="$(join_config_path "$PREF_PATH")"
|
||||
if [ ! -f "$conf" ]; then
|
||||
create_config "$conf"
|
||||
return
|
||||
fi
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
|
||||
for conf in "$CONFIG_DIR/pref.toml" "$CONFIG_DIR/pref.yml" "$CONFIG_DIR/pref.ini"; do
|
||||
if [ -f "$conf" ]; then
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
for conf in "$ROOT/base/pref.toml" "$ROOT/base/pref.yml" "$ROOT/base/pref.ini"; do
|
||||
if [ -f "$conf" ]; then
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
for pair in \
|
||||
"pref.example.toml:pref.toml" \
|
||||
"pref.example.yml:pref.yml" \
|
||||
"pref.example.ini:pref.ini"; do
|
||||
example="${pair%%:*}"
|
||||
target="${pair#*:}"
|
||||
if [ -f "$ROOT/base/$example" ]; then
|
||||
create_config "$target"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
echo "No configuration file found. Expected $CONFIG_DIR/pref.toml, pref.yml, or pref.ini." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ -x "$ROOT/lib64/ld-linux-x86-64.so.2" ]; then
|
||||
LOADER="$ROOT/lib64/ld-linux-x86-64.so.2"
|
||||
LIB_PATH="$ROOT/lib/x86_64-linux-gnu:$ROOT/usr/lib/x86_64-linux-gnu:$ROOT/lib64:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" ]; then
|
||||
LOADER="$ROOT/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
|
||||
LIB_PATH="$ROOT/lib/x86_64-linux-gnu:$ROOT/usr/lib/x86_64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/ld-linux-aarch64.so.1" ]; then
|
||||
LOADER="$ROOT/lib/ld-linux-aarch64.so.1"
|
||||
LIB_PATH="$ROOT/lib/aarch64-linux-gnu:$ROOT/usr/lib/aarch64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1" ]; then
|
||||
LOADER="$ROOT/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1"
|
||||
LIB_PATH="$ROOT/lib/aarch64-linux-gnu:$ROOT/usr/lib/aarch64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/lib/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
else
|
||||
echo "glibc loader not found in package." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONF="$(resolve_config)"
|
||||
exec "$LOADER" --library-path "$LIB_PATH" "$ROOT/subconverter" -f "$CONF"
|
||||
EOF
|
||||
chmod 0755 "${path}"
|
||||
bash "${RENDER_LAUNCHER}" "${path}" openwrt "/opt/subconverter-extended" "/etc/subconverter"
|
||||
}
|
||||
|
||||
create_init_script() {
|
||||
|
||||
145
scripts/templates/linux-launcher.sh
Normal file
145
scripts/templates/linux-launcher.sh
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
CONFIG_MODE="__CONFIG_MODE__"
|
||||
ROOT="__ROOT__"
|
||||
CONFIG_DIR="__CONFIG_DIR__"
|
||||
|
||||
if [ "$ROOT" = "__PORTABLE_ROOT__" ]; then
|
||||
ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
fi
|
||||
|
||||
if [ "$CONFIG_DIR" = "__ROOT_BASE__" ]; then
|
||||
CONFIG_DIR="$ROOT/base"
|
||||
fi
|
||||
|
||||
join_config_path() {
|
||||
case "$1" in
|
||||
/*) printf '%s\n' "$1" ;;
|
||||
*)
|
||||
if [ "$CONFIG_MODE" = "openwrt" ]; then
|
||||
printf '%s\n' "$CONFIG_DIR/$1"
|
||||
else
|
||||
printf '%s\n' "$ROOT/$1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
create_config() {
|
||||
target="$(join_config_path "$1")"
|
||||
target_dir="$(dirname "$target")"
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
case "$target" in
|
||||
*.yml|*.yaml) example="$ROOT/base/pref.example.yml" ;;
|
||||
*.ini) example="$ROOT/base/pref.example.ini" ;;
|
||||
*) example="$ROOT/base/pref.example.toml" ;;
|
||||
esac
|
||||
|
||||
if [ ! -f "$example" ]; then
|
||||
echo "Cannot create configuration file: $target" >&2
|
||||
echo "Missing example file: $example" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$example" "$target"
|
||||
if [ "$CONFIG_MODE" = "openwrt" ]; then
|
||||
chmod 0600 "$target"
|
||||
fi
|
||||
printf '%s\n' "$target"
|
||||
}
|
||||
|
||||
resolve_portable_config() {
|
||||
if [ -n "${PREF_PATH:-}" ]; then
|
||||
conf="$(join_config_path "$PREF_PATH")"
|
||||
if [ ! -f "$conf" ]; then
|
||||
create_config "$conf"
|
||||
return
|
||||
fi
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
|
||||
conf="$ROOT/base/pref.toml"
|
||||
if [ ! -f "$conf" ]; then
|
||||
create_config "$conf"
|
||||
return
|
||||
fi
|
||||
printf '%s\n' "$conf"
|
||||
}
|
||||
|
||||
resolve_openwrt_config() {
|
||||
if [ -n "${PREF_PATH:-}" ]; then
|
||||
conf="$(join_config_path "$PREF_PATH")"
|
||||
if [ ! -f "$conf" ]; then
|
||||
create_config "$conf"
|
||||
return
|
||||
fi
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
|
||||
for conf in "$CONFIG_DIR/pref.toml" "$CONFIG_DIR/pref.yml" "$CONFIG_DIR/pref.ini"; do
|
||||
if [ -f "$conf" ]; then
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
for conf in "$ROOT/base/pref.toml" "$ROOT/base/pref.yml" "$ROOT/base/pref.ini"; do
|
||||
if [ -f "$conf" ]; then
|
||||
printf '%s\n' "$conf"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
for pair in \
|
||||
"pref.example.toml:pref.toml" \
|
||||
"pref.example.yml:pref.yml" \
|
||||
"pref.example.ini:pref.ini"; do
|
||||
example="${pair%%:*}"
|
||||
target="${pair#*:}"
|
||||
if [ -f "$ROOT/base/$example" ]; then
|
||||
create_config "$target"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
echo "No configuration file found. Expected $CONFIG_DIR/pref.toml, pref.yml, or pref.ini." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ -x "$ROOT/lib64/ld-linux-x86-64.so.2" ]; then
|
||||
LOADER="$ROOT/lib64/ld-linux-x86-64.so.2"
|
||||
LIB_PATH="$ROOT/lib/x86_64-linux-gnu:$ROOT/usr/lib/x86_64-linux-gnu:$ROOT/lib64:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2" ]; then
|
||||
LOADER="$ROOT/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
|
||||
LIB_PATH="$ROOT/lib/x86_64-linux-gnu:$ROOT/usr/lib/x86_64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/ld-linux-aarch64.so.1" ]; then
|
||||
LOADER="$ROOT/lib/ld-linux-aarch64.so.1"
|
||||
LIB_PATH="$ROOT/lib/aarch64-linux-gnu:$ROOT/usr/lib/aarch64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1" ]; then
|
||||
LOADER="$ROOT/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1"
|
||||
LIB_PATH="$ROOT/lib/aarch64-linux-gnu:$ROOT/usr/lib/aarch64-linux-gnu:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/lib/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
elif [ -x "$ROOT/usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3" ]; then
|
||||
LOADER="$ROOT/usr/lib/arm-linux-gnueabihf/ld-linux-armhf.so.3"
|
||||
LIB_PATH="$ROOT/lib/arm-linux-gnueabihf:$ROOT/usr/lib/arm-linux-gnueabihf:$ROOT/usr/arm-linux-gnueabihf/lib:$ROOT/lib:$ROOT/usr/lib"
|
||||
else
|
||||
echo "glibc loader not found in package." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$CONFIG_MODE" = "openwrt" ]; then
|
||||
CONF="$(resolve_openwrt_config)"
|
||||
else
|
||||
CONF="$(resolve_portable_config)"
|
||||
fi
|
||||
|
||||
exec "$LOADER" --library-path "$LIB_PATH" "$ROOT/subconverter" -f "$CONF"
|
||||
Reference in New Issue
Block a user