blob: 6ce356fba48a138d3b5ada1200f2e5fb8014a743 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
#!/usr/bin/env bash
BASEDIR=/git
C_bg_green=$(echo -e "\\e[42m")
C_bg_magenta=$(echo -e "\\e[45m")
C_bg_default=$(echo -e "\\e[49m")
C_bg_lblue=$(echo -e "\\e[104m")
_getent() {
local db=$1
local key=$2
getent "$db" "$key" | cut -d ':' -f 1
}
_list_repo_dirs() {
find "$1" -maxdepth 6 -type d -not -path '*.git/*' -name '*.git' 2>/dev/null
}
# shellcheck disable=SC2154
do_list() {
local root_path
if [ -n "$1" ]; then
root_path=$(readlink -f "$1")
else
root_path=$BASEDIR
fi
echo -n "Working.. listing repositories under $root_path"
for repo_path in $(_list_repo_dirs $root_path | sort); do
echo -ne "\\r" # to remove "Working..."
eval "$(stat --format 'local repo_path_uid=%u repo_path_gid=%g repo_path_mode=%a' "$repo_path")"
if [ "$repo_path_uid" != "$UID" ]; then
# so there must be a matching gid
local pass=false
for user_gid in $(id -G); do
if [ "$repo_path_gid" == "$user_gid" ]; then
pass=true
fi
done
if ! $pass; then
continue # skip that repo since uid and gids don't match
fi
fi
local shortdesc permissions color
shortdesc=$(cut -c 1-40 < "$repo_path/description")
permissions="owner=$(_getent passwd "$repo_path_uid")"
color="$C_bg_green"
if [ $(( 0$repo_path_mode & 070 )) -gt 0 ]; then # shared
permissions="$permissions group=$(_getent group "$repo_path_gid")"
color="$C_bg_lblue"
fi
if [ $(( 0$repo_path_mode & 07 )) -gt 0 ]; then # public
permissions="$permissions PUBLIC"
if ! [ -f "$repo_path/PUBLIC" ]; then
permissions="${permissions}-but-cgit-marker-file-missing"
fi
color="$C_bg_magenta"
fi
printf "%s%-60s (%s) (%s)\\n" "$color" "$repo_path$C_bg_default" "$permissions" "$shortdesc"
done
}
_create_repo() {
local shared=$2
local group=$3
local is_public=$4
read -r -p 'New repository name.git: '
local repo_name=$REPLY
if ! expr "$repo_name" : '.*\.git' >/dev/null; then
echo "Repository name must end with .git"
return 1
fi
local repo_dir=$BASEDIR/$USER/${repo_name}
test \! -e "$repo_dir" || { echo "Repo $repo_dir already exist"; exit 1; }
read -r -p 'Set Description: '
local repo_desc=$REPLY
read -r -p "Create $repo_name in $repo_dir Ok? (y/N)"
test "$REPLY" == "y" || exit 1
mkdir -p "$repo_dir"
git init -q --bare --shared="$shared" "$repo_dir"
GIT_DIR="$repo_dir" git config receive.denyNonFastforwards false
chgrp -R "$group" "$repo_dir"
echo "$repo_desc" > "$repo_dir/description"
if $is_public; then
touch "$repo_dir/PUBLIC"
echo "created $repo_dir/PUBLIC to expose via cgit"
fi
echo "done creating $repo_name in $repo_dir"
echo "use 'git repo show $repo_dir' for details"
return 0
}
do_create_public() {
_create_repo "public repository" "0664" "share" true
return $?
}
do_create_shared() {
_create_repo "group-writeable repository" "0660" "share" false
return $?
}
do_create_private() {
_create_repo "private repository" "0600" "$USER" false
return $?
}
do_show() {
local repo_file_mode repo_file_uid repo_file_user repo_file_gid repo_file_group repo_git_shared
for repo_path in "$@"; do
if ! [ -f "$repo_path/HEAD" ]; then
repo_path="$BASEDIR/$repo_path"
fi
if ! [ -f "$repo_path/HEAD" ]; then
echo "Not a git repository: $repo_path"
continue
fi
repo_file_mode=$(stat --format %a "$repo_path")
repo_file_uid=$(stat --format %u "$repo_path")
repo_file_user=$(_getent passwd "$repo_file_uid")
repo_file_gid=$(stat --format %g "$repo_path")
repo_file_group=$(_getent group "$repo_file_gid")
repo_git_shared=$(GIT_DIR="$repo_path" git config --get core.sharedrepository)
echo " Directory: $repo_path"
echo " ✔ Permissions mode ${repo_file_mode} (uid=${repo_file_uid}/${repo_file_user} gid=${repo_file_gid}/${repo_file_group})"
echo " git core.sharedrepository=$repo_git_shared"
echo " ⚡ Clone read/write SSH: git clone 'ssh://localnet.cc${repo_path}'"
if [ $(( 0$repo_file_mode & 07 )) -gt 0 ]; then
echo " Clone read/- HTTP: git clone 'http://localnet.cc/cgit/cgit.cgi/${repo_path#$BASEDIR/}'"
fi
echo " Update remote: git remote set-url origin 'ssh://localnet.cc${repo_path}"
echo " ☛ To update description execute: $EDITOR $repo_path/description"
echo ""
done
}
do_mirror() {
local root_path is_mirror
root_path=$(readlink -f "$1")
for repo_path in $(_list_repo_dirs "$root_path"); do
is_mirror=$(GIT_DIR="$repo_path" git config --get remote.origin.mirror)
if [ "$is_mirror" != "true" ]; then
continue # skip non-mirror repo
fi
echo "Mirror $repo_path from $(GIT_DIR="$repo_path" git config --get remote.origin.url)"
GIT_DIR="$repo_path" git fetch --force --prune origin
done
}
_do_make() {
local repo_path=$1
local repo_file_mode=$2
local repo_dir_mode=$3
local repo_is_public=$4
test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; }
git init -q --bare --shared="$repo_file_mode" "$repo_path"
find "$repo_path" -type d -exec chown "$USER:share" \{\} \;
find "$repo_path" -type d -exec chmod "$repo_dir_mode" \{\} \;
find "$repo_path" -type f -exec chmod "$repo_file_mode" \{\} \;
if $repo_is_public; then
touch "$repo_path/PUBLIC"
else
rm -f "$repo_path/PUBLIC"
fi
do_list "$repo_path"
}
do_make_public() {
_do_make "$1" 0664 0775 true
}
do_make_shared() {
_do_make "$1" 0660 0770 false
}
do_make_private() {
_do_make "$1" 0600 0700 false
}
do_help() {
cat <<HERE
Subcommands of git repo:
list [<dir>] List infos (optional: only below <dir>)
show <dir...> Show Commands for repo <dir>
mirror <dir> Mirror git repositories under <dir> by looking
for 'remote.origin.mirror = true' config flag.
Also set: 'fetch = +refs/*:refs/*'
create-public Create new repository that is
- Read/Write by Owner
- Read/Write by 'share' group.
- Read-Only through www via cgit
create-shared Create new repository that is
- Read/Write by Owner
- Read/Write by 'share' group.
create-private Create new repository that is
- Read/Write by Owner only
make-public <dir> Change permissions to public
make-shared <dir> Change permissions to shared
make-private <dir> Change permissions to private
☛ Base directory of repositories: $BASEDIR
☛ Color codes: ${C_bg_magenta}public${C_bg_default}, ${C_bg_lblue}shared${C_bg_default}, ${C_bg_green}private${C_bg_default}
☛ For limited ssh-access with current user configure ~/.ssh/authorized_keys with:
command=\"git shell -c \\\"\$SSH_ORIGINAL_COMMAND\\\"\",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3.....
HERE
}
case "$1" in
list) do_list "$2" ;;
create-public) do_create_public ;;
create-shared) do_create_shared ;;
create-private) do_create_private ;;
make-public) do_make_public "$2" ;;
make-shared) do_make_shared "$2" ;;
make-private) do_make_private "$2" ;;
show)
shift
do_show "$@"
;;
mirror) do_mirror "$2" ;;
help)
do_help
;;
*)
cat <<HERE
Unknown subcommand: '$1'
HERE
do_help
;;
esac
|