From d339926490d4ee1dca096cdaba500ce9dfbd2a8a Mon Sep 17 00:00:00 2001 From: wikiboss Date: Wed, 28 Oct 2026 18:02:03 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AE=8C=E5=96=84=E4=BD=8E=E6=9D=83?= =?UTF-8?q?=E9=99=90=E5=A4=87=E4=BB=BD=E6=81=A2=E5=A4=8D=E4=B8=8E=E5=8F=AA?= =?UTF-8?q?=E8=AF=BB=E9=83=A8=E7=BD=B2=E5=AF=86=E9=92=A5=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ops/README.md | 2 +- ops/auto_deploy.py | 33 ++++++++++++++++++--------------- ops/grain-auto-deploy.service | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/ops/README.md b/ops/README.md index 4378fc0..bbcba10 100644 --- a/ops/README.md +++ b/ops/README.md @@ -14,7 +14,7 @@ - 程序:`/opt/grain-auto-deploy/auto_deploy.py` - 配置:`/etc/grain-auto-deploy.json` - 版本及文件清单:`/var/lib/grain-auto-deploy/backend.json`、`frontend.json` -- 备份:`/www/backup/grain-auto-deploy/` +- 备份:`/var/lib/grain-auto-deploy/backups/` - 日志:`/var/log/grain-auto-deploy.log`,以及 `journalctl -u grain-auto-deploy.service` 运维命令: diff --git a/ops/auto_deploy.py b/ops/auto_deploy.py index db6260b..4322cf0 100644 --- a/ops/auto_deploy.py +++ b/ops/auto_deploy.py @@ -9,6 +9,7 @@ import logging import os from pathlib import Path, PurePosixPath import shutil +import shlex import subprocess import tarfile import tempfile @@ -64,6 +65,17 @@ def healthcheck(url, host): raise RuntimeError('Granary API health check failed') +def replace_file(source, live): + live.parent.mkdir(parents=True, exist_ok=True) + temp = live.with_name(live.name + '.deploy-tmp') + shutil.copyfile(str(source), str(temp)) + mode = live.stat().st_mode & 0o777 if live.exists() else 0o644 + os.chmod(str(temp), mode) + # The service runs as www; replacing an older root-uploaded file must not + # attempt to chown it back to root or require elevated permissions. + os.replace(str(temp), str(live)) + + def publish(config, repo): state_dir = Path(config['state']) cache = state_dir / (repo['name'] + '.git') @@ -71,6 +83,10 @@ def publish(config, repo): if not cache.exists(): run(['git', 'init', '--bare', str(cache)]) git = ['git', '--git-dir=' + str(cache)] + if repo.get('key'): + ssh = 'ssh -i {} -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile={}'.format( + shlex.quote(repo['key']), shlex.quote(repo['known_hosts'])) + git += ['-c', 'core.sshCommand=' + ssh] run(git + ['fetch', '--depth=1', repo['url'], '+refs/heads/master:refs/heads/deploy']) revision = run(git + ['rev-parse', 'refs/heads/deploy']).decode().strip() state_path = state_dir / (repo['name'] + '.json') @@ -136,20 +152,7 @@ def publish(config, repo): try: for name in changes: live = destination(root, name) - live.parent.mkdir(parents=True, exist_ok=True) - temp = live.with_name(live.name + '.deploy-tmp') - shutil.copyfile(str(stage / name), str(temp)) - if live.exists(): - stat = live.stat() - os.chmod(str(temp), stat.st_mode & 0o777) - if hasattr(os, 'chown'): - os.chown(str(temp), stat.st_uid, stat.st_gid) - else: - os.chmod(str(temp), 0o644) - if hasattr(os, 'chown'): - parent = live.parent.stat() - os.chown(str(temp), parent.st_uid, parent.st_gid) - os.replace(str(temp), str(live)) + replace_file(stage / name, live) for name in deleted: live = destination(root, name) if live.is_file(): @@ -160,7 +163,7 @@ def publish(config, repo): for record in reversed(records): live = destination(root, record['path']) if record['existed']: - shutil.copy2(str(backup / 'files' / record['path']), str(live)) + replace_file(backup / 'files' / record['path'], live) elif live.is_file(): live.unlink() LOG.exception('%s rolled back; backup=%s', repo['name'], backup) diff --git a/ops/grain-auto-deploy.service b/ops/grain-auto-deploy.service index e0e1b0d..d0bf671 100644 --- a/ops/grain-auto-deploy.service +++ b/ops/grain-auto-deploy.service @@ -14,6 +14,6 @@ NoNewPrivileges=true PrivateTmp=true ProtectHome=true ProtectSystem=strict -ReadWritePaths=/var/lib/grain-auto-deploy /www/backup/grain-auto-deploy /www/wwwroot/grain.gynhc.com /var/log/grain-auto-deploy.log +ReadWritePaths=/var/lib/grain-auto-deploy /www/wwwroot/grain.gynhc.com /var/log/grain-auto-deploy.log ReadOnlyPaths=/www/wwwroot/grain.gynhc.com/.git /www/wwwroot/grain.gynhc.com/application/database.php /www/wwwroot/grain.gynhc.com/application/config.php /www/wwwroot/grain.gynhc.com/application/extra/site.php RestrictSUIDSGID=true