From 84a452783815b8b450a8a8e5f6229f90b02bb893 Mon Sep 17 00:00:00 2001 From: Do Siki Date: Tue, 18 Aug 2026 20:17:35 +0200 Subject: [PATCH] fix(cms): robust git publish workflow and untracked env handling - add .env.staging and .env.production patterns to .gitignore so local env files are ignored - use robust publish command: git add . && (git diff --cached --quiet || git commit ...) && git pull --rebase origin main && git push origin main - expand no-changes detection in publish response handling Closes MITHOME-59 --- .gitignore | 7 ++++++ content-editor.js | 29 +++++++++++++---------- scripts/test-content-editor-serializer.js | 3 ++- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 1b196d6..98c4f71 100755 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,13 @@ yarn-error.log* # local env files .env*.local .env +.env.staging +.env.production +.env.staging.* +.env.production.* +.env.* +!.env.staging.example +!.env.production.example # vercel .vercel diff --git a/content-editor.js b/content-editor.js index 6b8c0c0..c05bb9a 100644 --- a/content-editor.js +++ b/content-editor.js @@ -291,7 +291,7 @@ const server = http.createServer(async (req, res) => { return; } - // POST /publish — Git Commit & Push + // POST /publish — Git Commit, Pull Rebase & Push if (req.method === 'POST' && u.pathname === '/publish') { if (exceedsRateLimit(`publish:${clientAddress}`, PUBLISH_MAX_ATTEMPTS)) { writeAudit('publish_rate_limited', { clientAddress, user: CMS_USER }); @@ -299,22 +299,25 @@ const server = http.createServer(async (req, res) => { res.end(JSON.stringify({ ok: false, error: 'Túl sok publikálási kísérlet' })); return; } - exec('git add . && git commit -m "content: frissítve a CMS-ből" && git push', { cwd: CONTENT_DIR }, (error, stdout, stderr) => { + + // WHY: (git diff --cached --quiet || git commit) ensures we only commit when + // staged changes exist. git pull --rebase origin main integrates remote changes + // (or unpushed local commits) cleanly before git push origin main. + const publishCmd = 'git add . && (git diff --cached --quiet || git commit -m "content: frissítve a CMS-ből") && git pull --rebase origin main && git push origin main'; + + exec(publishCmd, { cwd: CONTENT_DIR }, (error, stdout, stderr) => { res.writeHead(200, { 'Content-Type': 'application/json' }); - if (error) { - // If there's nothing to commit, it's fine - if (stdout.includes('nothing to commit') || stdout.includes('working tree clean')) { - writeAudit('publish_finished', { clientAddress, user: CMS_USER, result: 'no_changes' }); - res.end(JSON.stringify({ ok: true, output: 'No changes to commit' })); - } else { - writeAudit('publish_finished', { clientAddress, user: CMS_USER, result: 'error' }); - res.end(JSON.stringify({ ok: false, error: stderr || stdout || error.message })); - } + const combinedOutput = `${stdout}\n${stderr}`; + const isNoChanges = /nothing to commit|nothing added to commit|working tree clean|everything up-to-date|already up to date/i.test(combinedOutput); + + if (error && !isNoChanges) { + writeAudit('publish_finished', { clientAddress, user: CMS_USER, result: 'error' }); + res.end(JSON.stringify({ ok: false, error: stderr || stdout || error.message })); } else { // Deploy only the explicitly configured environment; never default to production. exec(`cd ../../../ && ./deploy.sh ${CMS_DEPLOY_ENV} > deploy.log 2>&1 &`); - writeAudit('publish_finished', { clientAddress, user: CMS_USER, result: 'ok' }); - res.end(JSON.stringify({ ok: true, output: stdout })); + writeAudit('publish_finished', { clientAddress, user: CMS_USER, result: isNoChanges ? 'no_changes' : 'ok' }); + res.end(JSON.stringify({ ok: true, output: stdout || 'Sikeres publikálás' })); } }); return; diff --git a/scripts/test-content-editor-serializer.js b/scripts/test-content-editor-serializer.js index e5276b1..11a1e94 100644 --- a/scripts/test-content-editor-serializer.js +++ b/scripts/test-content-editor-serializer.js @@ -24,7 +24,8 @@ const fixture = { limit: 42, sections: [{ id: 'first', items: ['egy', 'kettő'], settings: { visible: false, weight: 1 } }], }; -const html = serverContext.globalThis.renderContentEditor('home', JSON.stringify(fixture), null); +const clientJs = fs.readFileSync('scripts/cms-editor-client.js', 'utf8'); +const html = serverContext.globalThis.renderContentEditor('home', JSON.stringify(fixture), null, 'csrf-test-token', { common: '⚙️ Közös' }, clientJs); const browserSource = [...html.matchAll(/]*)?>([\s\S]*?)<\/script>/g)].at(-1)[1] .replace("render(DATA, document.getElementById('editor'));", '') .replace("const toast = document.querySelector('.toast');", 'const toast = null;');