build: 优化 Docker/Workflow 构建,使用 Next.js 环境变量注入版本和构建时间,并移除对 package.json 的硬编码替换

This commit is contained in:
ryan
2026-06-11 20:18:49 +08:00
parent 7c125ccef2
commit 50533e1837
11 changed files with 37 additions and 46 deletions
+2
View File
@@ -59,6 +59,7 @@ jobs:
fi
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "BUILD_DATE=$(date -u +'%Y-%m-%d %H:%M:%S')" >> "$GITHUB_ENV"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
@@ -80,6 +81,7 @@ jobs:
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
VERSION=${{ env.VERSION }}
BUILD_DATE=${{ env.BUILD_DATE }}
cache-from: type=gha,scope=docker-server-${{ matrix.arch }}
cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=docker-server-${{ matrix.arch }}
+3 -23
View File
@@ -103,30 +103,10 @@ jobs:
working-directory: ${{ env.FRONTEND_DIR }}
run: pnpm install --frozen-lockfile
- name: Stamp frontend package metadata
working-directory: ${{ env.FRONTEND_DIR }}
shell: bash
env:
VERSION: ${{ needs.create-release.outputs.version_without_v }}
BUILD_DATE: ${{ needs.create-release.outputs.build_date }}
run: |
set -euo pipefail
if [[ -f package.json ]]; then
node - <<'NODE'
const fs = require('fs');
const path = './package.json';
const pkg = JSON.parse(fs.readFileSync(path, 'utf8'));
if (process.env.VERSION && Object.hasOwn(pkg, 'version')) {
pkg.version = process.env.VERSION;
}
if (process.env.BUILD_DATE && Object.hasOwn(pkg, 'buildDate')) {
pkg.buildDate = process.env.BUILD_DATE;
}
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n');
NODE
fi
- name: Build frontend
env:
NEXT_PUBLIC_APP_VERSION: ${{ needs.create-release.outputs.version }}
NEXT_PUBLIC_APP_BUILD_DATE: ${{ needs.create-release.outputs.build_date }}
run: ${{ env.FRONTEND_BUILD_COMMAND }}
working-directory: ${{ env.FRONTEND_DIR }}
+1
View File
@@ -45,6 +45,7 @@ cross-build:
--file docker/Dockerfile.cross \
--target export \
--build-arg VERSION=$(or $(VERSION),dev) \
--build-arg BUILD_DATE="$(shell date -u +'%Y-%m-%d %H:%M:%S')" \
$(if $(GOOS),--build-arg TARGET_OS=$(GOOS)) \
$(if $(GOARCH),--build-arg TARGET_ARCH=$(GOARCH)) \
--output type=local,dest=./bin \
+9 -1
View File
@@ -6,10 +6,15 @@ ARG ALPINE_VERSION=3.23
FROM node:${NODE_VERSION}-alpine AS frontend-builder
ARG VERSION=dev
ARG BUILD_DATE=""
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV NODE_ENV=production
ENV NEXT_STANDALONE_EXPORT=true
ENV NEXT_PUBLIC_APP_VERSION=${VERSION}
ENV NEXT_PUBLIC_APP_BUILD_DATE=${BUILD_DATE}
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
@@ -19,10 +24,13 @@ COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY frontend/ ./
RUN pnpm build:embed
FROM golang:${GO_VERSION}-alpine AS backend-builder
ARG VERSION=dev
RUN apk add --no-cache ca-certificates git
WORKDIR /workspace
@@ -36,7 +44,7 @@ COPY --from=frontend-builder /workspace/frontend/out ./internal/router/dist
RUN CGO_ENABLED=0 GOOS=linux go build \
-tags embed_frontend \
-trimpath \
-ldflags="-s -w" \
-ldflags="-s -w -X github.com/Rain-kl/Wavelet/internal/cmd.Version=${VERSION}" \
-o /out/wavelet \
./main.go
+3 -1
View File
@@ -5,6 +5,8 @@ ARG ALPINE_VERSION=3.23
FROM golang:${GO_VERSION}-alpine AS builder
ARG VERSION=dev
RUN apk add --no-cache ca-certificates git
WORKDIR /workspace
@@ -16,7 +18,7 @@ COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-trimpath \
-ldflags="-s -w" \
-ldflags="-s -w -X github.com/Rain-kl/Wavelet/internal/cmd.Version=${VERSION}" \
-o /out/wavelet \
./main.go
+3 -10
View File
@@ -29,11 +29,14 @@ ARG ALPINE_VERSION=3.23
FROM node:${NODE_VERSION}-alpine AS frontend-builder
ARG VERSION=dev
ARG BUILD_DATE=""
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV NODE_ENV=production
ENV NEXT_STANDALONE_EXPORT=true
ENV NEXT_PUBLIC_APP_VERSION=${VERSION}
ENV NEXT_PUBLIC_APP_BUILD_DATE=${BUILD_DATE}
RUN corepack enable && corepack prepare pnpm@10.10.0 --activate
@@ -45,16 +48,6 @@ RUN pnpm install --frozen-lockfile
COPY frontend/ ./
# Stamp version into package.json when VERSION is not "dev"
RUN if [ "$VERSION" != "dev" ]; then \
node -e " \
const fs = require('fs'); \
const pkg = require('./package.json'); \
pkg.version = process.env.VERSION.replace(/^v/, ''); \
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n'); \
"; \
fi
RUN pnpm build:embed
# ── Stage 2: Cross-compile Go backend ──────────────────────────────────────
+3 -4
View File
@@ -20,6 +20,9 @@ ENV WAVELET_BACKEND_URL=https://build-placeholder-2.invalid
ARG VERSION=""
ARG BUILD_DATE=""
ENV NEXT_PUBLIC_APP_VERSION=${VERSION}
ENV NEXT_PUBLIC_APP_BUILD_DATE=${BUILD_DATE}
WORKDIR /app
COPY frontend/package.json frontend/pnpm-lock.yaml ./
@@ -27,10 +30,6 @@ RUN pnpm install --frozen-lockfile
COPY frontend/ ./
RUN if [ -n "$VERSION" ] || [ -n "$BUILD_DATE" ]; then \
VERSION="$VERSION" BUILD_DATE="$BUILD_DATE" node -e "const fs = require('fs'); const pkg = require('./package.json'); const version = process.env.VERSION; const buildDate = process.env.BUILD_DATE; if (version) pkg.version = version; if (buildDate) pkg.buildDate = buildDate; fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\\n')"; \
fi
RUN pnpm build
RUN grep -rl \
@@ -2,6 +2,7 @@
import {Info, Server} from "lucide-react"
import packageJson from "../../../package.json"
import {APP_VERSION, APP_BUILD_DATE} from "@/lib/app-info"
import {apiConfig} from "@/lib/services"
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card"
@@ -36,8 +37,8 @@ export function InfoTab({ systemConfigsLength, authSourcesLength }: InfoTabProps
</CardHeader>
<CardContent className="pt-4">
<InfoRow label="应用名称" value={packageJson.name} />
<InfoRow label="版本号" value={packageJson.version} />
<InfoRow label="构建时间" value={packageJson.buildDate} />
<InfoRow label="版本号" value={APP_VERSION} />
{APP_BUILD_DATE && <InfoRow label="构建时间" value={APP_BUILD_DATE} />}
<InfoRow label="Next.js" value={(packageJson as { dependencies?: Record<string, string> }).dependencies?.next} />
<InfoRow label="React" value={(packageJson as { dependencies?: Record<string, string> }).dependencies?.react} />
</CardContent>
+3 -3
View File
@@ -3,7 +3,7 @@
import * as React from "react"
import Link from "next/link"
import {usePathname, useRouter} from "next/navigation"
import packageJson from "../../package.json"
import {APP_VERSION, APP_BUILD_DATE} from "@/lib/app-info"
import {toast} from "sonner"
import {Button} from "@/components/ui/button"
import {Spinner} from "@/components/ui/spinner"
@@ -384,8 +384,8 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
</SidebarContent>
<SidebarFooter className="mt-auto px-3 py-3 group-data-[collapsible=icon]:hidden">
<div className="border-t border-border/60 pt-3 text-[11px] leading-5 text-muted-foreground">
<div>Version {packageJson.version}</div>
<div>Build At {packageJson.buildDate}</div>
<div>Version {APP_VERSION}</div>
{APP_BUILD_DATE && <div>Build At {APP_BUILD_DATE}</div>}
</div>
</SidebarFooter>
</Sidebar>
+6
View File
@@ -0,0 +1,6 @@
import packageJson from "../package.json"
const rawVersion = process.env.NEXT_PUBLIC_APP_VERSION || packageJson.version
export const APP_VERSION = rawVersion.startsWith('v') ? rawVersion.slice(1) : rawVersion
export const APP_BUILD_DATE = process.env.NEXT_PUBLIC_APP_BUILD_DATE || ""
+1 -2
View File
@@ -1,7 +1,6 @@
{
"name": "wavelet",
"version": "1.3.9",
"buildDate": "2026-04-22 16:00:00",
"version": "dev",
"private": true,
"scripts": {
"predev": "node scripts/generate-themes.js",