diff --git a/.dockerignore b/.dockerignore index 7b13414..c9ebe8c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,6 +10,8 @@ main tmp .sync* *.tar.gz +frontend/node_modules +frontend/.vite # local env files .env.local diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 93bc964..12cc9a8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -7,7 +7,7 @@ on: jobs: frontend-build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout repository uses: actions/checkout@v6.0.2 @@ -16,7 +16,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 24 - name: Install dependencies and build frontend run: | cd frontend @@ -30,59 +30,130 @@ jobs: build: needs: frontend-build - runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: + - { platform: linux/amd64 } + - { platform: linux/386 } + - { platform: linux/arm64/v8 } + - { platform: linux/arm/v7 } + - { platform: linux/arm/v6 } + runs-on: ubuntu-24.04 steps: - - name: Checkout repository - uses: actions/checkout@v6.0.2 - - name: Download frontend build artifact - uses: actions/download-artifact@v8 - with: - name: frontend-dist - path: frontend_dist - - name: Docker meta - id: meta - uses: docker/metadata-action@v6 - with: - images: | - alireza7/s-ui - ghcr.io/alireza0/s-ui - tags: | - type=ref,event=branch - type=ref,event=tag - type=pep440,pattern={{version}} - - name: Set up QEMU - uses: docker/setup-qemu-action@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - with: - install: true - buildkitd-flags: --debug - - name: Cache Docker layers - uses: actions/cache@v5 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- - - name: Login to Docker Hub - uses: docker/login-action@v4 - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_TOKEN }} - - name: Login to GHCR - uses: docker/login-action@v4 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v7 - with: - context: . - file: Dockerfile.frontend-artifact - push: true - platforms: linux/amd64, linux/386, linux/arm64/v8, linux/arm/v7, linux/arm/v6 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=local,src=/tmp/.buildx-cache - cache-to: type=local,dest=/tmp/.buildx-cache,mode=max \ No newline at end of file + - name: Checkout repository + uses: actions/checkout@v6.0.2 + - name: Download frontend build artifact + uses: actions/download-artifact@v8 + with: + name: frontend-dist + path: frontend_dist + - name: Prepare + run: | + platform="${{ matrix.platform }}" + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + - name: Docker meta + id: meta + uses: docker/metadata-action@v6 + with: + images: | + alireza7/s-ui + ghcr.io/alireza0/s-ui + tags: | + type=ref,event=branch + type=ref,event=tag + type=pep440,pattern={{version}} + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Cache Docker layers + uses: actions/cache@v5 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ matrix.platform }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-${{ matrix.platform }}- + - name: Login to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + - name: Login to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push by digest + id: build + uses: docker/build-push-action@v7 + with: + context: . + file: Dockerfile.frontend-artifact + platforms: ${{ matrix.platform }} + labels: ${{ steps.meta.outputs.labels }} + tags: | + alireza7/s-ui + ghcr.io/alireza0/s-ui + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache,mode=max + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + echo "${digest#sha256:}" > "${{ runner.temp }}/digests/${digest#sha256:}" + - name: Upload digest + uses: actions/upload-artifact@v7 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + needs: build + runs-on: ubuntu-24.04 + steps: + - name: Download digests + uses: actions/download-artifact@v8 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + - name: Login to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + - name: Login to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Docker meta + id: meta + uses: docker/metadata-action@v6 + with: + images: | + alireza7/s-ui + ghcr.io/alireza0/s-ui + tags: | + type=ref,event=branch + type=ref,event=tag + type=pep440,pattern={{version}} + - name: Create manifest list and push + env: + DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }} + working-directory: ${{ runner.temp }}/digests + run: | + set -e + for img in alireza7/s-ui ghcr.io/alireza0/s-ui; do + TAGS_ARGS=$(echo "$DOCKER_METADATA_OUTPUT_JSON" | jq -cr --arg img "$img" '.tags | map(select(startswith($img))) | map("-t " + .) | join(" ")') + DIGEST_REFS=$(for f in *; do echo -n "${img}@sha256:$(cat "$f") "; done) + docker buildx imagetools create $TAGS_ARGS $DIGEST_REFS + done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83e1fc6..4ec61a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,10 +20,10 @@ on: env: NODE_VERSION: "24" - CRONET_GO_VERSION: "17c7ef18afa63b205e835c6270277b29382eb8e3" + CRONET_GO_VERSION: "cba7b9ac0399055aa49fbdc57c03c374f58e1597" CRONET_GO_REPO: https://github.com/sagernet/cronet-go.git BOOTLIN_BASE_URL: https://toolchains.bootlin.com/downloads/releases/toolchains - CHROMIUM_CACHE_KEY_SUFFIX: musl-17c7ef18afa6 + CHROMIUM_CACHE_KEY_SUFFIX: musl-cba7b9ac0399055aa49fbdc57c03c374f58e1597 jobs: build-frontend: diff --git a/Dockerfile b/Dockerfile index fbdf71a..c22447a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ RUN npm install && npm run build FROM golang:1.25-alpine AS backend-builder WORKDIR /app ARG TARGETARCH +ARG TARGETVARIANT ENV CGO_ENABLED=1 ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE" ENV GOARCH=$TARGETARCH @@ -18,22 +19,30 @@ RUN apk update && apk add --no-cache \ git \ wget \ unzip \ - bash + bash \ + curl ENV CC=gcc +RUN CRONET_ARCH="$TARGETARCH" && \ + CRONET_URL="https://github.com/SagerNet/cronet-go/releases/latest/download/libcronet-linux-${CRONET_ARCH}.so"; \ + echo "Downloading $CRONET_URL" && \ + wget -q -O ./libcronet.so "$CRONET_URL" && \ + chmod 755 ./libcronet.so + COPY . . COPY --from=front-builder /app/dist/ /app/web/html/ -RUN go build -ldflags="-w -s" \ - -tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor,with_naive_outbound,with_musl" \ +RUN if [ "$TARGETARCH" = "arm" ]; then export GOARM=7; [ "$TARGETVARIANT" = "v6" ] && export GOARM=6; fi; \ + go build -ldflags="-w -s" \ + -tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor,with_naive_outbound,with_purego" \ -o sui main.go -FROM --platform=$TARGETPLATFORM alpine +FROM alpine LABEL org.opencontainers.image.authors="alireza7@gmail.com" ENV TZ=Asia/Tehran WORKDIR /app RUN set -ex && apk add --no-cache --upgrade bash tzdata ca-certificates nftables -COPY --from=backend-builder /app/sui /app/ +COPY --from=backend-builder /app/sui /app/libcronet.so /app/ COPY entrypoint.sh /app/ ENTRYPOINT [ "./entrypoint.sh" ] \ No newline at end of file diff --git a/Dockerfile.frontend-artifact b/Dockerfile.frontend-artifact index a74b5eb..90f4cf1 100644 --- a/Dockerfile.frontend-artifact +++ b/Dockerfile.frontend-artifact @@ -1,6 +1,7 @@ FROM golang:1.25-alpine AS backend-builder WORKDIR /app ARG TARGETARCH +ARG TARGETVARIANT ENV CGO_ENABLED=1 ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE" ENV GOARCH=$TARGETARCH @@ -13,23 +14,30 @@ RUN apk update && apk add --no-cache \ git \ wget \ unzip \ - bash + bash \ + curl ENV CC=gcc +RUN CRONET_ARCH="$TARGETARCH" && \ + CRONET_URL="https://github.com/SagerNet/cronet-go/releases/latest/download/libcronet-linux-${CRONET_ARCH}.so"; \ + echo "Downloading $CRONET_URL" && \ + wget -q -O ./libcronet.so "$CRONET_URL" && \ + chmod 755 ./libcronet.so + COPY . . -# Copy pre-built frontend files from a known location (provided by workflow artifact) COPY frontend_dist/ /app/web/html/ -RUN go build -ldflags="-w -s" \ - -tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor,with_naive_outbound,with_musl" \ +RUN if [ "$TARGETARCH" = "arm" ]; then export GOARM=7; [ "$TARGETVARIANT" = "v6" ] && export GOARM=6; fi; \ + go build -ldflags="-w -s" \ + -tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor,with_naive_outbound,with_purego" \ -o sui main.go -FROM --platform=$TARGETPLATFORM alpine +FROM alpine LABEL org.opencontainers.image.authors="alireza7@gmail.com" ENV TZ=Asia/Tehran WORKDIR /app RUN set -ex && apk add --no-cache --upgrade bash tzdata ca-certificates nftables -COPY --from=backend-builder /app/sui /app/ +COPY --from=backend-builder /app/sui /app/libcronet.so /app/ COPY entrypoint.sh /app/ ENTRYPOINT [ "./entrypoint.sh" ] \ No newline at end of file diff --git a/build.sh b/build.sh index 8e3c8c0..2d8d0bf 100755 --- a/build.sh +++ b/build.sh @@ -11,4 +11,5 @@ mkdir -p web/html rm -fr web/html/* cp -R frontend/dist/* web/html/ -go build -ldflags "-w -s" -tags "with_quic,with_grpc,with_utls,with_acme,with_gvisor" -o sui main.go +BUILD_TAGS="with_quic,with_grpc,with_utls,with_acme,with_gvisor,with_naive_outbound,with_musl,badlinkname,tfogo_checklinkname0" +go build -ldflags '-w -s -checklinkname=0 -extldflags "-Wl,-no_warn_duplicate_libraries"' -tags "$BUILD_TAGS" -o sui main.go diff --git a/config/version b/config/version index 7169d35..e21e727 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -1.3.11 \ No newline at end of file +1.4.0 \ No newline at end of file diff --git a/docker-build-test.sh b/docker-build-test.sh new file mode 100755 index 0000000..f2df316 --- /dev/null +++ b/docker-build-test.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Test Docker multi-platform build (linux/amd64, 386, arm64, arm/v7, arm/v6) +# Requires: frontend_dist/ (run from repo root after building frontend) + +set -e +cd "$(dirname "$0")/.." + +echo "==> Preparing frontend_dist..." +if [ ! -d "frontend_dist" ] || [ -z "$(ls -A frontend_dist 2>/dev/null)" ]; then + echo "Building frontend..." + (cd frontend && npm install --prefer-offline --no-audit && npm run build) + rm -rf frontend_dist + mkdir -p frontend_dist + cp -R frontend/dist/* frontend_dist/ + echo "frontend_dist ready." +else + echo "frontend_dist exists, skipping frontend build." +fi + +PLATFORMS="linux/amd64,linux/386,linux/arm64/v8,linux/arm/v7,linux/arm/v6" +echo "==> Testing Docker build for: $PLATFORMS" +docker buildx build \ + --platform "$PLATFORMS" \ + -f Dockerfile.frontend-artifact \ + --build-arg CRONET_RELEASE=latest \ + --progress=plain \ + . 2>&1 | tee docker-build-test.log + +echo "==> Done. Check docker-build-test.log for full output." diff --git a/frontend b/frontend index 2d5a20f..1a62b65 160000 --- a/frontend +++ b/frontend @@ -1 +1 @@ -Subproject commit 2d5a20f967b98dec4d8c5619de2847dcfd7c4352 +Subproject commit 1a62b655138c13b77a20da939233cb22b642a511