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>
This commit is contained in:
Innei
2025-11-14 00:23:26 +08:00
parent 6a4f868a5a
commit 3e60b382e1
7 changed files with 122 additions and 79 deletions

View File

@@ -1,15 +1,10 @@
import os from 'node:os'
import { defineBuilderConfig } from '@afilmory/builder'
import { defineBuilderConfig, githubRepoSyncPlugin } from '@afilmory/builder'
import { env } from './env.js'
export default defineBuilderConfig(() => ({
repo: {
enable: false,
url: process.env.BUILDER_REPO_URL ?? '',
token: env.GIT_TOKEN,
},
storage: {
// "provider": "local",
// "basePath": "./apps/web/public/photos",
@@ -61,5 +56,14 @@ export default defineBuilderConfig(() => ({
},
},
// plugins: [thumbnailStoragePlugin()],
plugins: [],
plugins: [
githubRepoSyncPlugin({
repo: {
enable: false,
url: process.env.BUILDER_REPO_URL ?? '',
token: env.GIT_TOKEN,
branch: process.env.BUILDER_REPO_BRANCH ?? 'main',
},
}),
],
}))

View File

@@ -658,10 +658,6 @@ export class AfilmoryBuilder {
addReference(ref)
}
if (this.getUserSettings().repo?.enable && !hasPluginWithName('afilmory:github-repo-sync')) {
addReference(() => import('@afilmory/builder/plugins/github-repo-sync.js'))
}
return references
}

View File

@@ -59,10 +59,6 @@ async function main() {
在 builder.config.ts 中设置 performance.worker.useClusterMode = true
可启用多进程集群模式,发挥多核心优势。
远程仓库:
如果启用了远程仓库 (repo.enable = true),构建完成后会自动推送更新。
需要配置 repo.token 或设置 GIT_TOKEN 环境变量以提供推送权限。
如果没有提供 token将跳过推送步骤。
`)
return
}
@@ -105,16 +101,9 @@ async function main() {
logger.main.info(` 集群模式:${config.system.observability.performance.worker.useClusterMode ? '启用' : '禁用'}`)
logger.main.info('')
if (!userConfig) {
logger.main.warn('未配置用户级设置repo/storage')
logger.main.warn('未配置用户级存储设置')
return
}
logger.main.info('📦 远程仓库配置:')
logger.main.info(` 启用状态:${userConfig.repo.enable ? '启用' : '禁用'}`)
if (userConfig.repo.enable) {
logger.main.info(` 仓库地址:${userConfig.repo.url || '未设置'}`)
logger.main.info(` 推送权限:${userConfig.repo.token ? '已配置' : '未配置'}`)
}
return
}

View File

@@ -56,11 +56,6 @@ function applySystemOverrides(target: BuilderConfig['system'], overrides?: Build
function ensureUserSettings(target: BuilderConfig): UserBuilderSettings {
if (!target.user) {
target.user = {
repo: {
enable: false,
url: '',
token: '',
},
storage: null,
}
}
@@ -71,12 +66,6 @@ function applyUserOverrides(target: BuilderConfig, overrides?: BuilderConfigInpu
if (!overrides) return
const user = ensureUserSettings(target)
if (overrides.repo) {
user.repo = {
...user.repo,
...overrides.repo,
}
}
if (overrides.storage !== undefined) {
user.storage = overrides.storage as StorageConfig | null
}
@@ -88,14 +77,6 @@ function normalizeBuilderConfig(defaults: BuilderConfig, input: BuilderConfigInp
applySystemOverrides(next.system, input.system)
applyUserOverrides(next, input.user)
if (input.repo) {
const user = ensureUserSettings(next)
user.repo = {
...user.repo,
...input.repo,
}
}
if (input.storage !== undefined) {
ensureUserSettings(next).storage = input.storage ?? null
}

View File

@@ -10,30 +10,35 @@ import type { BuilderPlugin } from './types.js'
const RUN_SHARED_ASSETS_DIR = 'assetsGitDir'
export interface GitHubRepoSyncPluginOptions {
repo: {
enable: boolean
url: string
token?: string
branch?: string
}
autoPush?: boolean
}
export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOptions = {}): BuilderPlugin {
export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOptions): BuilderPlugin {
const autoPush = options.autoPush ?? true
const repoConfig = options.repo
if (!repoConfig) {
throw new Error('githubRepoSyncPlugin 需要 repo 配置')
}
const branchName = repoConfig.branch?.trim() || 'main'
return {
name: 'afilmory:github-repo-sync',
hooks: {
beforeBuild: async (context) => {
const userConfig = context.config.user
if (!userConfig) {
context.logger.main.warn('⚠️ 未配置用户级设置,跳过远程仓库同步')
return
}
if (!userConfig.repo.enable) {
if (!repoConfig.enable) {
return
}
const { logger } = context
const { repo } = userConfig
if (!repo.url) {
if (!repoConfig.url) {
logger.main.warn('⚠️ 未配置远程仓库地址,跳过同步')
return
}
@@ -43,7 +48,7 @@ export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOption
logger.main.info('🔄 同步远程仓库...')
const repoUrl = buildAuthenticatedRepoUrl(repo.url, repo.token)
const repoUrl = buildAuthenticatedRepoUrl(repoConfig.url, repoConfig.token)
if (!existsSync(assetsGitDir)) {
logger.main.info('📥 克隆远程仓库...')
@@ -67,17 +72,12 @@ export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOption
}
}
await ensureRepositoryBranch({ assetsGitDir, branchName, logger })
await prepareRepositoryLayout({ assetsGitDir, logger })
logger.main.success('✅ 远程仓库同步完成')
},
afterBuild: async (context) => {
const userConfig = context.config.user
if (!userConfig) {
context.logger.main.warn('⚠️ 未配置用户级设置,跳过推送')
return
}
if (!autoPush || !userConfig.repo.enable) {
if (!autoPush || !repoConfig.enable) {
return
}
@@ -97,7 +97,8 @@ export default function githubRepoSyncPlugin(options: GitHubRepoSyncPluginOption
await pushUpdatesToRemoteRepo({
assetsGitDir,
logger: context.logger,
repoConfig: userConfig.repo,
repoConfig,
branchName,
})
},
},
@@ -152,9 +153,15 @@ interface PushRemoteOptions {
url: string
token?: string
}
branchName: string
}
async function pushUpdatesToRemoteRepo({ assetsGitDir, logger, repoConfig }: PushRemoteOptions): Promise<void> {
async function pushUpdatesToRemoteRepo({
assetsGitDir,
logger,
repoConfig,
branchName,
}: PushRemoteOptions): Promise<void> {
if (!repoConfig.url) {
return
}
@@ -194,7 +201,7 @@ async function pushUpdatesToRemoteRepo({ assetsGitDir, logger, repoConfig }: Pus
cwd: assetsGitDir,
stdio: 'inherit',
})`git commit -m ${commitMessage}`
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git push origin HEAD`
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git push -u origin HEAD:${branchName}`
logger.main.success('✅ 成功推送更新到远程仓库')
}
@@ -214,6 +221,71 @@ async function ensureGitUserConfigured(assetsGitDir: string): Promise<void> {
}
}
interface EnsureRepositoryBranchOptions {
assetsGitDir: string
branchName: string
logger: typeof import('../logger/index.js').logger
}
async function ensureRepositoryBranch({
assetsGitDir,
branchName,
logger,
}: EnsureRepositoryBranchOptions): Promise<void> {
const currentBranch = await getCurrentBranch(assetsGitDir)
if (currentBranch === branchName) {
return
}
const hasLocalBranch = await branchExistsLocally(assetsGitDir, branchName)
if (hasLocalBranch) {
logger.main.info(`🔀 切换到分支 ${branchName}`)
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git checkout ${branchName}`
return
}
if (await remoteBranchExists(assetsGitDir, branchName)) {
logger.main.info(`🔄 检出远程分支 ${branchName}`)
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git checkout -b ${branchName} origin/${branchName}`
return
}
logger.main.info(`🌱 创建新分支 ${branchName}`)
await $({ cwd: assetsGitDir, stdio: 'inherit' })`git checkout -b ${branchName}`
}
async function getCurrentBranch(assetsGitDir: string): Promise<string | null> {
try {
const { stdout } = await $({ cwd: assetsGitDir, stdio: 'pipe' })`git rev-parse --abbrev-ref HEAD`
const branch = stdout.trim()
if (!branch || branch === 'HEAD') {
return null
}
return branch
} catch {
return null
}
}
async function branchExistsLocally(assetsGitDir: string, branchName: string): Promise<boolean> {
try {
await $({ cwd: assetsGitDir, stdio: 'pipe' })`git show-ref --verify --quiet refs/heads/${branchName}`
return true
} catch {
return false
}
}
async function remoteBranchExists(assetsGitDir: string, branchName: string): Promise<boolean> {
try {
await $({ cwd: assetsGitDir, stdio: 'pipe' })`git rev-parse --verify origin/${branchName}`
return true
} catch {
return false
}
}
function buildAuthenticatedRepoUrl(url: string, token?: string): string {
if (!token) return url
@@ -226,6 +298,6 @@ function buildAuthenticatedRepoUrl(url: string, token?: string): string {
}
export const plugin = githubRepoSyncPlugin
export function createGitHubRepoSyncPlugin(options?: GitHubRepoSyncPluginOptions): BuilderPlugin {
export function createGitHubRepoSyncPlugin(options: GitHubRepoSyncPluginOptions): BuilderPlugin {
return githubRepoSyncPlugin(options)
}

View File

@@ -1,12 +1,6 @@
import type { BuilderPluginConfigEntry } from '../plugins/types.js'
import type { StorageConfig } from '../storage/interfaces.js'
export interface BuilderRepoSettings {
enable: boolean
url: string
token?: string
}
export interface LoggingConfig {
verbose: boolean
level: 'info' | 'warn' | 'error' | 'debug'
@@ -43,7 +37,6 @@ export interface SystemBuilderSettings {
}
export interface UserBuilderSettings {
repo: BuilderRepoSettings
storage: StorageConfig | null
}
@@ -59,7 +52,7 @@ type DeepPartial<T> = T extends object
}
: T
export type BuilderConfigInput = DeepPartial<Omit<UserBuilderSettings, 'storage'>> & {
export type BuilderConfigInput = {
storage?: StorageConfig | null
user?: DeepPartial<UserBuilderSettings>
system?: DeepPartial<SystemBuilderSettings>

View File

@@ -2,7 +2,7 @@
title: Docker
description: Guide to deploying Afilmory via Docker.
createdAt: 2025-07-20T22:35:03+08:00
lastModified: 2025-10-28T19:48:05+08:00
lastModified: 2025-11-14T00:23:27+08:00
---
# Docker Deployment
@@ -53,13 +53,9 @@ Before building your Docker image, you'll need to configure the following files:
**`builder.config.ts`**
```ts
import { defineBuilderConfig } from '@afilmory/builder'
import { defineBuilderConfig, githubRepoSyncPlugin } from '@afilmory/builder'
export default defineBuilderConfig(() => ({
repo: {
enable: false,
url: 'https://github.com/username/gallery-public',
},
storage: {
provider: 's3',
bucket: 'your-photos-bucket',
@@ -67,6 +63,16 @@ export default defineBuilderConfig(() => ({
prefix: 'photos/',
customDomain: 'cdn.yourdomain.com',
},
plugins: [
githubRepoSyncPlugin({
repo: {
enable: true,
url: 'https://github.com/username/gallery-public',
token: process.env.GIT_TOKEN,
branch: process.env.BUILDER_REPO_BRANCH ?? 'main',
},
}),
],
performance: {
worker: {
enabled: true,
@@ -76,6 +82,8 @@ export default defineBuilderConfig(() => ({
}))
```
If you keep assets on a non-`main` branch, set `branch` accordingly; the plugin will also auto-create the specified branch when it doesnt exist yet so first pushes succeed even for empty repositories.
**`.env`**
```bash