chore: skip AppleDouble migrations + Alpine mirror for CN builds

- migrations_runner: filter ._* / .hidden.sql so macOS metadata files don't
  break the migration scanner; new helper + regression tests cover this.
- Docker images switch Alpine repo to mirrors.aliyun.com (CN build speed)
  and pin frontend pnpm to 9.15.9 to dodge v10 interactive build prompts.
- dockerignore (root + backend): exclude **/.DS_Store and ._* AppleDouble
  artefacts so they don't sneak into image context.
This commit is contained in:
win 2026-05-20 12:15:25 +08:00
parent 35c6c2b097
commit 27f2b442cb
5 changed files with 50 additions and 7 deletions

View File

@ -20,6 +20,9 @@ docs/
# OS files
.DS_Store
**/.DS_Store
._*
**/._*
Thumbs.db
# Build artifacts

View File

@ -18,10 +18,13 @@ ARG GOSUMDB=sum.golang.google.cn
# -----------------------------------------------------------------------------
FROM ${NODE_IMAGE} AS frontend-builder
RUN sed -i 's#https://dl-cdn.alpinelinux.org/alpine#https://mirrors.aliyun.com/alpine#g' /etc/apk/repositories
WORKDIR /app/frontend
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Install pnpm. Keep this on v9 so Docker builds do not fail on pnpm v10's
# interactive build-script approval flow.
RUN corepack enable && corepack prepare pnpm@9.15.9 --activate
# Install dependencies first (better caching)
COPY frontend/package.json frontend/pnpm-lock.yaml ./
@ -36,6 +39,8 @@ RUN pnpm run build
# -----------------------------------------------------------------------------
FROM ${GOLANG_IMAGE} AS backend-builder
RUN sed -i 's#https://dl-cdn.alpinelinux.org/alpine#https://mirrors.aliyun.com/alpine#g' /etc/apk/repositories
# Build arguments for version info (set by CI)
ARG VERSION=
ARG COMMIT=docker
@ -83,6 +88,8 @@ FROM ${POSTGRES_IMAGE} AS pg-client
# -----------------------------------------------------------------------------
FROM ${ALPINE_IMAGE}
RUN sed -i 's#https://dl-cdn.alpinelinux.org/alpine#https://mirrors.aliyun.com/alpine#g' /etc/apk/repositories
# Labels
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
LABEL description="Sub2API - AI API Gateway Platform"

View File

@ -1,2 +1,5 @@
.cache/
.DS_Store
**/.DS_Store
._*
**/._*

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io/fs"
"path/filepath"
"sort"
"strings"
"time"
@ -144,11 +145,10 @@ func applyMigrationsFS(ctx context.Context, db *sql.DB, fsys fs.FS) error {
// 获取所有 .sql 迁移文件并按文件名排序。
// 命名规范:使用零填充数字前缀(如 001_init.sql, 002_add_users.sql
files, err := fs.Glob(fsys, "*.sql")
files, err := migrationFiles(fsys)
if err != nil {
return fmt.Errorf("list migrations: %w", err)
}
sort.Strings(files) // 确保按文件名顺序执行迁移
for _, name := range files {
// 读取迁移文件内容
@ -392,14 +392,13 @@ func tableExists(ctx context.Context, db *sql.DB, tableName string) (bool, error
}
func latestMigrationBaseline(fsys fs.FS) (string, string, string, error) {
files, err := fs.Glob(fsys, "*.sql")
files, err := migrationFiles(fsys)
if err != nil {
return "", "", "", err
}
if len(files) == 0 {
return "baseline", "baseline", "", nil
}
sort.Strings(files)
name := files[len(files)-1]
contentBytes, err := fs.ReadFile(fsys, name)
if err != nil {
@ -412,6 +411,23 @@ func latestMigrationBaseline(fsys fs.FS) (string, string, string, error) {
return version, version, hash, nil
}
func migrationFiles(fsys fs.FS) ([]string, error) {
files, err := fs.Glob(fsys, "*.sql")
if err != nil {
return nil, err
}
out := files[:0]
for _, name := range files {
base := filepath.Base(name)
if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "._") {
continue
}
out = append(out, name)
}
sort.Strings(out)
return out, nil
}
func checksumSet(values ...string) map[string]struct{} {
out := make(map[string]struct{}, len(values))
for _, value := range values {

View File

@ -47,7 +47,8 @@ func TestLatestMigrationBaseline(t *testing.T) {
t.Run("uses_latest_sorted_sql_file", func(t *testing.T) {
fsys := fstest.MapFS{
"001_init.sql": &fstest.MapFile{Data: []byte("CREATE TABLE t1(id int);")},
"._999_final.sql": &fstest.MapFile{Data: []byte("AppleDouble metadata")},
"001_init.sql": &fstest.MapFile{Data: []byte("CREATE TABLE t1(id int);")},
"010_final.sql": &fstest.MapFile{
Data: []byte("CREATE TABLE t2(id int);"),
},
@ -68,6 +69,19 @@ func TestLatestMigrationBaseline(t *testing.T) {
})
}
func TestMigrationFiles_SkipsHiddenAppleDoubleSQL(t *testing.T) {
fsys := fstest.MapFS{
"._001_init.sql": &fstest.MapFile{Data: []byte("AppleDouble metadata")},
".hidden.sql": &fstest.MapFile{Data: []byte("hidden")},
"001_init.sql": &fstest.MapFile{Data: []byte("SELECT 1;")},
"002_next.sql": &fstest.MapFile{Data: []byte("SELECT 2;")},
}
files, err := migrationFiles(fsys)
require.NoError(t, err)
require.Equal(t, []string{"001_init.sql", "002_next.sql"}, files)
}
func TestIsMigrationChecksumCompatible_AdditionalCases(t *testing.T) {
require.False(t, isMigrationChecksumCompatible("unknown.sql", "db", "file"))