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:
parent
35c6c2b097
commit
27f2b442cb
@ -20,6 +20,9 @@ docs/
|
|||||||
|
|
||||||
# OS files
|
# OS files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
**/.DS_Store
|
||||||
|
._*
|
||||||
|
**/._*
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
|
|||||||
11
Dockerfile
11
Dockerfile
@ -18,10 +18,13 @@ ARG GOSUMDB=sum.golang.google.cn
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
FROM ${NODE_IMAGE} AS frontend-builder
|
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
|
WORKDIR /app/frontend
|
||||||
|
|
||||||
# Install pnpm
|
# Install pnpm. Keep this on v9 so Docker builds do not fail on pnpm v10's
|
||||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
# interactive build-script approval flow.
|
||||||
|
RUN corepack enable && corepack prepare pnpm@9.15.9 --activate
|
||||||
|
|
||||||
# Install dependencies first (better caching)
|
# Install dependencies first (better caching)
|
||||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||||
@ -36,6 +39,8 @@ RUN pnpm run build
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
FROM ${GOLANG_IMAGE} AS backend-builder
|
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)
|
# Build arguments for version info (set by CI)
|
||||||
ARG VERSION=
|
ARG VERSION=
|
||||||
ARG COMMIT=docker
|
ARG COMMIT=docker
|
||||||
@ -83,6 +88,8 @@ FROM ${POSTGRES_IMAGE} AS pg-client
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
FROM ${ALPINE_IMAGE}
|
FROM ${ALPINE_IMAGE}
|
||||||
|
|
||||||
|
RUN sed -i 's#https://dl-cdn.alpinelinux.org/alpine#https://mirrors.aliyun.com/alpine#g' /etc/apk/repositories
|
||||||
|
|
||||||
# Labels
|
# Labels
|
||||||
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
|
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
|
||||||
LABEL description="Sub2API - AI API Gateway Platform"
|
LABEL description="Sub2API - AI API Gateway Platform"
|
||||||
|
|||||||
@ -1,2 +1,5 @@
|
|||||||
.cache/
|
.cache/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
**/.DS_Store
|
||||||
|
._*
|
||||||
|
**/._*
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -144,11 +145,10 @@ func applyMigrationsFS(ctx context.Context, db *sql.DB, fsys fs.FS) error {
|
|||||||
|
|
||||||
// 获取所有 .sql 迁移文件并按文件名排序。
|
// 获取所有 .sql 迁移文件并按文件名排序。
|
||||||
// 命名规范:使用零填充数字前缀(如 001_init.sql, 002_add_users.sql)。
|
// 命名规范:使用零填充数字前缀(如 001_init.sql, 002_add_users.sql)。
|
||||||
files, err := fs.Glob(fsys, "*.sql")
|
files, err := migrationFiles(fsys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("list migrations: %w", err)
|
return fmt.Errorf("list migrations: %w", err)
|
||||||
}
|
}
|
||||||
sort.Strings(files) // 确保按文件名顺序执行迁移
|
|
||||||
|
|
||||||
for _, name := range 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) {
|
func latestMigrationBaseline(fsys fs.FS) (string, string, string, error) {
|
||||||
files, err := fs.Glob(fsys, "*.sql")
|
files, err := migrationFiles(fsys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return "", "", "", err
|
||||||
}
|
}
|
||||||
if len(files) == 0 {
|
if len(files) == 0 {
|
||||||
return "baseline", "baseline", "", nil
|
return "baseline", "baseline", "", nil
|
||||||
}
|
}
|
||||||
sort.Strings(files)
|
|
||||||
name := files[len(files)-1]
|
name := files[len(files)-1]
|
||||||
contentBytes, err := fs.ReadFile(fsys, name)
|
contentBytes, err := fs.ReadFile(fsys, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -412,6 +411,23 @@ func latestMigrationBaseline(fsys fs.FS) (string, string, string, error) {
|
|||||||
return version, version, hash, nil
|
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{} {
|
func checksumSet(values ...string) map[string]struct{} {
|
||||||
out := make(map[string]struct{}, len(values))
|
out := make(map[string]struct{}, len(values))
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
|
|||||||
@ -47,7 +47,8 @@ func TestLatestMigrationBaseline(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("uses_latest_sorted_sql_file", func(t *testing.T) {
|
t.Run("uses_latest_sorted_sql_file", func(t *testing.T) {
|
||||||
fsys := fstest.MapFS{
|
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{
|
"010_final.sql": &fstest.MapFile{
|
||||||
Data: []byte("CREATE TABLE t2(id int);"),
|
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) {
|
func TestIsMigrationChecksumCompatible_AdditionalCases(t *testing.T) {
|
||||||
require.False(t, isMigrationChecksumCompatible("unknown.sql", "db", "file"))
|
require.False(t, isMigrationChecksumCompatible("unknown.sql", "db", "file"))
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user