Git Workflow » History » Revision 5
« Previous |
Revision 5/11
(diff)
| Next »
Redmine Admin, 10/31/2026 08:00 PM
deploy-publish.sh
Chapter 3.1 of the dmz-web documentation · headings are numbered like the cheat sheets · index: Wiki
- Table of contents
- 3.1.1 How Redmine reads them
- 3.1.2 New bare repo — root
- 3.1.3 Working copy, first time — owner user (su - <user> from root)
- 3.1.4 Push — root
- 3.1.5 Redmine
- 3.1.6 Day-to-day — deploy-commit.sh (root)
- 3.1.7 Publishing finished work — deploy-publish.sh (root)
- 3.1.8 Recovering from the usual mistakes
Repos are bare, owned by the restricted host user git (git-shell) — the one
deliberate exception to the no-host-packages rule. Redmine only reads them.
Who does what — this is the part that causes confusion, so read it first:
| Step | Run as | Why |
|---|---|---|
| create bare repo, ACL, SELinux label | root |
/srv/git is root/git territory |
git add / git commit in a working copy |
owner user (websvcmaster) | keeps .git/objects owned by that user |
| commit + push, day to day |
root, via deploy-commit.sh
|
it commits as websvcmaster and pushes as root itself — the only command you normally type |
git push by hand |
root | push needs sudo -u git git-receive-pack; websvcmaster has no sudo and never will |
| register repo in Redmine | Redmine admin UI |
websvcmaster's password is locked on purpose (passwd -l, set by
01-bootstrap-root.sh). Enter it only via su - websvcmaster from root — never
set a password for it. If a command run as websvcmaster asks for a password, it
is a root command run in the wrong shell: exit to root and run it there.
(passwd -S websvcmaster must show L.)
Repos: mds-dark.git (WordPress theme, project "MDS / MIMMAR Web Page Design"),
dmz-web-deploy.git (this host's ~/deploy, project "dmz-web Infrastructure").
3.1.1 How Redmine reads them¶
# site-redmine.container
Volume=/srv/git:/srv/git:ro
Environment=GIT_CONFIG_COUNT=1
Environment=GIT_CONFIG_KEY_0=safe.directory
Environment=GIT_CONFIG_VALUE_0=*
Rootless UID mapping: container uid n → host uid 200000 + n − 1; Redmine
runs as uid 999 → host 200998 (podman top site-redmine user huser).
One-time host prep (root):
semanage fcontext -a -t container_file_t '/srv/git(/.*)?'
restorecon -Rv /srv/git
3.1.2 New bare repo — root¶
R=/srv/git/<repo>.git
sudo -u git git init --bare $R
sudo -u git git -C $R symbolic-ref HEAD refs/heads/main # bare HEAD defaults to master; every repo here uses main
setfacl -R -m u:200998:rX $R # Redmine read access
setfacl -R -d -m u:200998:rX $R # ...also for files future pushes create
restorecon -Rv $R # "not reset as customized by admin" on every file = already container_file_t = OK
git config --global --add safe.directory /home/<user>/<workdir> # lets root push from that working copy
3.1.3 Working copy, first time — owner user (su - <user> from root)¶
git config --global user.name "<user>" # once per user, or commit refuses
git config --global user.email "sysadmin@mdsdefense.com" # matching a Redmine user's email links commits to them
cd <workdir>
git init -b main
# .gitignore first: no *.env, *secret*, *password*, *.key/*.pem/*.crt, dumps, archives, data/, tmp/
git add -A && git status --short
grep -nEi 'password|secret|token|key_base' -r . --exclude-dir=.git | grep -viE 'Secret=|podman secret|\$\{?[A-Z_]*(PASSWORD|SECRET)' # must be comments/prompts only
git commit -m "initial import $(date +%F)"
git remote add origin /srv/git/<repo>.git
git log --oneline
exit # back to root
3.1.4 Push — root¶
First push of a new working copy (once):
cd /home/<user>/<workdir>
git push --receive-pack='sudo -u git git-receive-pack' -u origin main
chown -R <user>:<user> .git # root's push leaves root-owned files in .git
Expected: * [new branch] main -> main. Verify: sudo -u git git -C /srv/git/<repo>.git log --oneline.
After that, never push by hand — use deploy-commit.sh (E).
3.1.5 Redmine¶
Project → Settings → Repositories → New repository: SCM Git, Main repository
✓, Identifier <repo>, Path /srv/git/<repo>.git, Report last commit ✓ → Create.
Open the Repository tab; the commit must be listed. If not:
podman exec site-redmine git -C /srv/git/<repo>.git log --oneline —
"dubious ownership" → safe.directory env missing; "permission denied" → ACL (step 3.1.2).
3.1.6 Day-to-day — deploy-commit.sh (root)¶
One root command does the whole cycle in the right identities: git add -A
and git commit as websvcmaster (identity auto-set), a guard that refuses
to commit staged lines that look like literal credentials, git push
as root via sudo -u git git-receive-pack, optional annotated tag, and
chown of .git back to websvcmaster. Source: root-bin/deploy-commit.sh in
the repo; installed as /usr/local/sbin/deploy-commit.sh (0750).
deploy-commit.sh -n # dry run: shows what is staged, commits nothing
deploy-commit.sh -m "what changed" # commit + push
deploy-commit.sh -m "cheat sheet v1.1" -t cheatsheet-1.1 # ...+ tag
deploy-commit.sh -t cheatsheet-1.1 # nothing to commit: tag HEAD, push the tag
deploy-commit.sh -y -m "..." # skip the credential guard after checking the flagged lines
deploy-commit.sh -h
Editing still happens as websvcmaster (su - websvcmaster, edit, exit);
deploy-commit.sh never edits files, only .git. The guard flags staged lines of
the form <…password|secret|token|api_key|app_key…> = <literal of 8+ chars>;
variables ($X), placeholders (<…>, ...), Quadlet Secret= lines and
comments are ignored. If it fires: move the value to a Podman secret or
.gitignore the file and re-run; if the lines are harmless, re-run with -y. Install/update the script:
install -o root -g root -m 0750 /home/websvcmaster/deploy/root-bin/deploy-commit.sh /usr/local/sbin/deploy-commit.sh
3.1.7 Publishing finished work — deploy-publish.sh (root)¶
Replaces the manual "copy from /root into the repo, fix owner, commit, tag, type the wiki into
Redmine" cycle. A delivery is one file, dmz-web-release-<version>.zip (<version> = the cheat sheet
version of that release), dropped into /root; run deploy-publish.sh as root. It calls
deploy-commit.sh for the git part, so E still applies.
The package holds the changed files (scripts, the cheat sheet), a wiki/ folder (pages + png),
RELEASE (version, build date) and SHA256SUMS. What the script does:
-
Package: unpacked into a fresh private directory; paths are checked (no
.., absolute paths
or symlinks), the file name must matchRELEASE,SHA256SUMSmust verify, and a file that is not
listed inSHA256SUMSrejects the package. -
Repo: each known file is compared by its version header with the repo / installed copy:
newer → installed (repo files owned by websvcmaster,/usr/local/sbincopies root 0750);
identical → nothing to do; older, same version with different content, or no version header →
refused (stops a stale copy overwriting a newer one;-foverrides). Known files: cheat sheet,
snipeit-install.sh,add-wordpress-site.sh,redmine-install.sh,check-sites.sh,
verify-host-deps.sh,deploy-commit.sh,deploy-publish.sh— add a line to theITEMStable in
the script for another one. -
Commit:
deploy-commit.sh -m "release <version>: <files and versions>"; a cheat sheet install also
gets the tagcheatsheet-<version>(skipped if that tag exists). -
Wiki:
wiki/*.md→ Redmine REST API: page created or updated (parentWiki, the duplicate
# titlefirst line dropped,.pngattached once, unchanged pages skipped).@@COMMIT@@in a page
(the Releases table) becomes the commit made in step 2 (on a re-run: the commit of the tagcheatsheet-<version>; a page that needs a hash and has none is skipped, never overwritten with the placeholder). -
Tidy: after a complete, clean run the package is moved (never deleted) to
/root/deploy-done/<timestamp>/. With refusals, errors,-n,-Ror-Wit stays in/root; running it
again is safe, handled files then show as identical and are skipped.
Loose files (no package) still work: put them into /root (and dmz-web-wiki-pages.zip for the wiki)
and the handled ones are moved to /root/deploy-done/<timestamp>/.
deploy-publish.sh -n # dry run: the plan, wiki read-only GETs, nothing changed or moved
deploy-publish.sh # do everything with the newest /root/dmz-web-release-*.zip
deploy-publish.sh -R # wiki only deploy-publish.sh -W # repo only
deploy-publish.sh -m "msg" # own commit message deploy-publish.sh -y # skip deploy-commit's guard
deploy-publish.sh -f # install even when older / equal / no version header
deploy-publish.sh --status # read-only overview: published / installed / waiting (see below)
What is published, installed and waiting? deploy-publish.sh --status answers it and changes nothing: the version of every file in the repo and in /usr/local/sbin (a difference is flagged), HEAD and the last cheatsheet-* tag, uncommitted or unpushed commits, release packages and pf-app-*.tar.gz waiting in /root (flagged NOT PUBLISHED YET when newer than the last tag), and for PF the Image= tag in the repo unit, in the installed unit and of the running site-pf container. Lines starting with !! are the things to look at.
One-time setup for the wiki step: deploy-publish.sh --init writes /root/.deploy-publish.conf
(mode 0600; set RM_PROJECT to the project identifier, Project settings → Identifier); put the
Redmine API key (My account → API access key) into /root/.redmine-api-key, chmod 600; Redmine
Administration → Settings → API → Enable REST web service. Requests go to NPM on this host
(--resolve track.mdsdefense.com:444:127.0.0.1), like check-sites.sh. The page text in Redmine is
overwritten by the package's text — the first real run replaces pages that were typed in by hand, so
start with -n. Bootstrap the first time (the script cannot install itself before it exists):
install -o root -g root -m 0750 /root/deploy-publish.sh /usr/local/sbin/deploy-publish.sh
deploy-publish.sh --init && deploy-publish.sh -n
Afterwards the script updates itself from the package (the running copy is replaced safely).
3.1.8 Recovering from the usual mistakes¶
-
fatal: could not open '.git/COMMIT_EDITMSG': Permission denied(or any
permission error on commit) → root touched.git(committed there, or pushed/tagged
withoutdeploy-commit.sh). Fix:chown -R <user>:<user> /home/<user>/<workdir>/.git,
then usedeploy-commit.shfrom now on (it chowns every time). Also check root's~/.gitconfigdidn't pick up the user's identity. -
Everything up-to-dateon push right after you expected a new commit → the
commit didn't happen (identity error or permission error scrolled past);git log --oneline
andgit status --shortin the working copy. A tag made at that moment points at the
old commit:deploy-commit.sh -t <tag>after the real commit (it force-moves the tag). -
error: src refspec main does not match any→ no commit yet onmain(step 3.1.3 not done). -
error: key does not contain a section: receivepack→ wrong syntax; use
--receive-pack=(step 3.1.4), not-c receivepack=. -
sudo: a password is required/ password prompt → you are websvcmaster; this is a root step (exit, thendeploy-commit.sh). - Redmine shows the repo empty → bare HEAD still points to
master(step 3.1.2symbolic-ref). -
deploy-publish.sh:REFUSED: no version header→ the copy is an old/unversioned one; fetch the current file or-fif you are sure.checksum mismatch/does not match RELEASE→ the package is damaged or was renamed; get it again.HTTP 401/403on the wiki step → API key wrong, REST not enabled, or the account may not edit this project's wiki.HTTP 404for every page → wrongRM_PROJECT(it must be the identifier, not the project name).
Source: dmz-web-podman-cheatsheet.md v1.5 in the dmz-web-deploy repo (Releases). Edit the wiki and the repo copy together.
Updated by Redmine Admin 6 days ago · 11 revisions