Preview-Deploy: gleichzeitige Deploys desselben Repos nacheinander
Projektaenderung und Gitea-Webhook deployten Kirschner Gebaeudeservice am 30.09.2026 gleichzeitig; beide git clone liefen ins selbe preview-data-Verzeichnis und brachen ab. runDeploy sperrt jetzt je Arbeitsverzeichnis und Subdomain (keyedLock), andere Repos laufen weiter parallel. Test stellt drei gleichzeitige Deploys nach. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d4241d20c4
commit
edef85c7b3
2 files changed
+67
-1
No files matched your search
@@ -4,6 +4,7 @@ import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { config } from '../config.js'
|
||||
import { listDocuments, Query } from './appwriteAdmin.js'
|
||||
import { withLocks } from './keyedLock.js'
|
||||
|
||||
const execFileAsync = promisify(execFile)
|
||||
|
||||
@@ -428,8 +429,17 @@ export async function runDeploy(repoFullName, branch, previewConfig) {
|
||||
}
|
||||
|
||||
const subdomain = previewConfig.subdomain || repoFullName.split('/').pop()
|
||||
const cloneUrl = `${config.gitea.baseUrl}/${repoFullName}.git`
|
||||
const workDir = previewWorkDir(repoFullName)
|
||||
// Deploys desselben Repos oder derselben Subdomain nacheinander: Projektaenderung und
|
||||
// Gitea-Webhook kamen gleichzeitig, beide git clone liefen ins selbe workDir und
|
||||
// brachen ab (Kirschner Gebaeudeservice, 30.09.2026).
|
||||
return withLocks([`preview-work:${workDir}`, `preview-site:${subdomain}`], () =>
|
||||
deployLocked(repoFullName, branch, previewConfig, subdomain, workDir)
|
||||
)
|
||||
}
|
||||
|
||||
async function deployLocked(repoFullName, branch, previewConfig, subdomain, workDir) {
|
||||
const cloneUrl = `${config.gitea.baseUrl}/${repoFullName}.git`
|
||||
const targetDir = path.join(config.preview.deployRoot, subdomain)
|
||||
|
||||
await cloneRepo(cloneUrl, workDir, branch, config.gitea.apiToken)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { test, before, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs/promises'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
|
||||
// 2026-09-30 (Kirschner Gebäudeservice): Projektänderung und Gitea-Webhook deployten dasselbe
|
||||
// Repo gleichzeitig, beide git clone liefen ins selbe Arbeitsverzeichnis und brachen ab.
|
||||
let tmp
|
||||
let previewDeploy
|
||||
const cwd = process.cwd()
|
||||
|
||||
before(async () => {
|
||||
tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'preview-lock-'))
|
||||
const repo = path.join(tmp, 'gitea', 'WEBklar', 'kunde.git')
|
||||
const src = path.join(tmp, 'src')
|
||||
await fs.mkdir(src, { recursive: true })
|
||||
// Genug Inhalt, dass ein Klon Zeit braucht und sich zwei Klone überschneiden
|
||||
for (let i = 0; i < 300; i += 1) await fs.writeFile(path.join(src, `seite-${i}.html`), `<p>${i}</p>`.repeat(200))
|
||||
await fs.writeFile(path.join(src, 'index.html'), '<title>Kunde</title>')
|
||||
const git = (args, dir) => execFileSync('git', args, { cwd: dir, stdio: 'ignore' })
|
||||
git(['init', '-q', '-b', 'main'], src)
|
||||
git(['-c', 'user.name=t', '-c', 'user.email=t@t', 'add', '.'], src)
|
||||
git(['-c', 'user.name=t', '-c', 'user.email=t@t', 'commit', '-q', '-m', 'init'], src)
|
||||
git(['clone', '-q', '--bare', src, repo], tmp)
|
||||
|
||||
await fs.mkdir(path.join(tmp, 'work'))
|
||||
process.chdir(path.join(tmp, 'work')) // previewWorkDir() liegt unter process.cwd()
|
||||
Object.assign(process.env, {
|
||||
APPWRITE_ENDPOINT: 'http://127.0.0.1:9/v1',
|
||||
APPWRITE_API_KEY: 'test-key',
|
||||
GITEA_BASE_URL: `file://${path.join(tmp, 'gitea')}`,
|
||||
GITEA_API_TOKEN: '',
|
||||
PREVIEW_DEPLOY_ROOT: path.join(tmp, 'previews'),
|
||||
TRAEFIK_DYNAMIC_DIR: '',
|
||||
})
|
||||
previewDeploy = await import('../server/services/previewDeploy.js')
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
process.chdir(cwd)
|
||||
await fs.rm(tmp, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test('gleichzeitige Deploys desselben Repos laufen nacheinander und gelingen beide', async () => {
|
||||
const cfg = { subdomain: 'kunde', type: 'static' }
|
||||
const results = await Promise.allSettled([
|
||||
previewDeploy.runDeploy('WEBklar/kunde', 'main', cfg),
|
||||
previewDeploy.runDeploy('WEBklar/kunde', 'main', cfg),
|
||||
previewDeploy.runDeploy('WEBklar/kunde', 'main', cfg),
|
||||
])
|
||||
for (const r of results) assert.equal(r.status, 'fulfilled', r.reason?.message)
|
||||
assert.match(await fs.readFile(path.join(tmp, 'previews', 'kunde', 'index.html'), 'utf8'), /Kunde/)
|
||||
assert.equal((await fs.readdir(path.join(tmp, 'previews', 'kunde'))).filter((f) => f.startsWith('seite-')).length, 300)
|
||||
})
|
||||
Reference in new issue
Block a user