mirror of
https://github.com/Afilmory/afilmory
synced 2026-02-01 14:44:48 +00:00
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:
@@ -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',
|
||||
},
|
||||
}),
|
||||
],
|
||||
}))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 doesn’t exist yet so first pushes succeed even for empty repositories.
|
||||
|
||||
**`.env`**
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user