Project

General

Profile

Git Workflow » History » Version 1

Redmine Admin, 10/25/2026 04:16 PM

1 1 Redmine Admin
# Git Workflow (`/srv/git`, read-only into Redmine)
2
3
Repos are bare, owned by the restricted host user `git` (git-shell) — the one  
4
5
deliberate exception to the no-host-packages rule. Redmine only *reads* them.
6
7
**Who does what — this is the part that causes confusion, so read it first:**
8
9
| Step | Run as | Why |
10
| --- | --- | --- |
11
| create bare repo, ACL, SELinux label | **root** | `/srv/git` is root/git territory |
12
| `git add` / `git commit` in a working copy | **owner user** (websvcmaster) | keeps `.git/objects` owned by that user |
13
| 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 |
14
| `git push` by hand | **root** | push needs `sudo -u git git-receive-pack`; websvcmaster has **no sudo** and never will |
15
| register repo in Redmine | Redmine admin UI |  |
16
17
websvcmaster's password is **locked** on purpose (`passwd -l`, set by  
18
19
`01-bootstrap-root.sh`). Enter it only via `su - websvcmaster` from root — never  
20
21
set a password for it. If a command run as websvcmaster asks for a password, it  
22
23
is a root command run in the wrong shell: `exit` to root and run it there.  
24
25
(`passwd -S websvcmaster` must show `L`.)
26
27
Repos: `mds-dark.git` (WordPress theme, project "MDS / MIMMAR Web Page Design"),  
28
29
`dmz-web-deploy.git` (this host's `~/deploy`, project "dmz-web Infrastructure").
30
31
### How Redmine reads them
32
33
```
34
# site-redmine.container
35
Volume=/srv/git:/srv/git:ro
36
Environment=GIT_CONFIG_COUNT=1
37
Environment=GIT_CONFIG_KEY_0=safe.directory
38
Environment=GIT_CONFIG_VALUE_0=*
39
```
40
41
Rootless UID mapping: container uid *n* → host uid **200000 + n − 1**; Redmine  
42
43
runs as uid 999 → host **200998** (`podman top site-redmine user huser`).  
44
45
One-time host prep (root):
46
47
```
48
semanage fcontext -a -t container_file_t '/srv/git(/.*)?'
49
restorecon -Rv /srv/git
50
```
51
52
### A. New bare repo — root
53
54
```
55
R=/srv/git/<repo>.git
56
sudo -u git git init --bare $R
57
sudo -u git git -C $R symbolic-ref HEAD refs/heads/main   # bare HEAD defaults to master; every repo here uses main
58
setfacl -R  -m u:200998:rX $R                             # Redmine read access
59
setfacl -R -d -m u:200998:rX $R                           # ...also for files future pushes create
60
restorecon -Rv $R      # "not reset as customized by admin" on every file = already container_file_t = OK
61
git config --global --add safe.directory /home/<user>/<workdir>   # lets root push from that working copy
62
```
63
64
### B. Working copy, first time — owner user (`su - <user>` from root)
65
66
```
67
git config --global user.name  "<user>"                   # once per user, or commit refuses
68
git config --global user.email "sysadmin@mdsdefense.com"  # matching a Redmine user's email links commits to them
69
cd <workdir>
70
git init -b main
71
# .gitignore first: no *.env, *secret*, *password*, *.key/*.pem/*.crt, dumps, archives, data/, tmp/
72
git add -A && git status --short
73
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
74
git commit -m "initial import $(date +%F)"
75
git remote add origin /srv/git/<repo>.git
76
git log --oneline
77
exit                                                      # back to root
78
```
79
80
### C. Push — root
81
82
First push of a new working copy (once):
83
84
```
85
cd /home/<user>/<workdir>
86
git push --receive-pack='sudo -u git git-receive-pack' -u origin main
87
chown -R <user>:<user> .git          # root's push leaves root-owned files in .git
88
```
89
90
Expected: `* [new branch] main -> main`. Verify: `sudo -u git git -C /srv/git/<repo>.git log --oneline`.  
91
92
After that, never push by hand — use `deploy-commit.sh` (E).
93
94
### D. Redmine
95
96
Project → Settings → Repositories → New repository: SCM **Git**, Main repository  
97
98
✓, Identifier `<repo>`, Path `/srv/git/<repo>.git`, Report last commit ✓ → Create.  
99
100
Open the Repository tab; the commit must be listed. If not:  
101
102
`podman exec site-redmine git -C /srv/git/<repo>.git log --oneline` —  
103
104
"dubious ownership" → safe.directory env missing; "permission denied" → ACL (step A).
105
106
### E. Day-to-day — `deploy-commit.sh` (root)
107
108
One root command does the whole cycle in the right identities: `git add -A`  
109
110
and `git commit` **as websvcmaster** (identity auto-set), a guard that refuses  
111
112
to commit staged lines that look like literal credentials, `git push`  
113
114
**as root** via `sudo -u git git-receive-pack`, optional annotated tag, and  
115
116
`chown` of `.git` back to websvcmaster. Source: `root-bin/deploy-commit.sh` in  
117
118
the repo; installed as `/usr/local/sbin/deploy-commit.sh` (0750).
119
120
```
121
deploy-commit.sh -n                         # dry run: shows what is staged, commits nothing
122
deploy-commit.sh -m "what changed"          # commit + push
123
deploy-commit.sh -m "cheat sheet v1.1" -t cheatsheet-1.1   # ...+ tag
124
deploy-commit.sh -t cheatsheet-1.1          # nothing to commit: tag HEAD, push the tag
125
deploy-commit.sh -y -m "..."                # skip the credential guard after checking the flagged lines
126
deploy-commit.sh -h
127
```
128
129
Editing still happens as websvcmaster (`su - websvcmaster`, edit, `exit`);  
130
131
`deploy-commit.sh` never edits files, only `.git`. The guard flags staged lines of  
132
133
the form `<…password|secret|token|api_key|app_key…> = <literal of 8+ chars>`;  
134
135
variables (`$X`), placeholders (`<…>`, `...`), Quadlet `Secret=` lines and  
136
137
comments are ignored. If it fires: move the value to a Podman secret or  
138
139
`.gitignore` the file and re-run; if the lines are harmless, re-run with `-y`. Install/update the script:
140
141
```
142
install -o root -g root -m 0750 /home/websvcmaster/deploy/root-bin/deploy-commit.sh /usr/local/sbin/deploy-commit.sh
143
```
144
145
### Recovering from the usual mistakes
146
147
- `fatal: could not open '.git/COMMIT_EDITMSG': Permission denied` (or any  
148
149
permission error on commit) → root touched `.git` (committed there, or pushed/tagged  
150
151
without `deploy-commit.sh`). Fix: `chown -R <user>:<user> /home/<user>/<workdir>/.git`,  
152
153
then use `deploy-commit.sh` from now on (it chowns every time). Also check root's `~/.gitconfig` didn't pick up the user's identity.
154
- `Everything up-to-date` on push right after you expected a new commit → the  
155
156
commit didn't happen (identity error or permission error scrolled past); `git log --oneline`  
157
158
and `git status --short` in the working copy. A tag made at that moment points at the  
159
160
old commit: `deploy-commit.sh -t <tag>` after the real commit (it force-moves the tag).
161
- `error: src refspec main does not match any` → no commit yet on `main` (step B not done).
162
- `error: key does not contain a section: receivepack` → wrong syntax; use  
163
`--receive-pack=` (step C), not `-c receivepack=`.
164
- `sudo: a password is required` / password prompt → you are websvcmaster; this is a root step (`exit`, then `deploy-commit.sh`).
165
- Redmine shows the repo empty → bare HEAD still points to `master` (step A `symbolic-ref`).
166
---
167
168
*Source: `dmz-web-podman-cheatsheet.md` v1.1.1 in the `dmz-web-deploy` repo ([Releases](#)). Edit the wiki and the repo copy together.*
169
Git Workflow (/srv/git, read-only into Redmine)
170
[How Redmine reads them](#)[A. New bare repo — root](#)[B. Working copy, first time — owner user (su - <user> from root)](#)[C. Push — root](#)[D. Redmine](#)[E. Day-to-day — deploy-commit.sh (root)](#)[Recovering from the usual mistakes](#)
171
☰