From 66adf444ba7dea09a944a9d4f5bb4dc966d393bb Mon Sep 17 00:00:00 2001 From: nianzhibai Date: Sun, 31 May 2026 09:54:56 +0800 Subject: [PATCH] fix: detect Docker image version for update checks --- .github/workflows/docker-build.yml | 17 +++++++++-- Dockerfile | 2 +- backend/cmd/server/main.go | 1 + backend/internal/api/admin.go | 7 +++++ backend/internal/api/admin_test.go | 48 ++++++++++++++++++++++++++++++ docker-entrypoint.sh | 2 +- 6 files changed, 73 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index c52665d..7cb4ea0 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -26,6 +26,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -52,7 +54,18 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix=sha- - type=raw,value=latest,enable={{is_default_branch}} + type=raw,value=latest,enable=${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) }} + + - name: Determine image version + id: version + shell: bash + run: | + if [[ "$GITHUB_REF" == refs/tags/v* ]]; then + version="$GITHUB_REF_NAME" + else + version="$(git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short=12 HEAD)" + fi + echo "version=$version" >> "$GITHUB_OUTPUT" - name: Build and push Docker image uses: docker/build-push-action@v6 @@ -63,6 +76,6 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | - VERSION=${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || '' }} + VERSION=${{ steps.version.outputs.version }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index aa4fab9..7d3a2c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,7 @@ COPY backend/config.example.yaml ./config.example.yaml COPY 91VideoSpider/ ./91VideoSpider/ COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh -ARG VERSION= +ARG VERSION=dev ENV VIDEO_CONFIG=/opt/video-site-91/data/config.yaml \ VIDEO_FRONTEND_DIR=/opt/video-site-91/dist \ diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index d4e32b0..a9c8713 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -125,6 +125,7 @@ func main() { Catalog: cat, Auth: authr, VersionFilePath: versionFilePath, + ImageVersion: strings.TrimSpace(os.Getenv("VIDEO_IMAGE_VERSION")), GitHubRepo: githubRepo, SetupRequired: func() bool { setupMu.Lock() diff --git a/backend/internal/api/admin.go b/backend/internal/api/admin.go index ae068bd..bc5d2a7 100644 --- a/backend/internal/api/admin.go +++ b/backend/internal/api/admin.go @@ -23,6 +23,10 @@ type AdminServer struct { Auth *auth.Authenticator // VersionFilePath points to the installer-written .version file. VersionFilePath string + // ImageVersion is the Docker image version injected at build/runtime. + // It takes precedence over VersionFilePath because Docker data volumes can + // keep an older .version file across image upgrades. + ImageVersion string // GitHubRepo is the owner/name repo used for update checks. GitHubRepo string // ReleaseAPIURL and HTTPClient are injectable for tests. Production code leaves them empty. @@ -279,6 +283,9 @@ func (a *AdminServer) checkUpdate(ctx context.Context) (updateCheckDTO, error) { } func (a *AdminServer) installedVersion() string { + if version := strings.TrimSpace(a.ImageVersion); version != "" { + return version + } path := strings.TrimSpace(a.VersionFilePath) if path == "" { path = ".version" diff --git a/backend/internal/api/admin_test.go b/backend/internal/api/admin_test.go index 858e018..8203f73 100644 --- a/backend/internal/api/admin_test.go +++ b/backend/internal/api/admin_test.go @@ -194,6 +194,54 @@ func TestHandleCheckUpdateReportsUpToDate(t *testing.T) { } } +func TestHandleCheckUpdateUsesDockerImageVersion(t *testing.T) { + releaseServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, map[string]any{ + "tag_name": "v0.2.0", + "html_url": "https://github.com/nianzhibai/91/releases/tag/v0.2.0", + }) + })) + t.Cleanup(releaseServer.Close) + + req := httptest.NewRequest(http.MethodGet, "/admin/api/update/check", nil) + rr := httptest.NewRecorder() + (&AdminServer{ + ImageVersion: "v0.1.0", + ReleaseAPIURL: releaseServer.URL, + }).handleCheckUpdate(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, body = %s", rr.Code, rr.Body.String()) + } + var got updateCheckDTO + if err := json.NewDecoder(rr.Body).Decode(&got); err != nil { + t.Fatalf("decode: %v", err) + } + if got.CurrentVersion != "v0.1.0" { + t.Fatalf("currentVersion = %q, want v0.1.0", got.CurrentVersion) + } + if !got.HasUpdate { + t.Fatalf("hasUpdate = false, want true") + } +} + +func TestInstalledVersionPrefersDockerImageVersionOverVersionFile(t *testing.T) { + dir := t.TempDir() + versionFile := filepath.Join(dir, ".version") + if err := os.WriteFile(versionFile, []byte("v0.1.0\n"), 0o644); err != nil { + t.Fatalf("write version file: %v", err) + } + + got := (&AdminServer{ + VersionFilePath: versionFile, + ImageVersion: "v0.2.0", + }).installedVersion() + + if got != "v0.2.0" { + t.Fatalf("installedVersion = %q, want v0.2.0", got) + } +} + func TestHandleUpsertDrivePreservesExistingCredentialsWhenRequestCredentialsEmpty(t *testing.T) { ctx := context.Background() cat, err := catalog.Open(t.TempDir() + "/catalog.db") diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index f7a04ed..6456121 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -30,7 +30,7 @@ else echo "[entrypoint] using existing $CONFIG" fi -if [ -n "${VIDEO_VERSION_FILE:-}" ] && [ -n "${VIDEO_IMAGE_VERSION:-}" ] && [ ! -f "$VIDEO_VERSION_FILE" ]; then +if [ -n "${VIDEO_VERSION_FILE:-}" ] && [ -n "${VIDEO_IMAGE_VERSION:-}" ]; then mkdir -p "$(dirname "$VIDEO_VERSION_FILE")" printf '%s\n' "$VIDEO_IMAGE_VERSION" > "$VIDEO_VERSION_FILE" fi