Skip to content

Commit 3e60b38

Browse files
committed
feat(builder): integrate GitHub repository synchronization plugin and update configuration
- Added `githubRepoSyncPlugin` to the builder configuration, enabling synchronization with a GitHub repository. - Removed deprecated repository settings from user configuration and streamlined the plugin's integration. - Updated CLI and documentation to reflect changes in repository configuration handling, enhancing clarity for users. Signed-off-by: Innei <tukon479@gmail.com>
1 parent 6a4f868 commit 3e60b38

File tree

7 files changed

+122
-79
lines changed

7 files changed

+122
-79
lines changed

builder.config.default.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import os from 'node:os'
22

3-
import { defineBuilderConfig } from '@afilmory/builder'
3+
import { defineBuilderConfig, githubRepoSyncPlugin } from '@afilmory/builder'
44

55
import { env } from './env.js'
66

77
export default defineBuilderConfig(() => ({
8-
repo: {
9-
enable: false,
10-
url: process.env.BUILDER_REPO_URL ?? '',
11-
token: env.GIT_TOKEN,
12-
},
138
storage: {
149
// "provider": "local",
1510
// "basePath": "./apps/web/public/photos",
@@ -61,5 +56,14 @@ export default defineBuilderConfig(() => ({
6156
},
6257
},
6358
// plugins: [thumbnailStoragePlugin()],
64-
plugins: [],
59+
plugins: [
60+
githubRepoSyncPlugin({
61+
repo: {
62+
enable: false,
63+
url: process.env.BUILDER_REPO_URL ?? '',
64+
token: env.GIT_TOKEN,
65+
branch: process.env.BUILDER_REPO_BRANCH ?? 'main',
66+
},
67+
}),
68+
],
6569
}))

packages/builder/src/builder/builder.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,10 +658,6 @@ export class AfilmoryBuilder {
658658
addReference(ref)
659659
}
660660

661-
if (this.getUserSettings().repo?.enable && !hasPluginWithName('afilmory:github-repo-sync')) {
662-
addReference(() => import('@afilmory/builder/plugins/github-repo-sync.js'))
663-
}
664-
665661
return references
666662
}
667663

packages/builder/src/cli.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ async function main() {
5959
在 builder.config.ts 中设置 performance.worker.useClusterMode = true
6060
可启用多进程集群模式,发挥多核心优势。
6161
62-
远程仓库:
63-
如果启用了远程仓库 (repo.enable = true),构建完成后会自动推送更新。
64-
需要配置 repo.token 或设置 GIT_TOKEN 环境变量以提供推送权限。
65-
如果没有提供 token,将跳过推送步骤。
6662
`)
6763
return
6864
}
@@ -105,16 +101,9 @@ async function main() {
105101
logger.main.info(` 集群模式:${config.system.observability.performance.worker.useClusterMode ? '启用' : '禁用'}`)
106102
logger.main.info('')
107103
if (!userConfig) {
108-
logger.main.warn('未配置用户级设置(repo/storage)')
104+
logger.main.warn('未配置用户级存储设置')
109105
return
110106
}
111-
112-
logger.main.info('📦 远程仓库配置:')
113-
logger.main.info(` 启用状态:${userConfig.repo.enable ? '启用' : '禁用'}`)
114-
if (userConfig.repo.enable) {
115-
logger.main.info(` 仓库地址:${userConfig.repo.url || '未设置'}`)
116-
logger.main.info(` 推送权限:${userConfig.repo.token ? '已配置' : '未配置'}`)
117-
}
118107
return
119108
}
120109

packages/builder/src/config/index.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ function applySystemOverrides(target: BuilderConfig['system'], overrides?: Build
5656
function ensureUserSettings(target: BuilderConfig): UserBuilderSettings {
5757
if (!target.user) {
5858
target.user = {
59-
repo: {
60-
enable: false,
61-
url: '',
62-
token: '',
63-
},
6459
storage: null,
6560
}
6661
}
@@ -71,12 +66,6 @@ function applyUserOverrides(target: BuilderConfig, overrides?: BuilderConfigInpu
7166
if (!overrides) return
7267
const user = ensureUserSettings(target)
7368

74-
if (overrides.repo) {
75-
user.repo = {
76-
...user.repo,
77-
...overrides.repo,
78-
}
79-
}
8069
if (overrides.storage !== undefined) {
8170
user.storage = overrides.storage as StorageConfig | null
8271
}
@@ -88,14 +77,6 @@ function normalizeBuilderConfig(defaults: BuilderConfig, input: BuilderConfigInp
8877
applySystemOverrides(next.system, input.system)
8978
applyUserOverrides(next, input.user)
9079

91-
if (input.repo) {
92-
const user = ensureUserSettings(next)
93-
user.repo = {
94-
...user.repo,
95-
...input.repo,
96-
}
97-
}
98-
9980
if (input.storage !== undefined) {
10081
ensureUserSettings(next).storage = input.storage ?? null
10182
}

packages/builder/src/plugins/github-repo-sync.ts

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,35 @@ import type { BuilderPlugin } from './types.js'
1010
const RUN_SHARED_ASSETS_DIR = 'assetsGitDir'
1111

1212
export interface GitHubRepoSyncPluginOptions {
13+
repo: {
14+
enable: boolean
15+
url: string
16+
token?: string
17+
branch?: string
18+
}
1319
autoPush?: boolean
1420
}
1521

16-
export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOptions = {}): BuilderPlugin {
22+
export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOptions): BuilderPlugin {
1723
const autoPush = options.autoPush ?? true
24+
const repoConfig = options.repo
25+
26+
if (!repoConfig) {
27+
throw new Error('githubRepoSyncPlugin 需要 repo 配置')
28+
}
29+
30+
const branchName = repoConfig.branch?.trim() || 'main'
1831

1932
return {
2033
name: 'afilmory:github-repo-sync',
2134
hooks: {
2235
beforeBuild: async (context) => {
23-
const userConfig = context.config.user
24-
if (!userConfig) {
25-
context.logger.main.warn('⚠️ 未配置用户级设置,跳过远程仓库同步')
26-
return
27-
}
28-
29-
if (!userConfig.repo.enable) {
36+
if (!repoConfig.enable) {
3037
return
3138
}
3239

3340
const { logger } = context
34-
const { repo } = userConfig
35-
36-
if (!repo.url) {
41+
if (!repoConfig.url) {
3742
logger.main.warn('⚠️ 未配置远程仓库地址,跳过同步')
3843
return
3944
}
@@ -43,7 +48,7 @@ export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOption
4348

4449
logger.main.info('🔄 同步远程仓库...')
4550

46-
const repoUrl = buildAuthenticatedRepoUrl(repo.url, repo.token)
51+
const repoUrl = buildAuthenticatedRepoUrl(repoConfig.url, repoConfig.token)
4752

4853
if (!existsSync(assetsGitDir)) {
4954
logger.main.info('📥 克隆远程仓库...')
@@ -67,17 +72,12 @@ export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOption
6772
}
6873
}
6974

75+
await ensureRepositoryBranch({ assetsGitDir, branchName, logger })
7076
await prepareRepositoryLayout({ assetsGitDir, logger })
7177
logger.main.success('✅ 远程仓库同步完成')
7278
},
7379
afterBuild: async (context) => {
74-
const userConfig = context.config.user
75-
if (!userConfig) {
76-
context.logger.main.warn('⚠️ 未配置用户级设置,跳过推送')
77-
return
78-
}
79-
80-
if (!autoPush || !userConfig.repo.enable) {
80+
if (!autoPush || !repoConfig.enable) {
8181
return
8282
}
8383

@@ -97,7 +97,8 @@ export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOption
9797
await pushUpdatesToRemoteRepo({
9898
assetsGitDir,
9999
logger: context.logger,
100-
repoConfig: userConfig.repo,
100+
repoConfig,
101+
branchName,
101102
})
102103
},
103104
},
@@ -152,9 +153,15 @@ interface PushRemoteOptions {
152153
url: string
153154
token?: string
154155
}
156+
branchName: string
155157
}
156158

157-
async function pushUpdatesToRemoteRepo({ assetsGitDir, logger, repoConfig }: PushRemoteOptions): Promise<void> {
159+
async function pushUpdatesToRemoteRepo({
160+
assetsGitDir,
161+
logger,
162+
repoConfig,
163+
branchName,
164+
}: PushRemoteOptions): Promise<void> {
158165
if (!repoConfig.url) {
159166
return
160167
}
@@ -194,7 +201,7 @@ async function pushUpdatesToRemoteRepo({ assetsGitDir, logger, repoConfig }: Pus
194201
cwd: assetsGitDir,
195202
stdio: 'inherit',
196203
})`git commit -m ${commitMessage}`
197-
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git push origin HEAD`
204+
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git push -u origin HEAD:${branchName}`
198205

199206
logger.main.success('✅ 成功推送更新到远程仓库')
200207
}
@@ -214,6 +221,71 @@ async function ensureGitUserConfigured(assetsGitDir: string): Promise<void> {
214221
}
215222
}
216223

224+
interface EnsureRepositoryBranchOptions {
225+
assetsGitDir: string
226+
branchName: string
227+
logger: typeof import('../logger/index.js').logger
228+
}
229+
230+
async function ensureRepositoryBranch({
231+
assetsGitDir,
232+
branchName,
233+
logger,
234+
}: EnsureRepositoryBranchOptions): Promise<void> {
235+
const currentBranch = await getCurrentBranch(assetsGitDir)
236+
237+
if (currentBranch === branchName) {
238+
return
239+
}
240+
241+
const hasLocalBranch = await branchExistsLocally(assetsGitDir, branchName)
242+
if (hasLocalBranch) {
243+
logger.main.info(`🔀 切换到分支 ${branchName}`)
244+
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git checkout ${branchName}`
245+
return
246+
}
247+
248+
if (await remoteBranchExists(assetsGitDir, branchName)) {
249+
logger.main.info(`🔄 检出远程分支 ${branchName}`)
250+
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git checkout -b ${branchName} origin/${branchName}`
251+
return
252+
}
253+
254+
logger.main.info(`🌱 创建新分支 ${branchName}`)
255+
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git checkout -b ${branchName}`
256+
}
257+
258+
async function getCurrentBranch(assetsGitDir: string): Promise<string | null> {
259+
try {
260+
const { stdout } = await $({ cwd: assetsGitDir, stdio: 'pipe' })`git rev-parse --abbrev-ref HEAD`
261+
const branch = stdout.trim()
262+
if (!branch || branch === 'HEAD') {
263+
return null
264+
}
265+
return branch
266+
} catch {
267+
return null
268+
}
269+
}
270+
271+
async function branchExistsLocally(assetsGitDir: string, branchName: string): Promise<boolean> {
272+
try {
273+
await $({ cwd: assetsGitDir, stdio: 'pipe' })`git show-ref --verify --quiet refs/heads/${branchName}`
274+
return true
275+
} catch {
276+
return false
277+
}
278+
}
279+
280+
async function remoteBranchExists(assetsGitDir: string, branchName: string): Promise<boolean> {
281+
try {
282+
await $({ cwd: assetsGitDir, stdio: 'pipe' })`git rev-parse --verify origin/${branchName}`
283+
return true
284+
} catch {
285+
return false
286+
}
287+
}
288+
217289
function buildAuthenticatedRepoUrl(url: string, token?: string): string {
218290
if (!token) return url
219291

@@ -226,6 +298,6 @@ function buildAuthenticatedRepoUrl(url: string, token?: string): string {
226298
}
227299

228300
export const plugin = githubRepoSyncPlugin
229-
export function createGitHubRepoSyncPlugin(options?: GitHubRepoSyncPluginOptions): BuilderPlugin {
301+
export function createGitHubRepoSyncPlugin(options: GitHubRepoSyncPluginOptions): BuilderPlugin {
230302
return githubRepoSyncPlugin(options)
231303
}

packages/builder/src/types/config.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import type { BuilderPluginConfigEntry } from '../plugins/types.js'
22
import type { StorageConfig } from '../storage/interfaces.js'
33

4-
export interface BuilderRepoSettings {
5-
enable: boolean
6-
url: string
7-
token?: string
8-
}
9-
104
export interface LoggingConfig {
115
verbose: boolean
126
level: 'info' | 'warn' | 'error' | 'debug'
@@ -43,7 +37,6 @@ export interface SystemBuilderSettings {
4337
}
4438

4539
export interface UserBuilderSettings {
46-
repo: BuilderRepoSettings
4740
storage: StorageConfig | null
4841
}
4942

@@ -59,7 +52,7 @@ type DeepPartial<T> = T extends object
5952
}
6053
: T
6154

62-
export type BuilderConfigInput = DeepPartial<Omit<UserBuilderSettings, 'storage'>> & {
55+
export type BuilderConfigInput = {
6356
storage?: StorageConfig | null
6457
user?: DeepPartial<UserBuilderSettings>
6558
system?: DeepPartial<SystemBuilderSettings>

packages/docs/contents/deployment/docker.mdx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Docker
33
description: Guide to deploying Afilmory via Docker.
44
createdAt: 2025-07-20T22:35:03+08:00
5-
lastModified: 2025-10-28T19:48:05+08:00
5+
lastModified: 2025-11-14T00:23:27+08:00
66
---
77

88
# Docker Deployment
@@ -53,20 +53,26 @@ Before building your Docker image, you'll need to configure the following files:
5353
**`builder.config.ts`**
5454

5555
```ts
56-
import { defineBuilderConfig } from '@afilmory/builder'
56+
import { defineBuilderConfig, githubRepoSyncPlugin } from '@afilmory/builder'
5757

5858
export default defineBuilderConfig(() => ({
59-
repo: {
60-
enable: false,
61-
url: 'https://github.com/username/gallery-public',
62-
},
6359
storage: {
6460
provider: 's3',
6561
bucket: 'your-photos-bucket',
6662
region: 'us-east-1',
6763
prefix: 'photos/',
6864
customDomain: 'cdn.yourdomain.com',
6965
},
66+
plugins: [
67+
githubRepoSyncPlugin({
68+
repo: {
69+
enable: true,
70+
url: 'https://github.com/username/gallery-public',
71+
token: process.env.GIT_TOKEN,
72+
branch: process.env.BUILDER_REPO_BRANCH ?? 'main',
73+
},
74+
}),
75+
],
7076
performance: {
7177
worker: {
7278
enabled: true,
@@ -76,6 +82,8 @@ export default defineBuilderConfig(() => ({
7682
}))
7783
```
7884

85+
If you keep assets on a non-`main` branch, set `branch` accordingly; the plugin will also auto-create the specified branch when it doesn’t exist yet so first pushes succeed even for empty repositories.
86+
7987
**`.env`**
8088

8189
```bash

0 commit comments

Comments
 (0)