All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Bouton PDF blanc par chapitre avec numéro de page (ChapterHeader) - Badges morceaux plus visibles (bordure, poids, hover) dans ChapterHeader et SongBadges - PDF viewer : page cible + panneau signets ouverts par défaut (BookPdfReader) - Config YAML : pdfFile dans book, chapterPages pour le mapping chapitre→page - Admin book : section PDF du livre avec chemin éditable et sauvegarde - Git sync automatique : chaque sauvegarde admin commit+push en prod (ADMIN_GIT_SYNC=true) - Docker : git installé en prod, volumes pour .git/site/content/public Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
45 lines
1.2 KiB
TypeScript
45 lines
1.2 KiB
TypeScript
import { execFile } from 'node:child_process'
|
|
import { promisify } from 'node:util'
|
|
|
|
const exec = promisify(execFile)
|
|
|
|
const enabled = process.env.ADMIN_GIT_SYNC === 'true'
|
|
const cwd = process.cwd()
|
|
|
|
async function git(...args: string[]): Promise<string> {
|
|
const { stdout } = await exec('git', args, { cwd })
|
|
return stdout.trim()
|
|
}
|
|
|
|
/**
|
|
* Commit et push les modifications admin vers le dépôt git.
|
|
* Activé uniquement si ADMIN_GIT_SYNC=true (prod).
|
|
* Ne bloque jamais la réponse HTTP — exécution fire-and-forget.
|
|
*/
|
|
export function gitSyncContent(description: string, paths: string[] = ['.']) {
|
|
if (!enabled) return
|
|
|
|
const run = async () => {
|
|
try {
|
|
for (const p of paths) {
|
|
await git('add', p)
|
|
}
|
|
|
|
// Vérifier s'il y a vraiment des changements stagés
|
|
const status = await git('diff', '--cached', '--name-only')
|
|
if (!status) return
|
|
|
|
await git('commit', '-m', `[admin] ${description}`)
|
|
await git('push')
|
|
|
|
console.log(`[gitSync] pushed: ${description}`)
|
|
}
|
|
catch (err: any) {
|
|
console.error(`[gitSync] error:`, err.message ?? err)
|
|
}
|
|
}
|
|
|
|
// Fire-and-forget : ne pas bloquer la réponse HTTP
|
|
run()
|
|
}
|