geet-geet 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/bin/geet.sh +150 -0
- package/docs/AUTO_PROMOTE.md +173 -0
- package/docs/CONTRIBUTING.md +142 -0
- package/docs/DEMO.md +131 -0
- package/docs/DEV_GUIDE.md +519 -0
- package/docs/FAQ.md +128 -0
- package/docs/MERGE_KEEP_OURS.md +243 -0
- package/docs/MULTI_LAYERED_TEMPLATES.md +92 -0
- package/docs/PREVENT_COMMIT_PATTERNS.md +250 -0
- package/docs/PUBLISHING_A_TEMPLATE.md +150 -0
- package/docs/UNDERSTANDING_GEET.md +68 -0
- package/docs/USING_A_TEMPLATE.md +131 -0
- package/lib/detach.sh +225 -0
- package/lib/digest-and-locate.sh +476 -0
- package/lib/doctor.sh +252 -0
- package/lib/flags.sh +52 -0
- package/lib/ghcli.sh +386 -0
- package/lib/git.sh +55 -0
- package/lib/help.sh +53 -0
- package/lib/ignored.sh +4 -0
- package/lib/include.sh +54 -0
- package/lib/init.sh +234 -0
- package/lib/install.sh +166 -0
- package/lib/logger.sh +278 -0
- package/lib/pre-commit/auto-promote-pgi.sh +20 -0
- package/lib/pre-commit/auto-promote-readme.sh +20 -0
- package/lib/pre-commit/hook.sh +66 -0
- package/lib/pre-commit/protect-patterns.sh +76 -0
- package/lib/pre-commit/unstage-soft-detached-files.sh +48 -0
- package/lib/prework.sh +181 -0
- package/lib/session.sh +111 -0
- package/lib/split.sh +120 -0
- package/lib/sync.sh +98 -0
- package/lib/template.sh +635 -0
- package/lib/tree.sh +252 -0
- package/lib/whoops.sh +37 -0
- package/package.json +15 -0
package/lib/doctor.sh
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# doctor.sh — sanity checks for this repo + template layers
|
|
2
|
+
# Usage:
|
|
3
|
+
# source doctor.sh
|
|
4
|
+
# doctor
|
|
5
|
+
#
|
|
6
|
+
# Read-only health checks to catch common setup issues
|
|
7
|
+
|
|
8
|
+
doctor() {
|
|
9
|
+
|
|
10
|
+
# Show help if requested
|
|
11
|
+
if [[ "${1:-}" == "help" || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
|
12
|
+
cat <<EOF
|
|
13
|
+
$GEET_ALIAS doctor — run health checks on your geet setup
|
|
14
|
+
|
|
15
|
+
Goal:
|
|
16
|
+
- Help you quickly answer: "Is this repo set up correctly?"
|
|
17
|
+
- Catch common foot-guns (especially committing dot-git/)
|
|
18
|
+
|
|
19
|
+
What it checks:
|
|
20
|
+
✅ App repo exists (.git)
|
|
21
|
+
✅ Layer scripts present (git.sh, init.sh, etc.)
|
|
22
|
+
✅ Template git repo exists (dot-git/)
|
|
23
|
+
✅ Whitelist files exist (.geetinclude, .geetexclude)
|
|
24
|
+
✅ dot-git/ is NOT tracked by app repo (critical!)
|
|
25
|
+
✅ Detects other template layers
|
|
26
|
+
|
|
27
|
+
This is READ-ONLY:
|
|
28
|
+
- Does not modify any files
|
|
29
|
+
- Does not run merges/resets/cleans
|
|
30
|
+
- Safe to run anytime
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
$GEET_ALIAS doctor
|
|
34
|
+
|
|
35
|
+
Exit code:
|
|
36
|
+
0 - All checks passed
|
|
37
|
+
1 - One or more issues found
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
$GEET_ALIAS doctor # Run all checks
|
|
41
|
+
EOF
|
|
42
|
+
return 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# digest-and-locate.sh provides: APP_DIR, TEMPLATE_DIR, DOTGIT, TEMPLATE_NAME,
|
|
46
|
+
# TEMPLATE_GEETINCLUDE, TEMPLATE_GEETEXCLUDE, GEET_LIB, die, log, debug
|
|
47
|
+
|
|
48
|
+
# Additional paths
|
|
49
|
+
APP_GIT="$APP_DIR/.git"
|
|
50
|
+
|
|
51
|
+
# Pretty printing helpers
|
|
52
|
+
ok() { echo "[$TEMPLATE_NAME doctor] ✅ $*"; }
|
|
53
|
+
warn() { echo "[$TEMPLATE_NAME doctor] ⚠️ $*" >&2; }
|
|
54
|
+
bad() { echo "[$TEMPLATE_NAME doctor] ❌ $*" >&2; }
|
|
55
|
+
info() { echo "[$TEMPLATE_NAME doctor] $*"; }
|
|
56
|
+
|
|
57
|
+
# Track whether we should exit nonzero
|
|
58
|
+
HAS_ERRORS=0
|
|
59
|
+
fail() { bad "$*"; HAS_ERRORS=1; }
|
|
60
|
+
|
|
61
|
+
###############################################################################
|
|
62
|
+
# Helpers
|
|
63
|
+
###############################################################################
|
|
64
|
+
|
|
65
|
+
# Returns 0 if command exists
|
|
66
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
67
|
+
|
|
68
|
+
# Print a short list of dot-directories that look like layers
|
|
69
|
+
# A "layer" is defined as: hidden dir at repo root containing git.sh
|
|
70
|
+
detect_layers() {
|
|
71
|
+
# Only look one level deep, and only at hidden dirs.
|
|
72
|
+
# (We intentionally ignore .git and other common dotdirs.)
|
|
73
|
+
find "$APP_DIR" -maxdepth 1 -mindepth 1 -type d -name ".*" 2>/dev/null \
|
|
74
|
+
| while IFS= read -r d; do
|
|
75
|
+
base="$(basename "$d")"
|
|
76
|
+
case "$base" in
|
|
77
|
+
.|..|.git) continue ;;
|
|
78
|
+
esac
|
|
79
|
+
if [[ -f "$d/lib/git.sh" && -f "$d/lib/init.sh" ]]; then
|
|
80
|
+
echo "$d"
|
|
81
|
+
fi
|
|
82
|
+
done | sort
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Check whether a path is tracked by the APP repo (not ignored).
|
|
86
|
+
# If it is tracked, that's a big red flag for dot-git.
|
|
87
|
+
app_tracks_path() {
|
|
88
|
+
local path="$1"
|
|
89
|
+
git -C "$APP_DIR" ls-files --error-unmatch -- "$path" >/dev/null 2>&1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
###############################################################################
|
|
93
|
+
# Start
|
|
94
|
+
###############################################################################
|
|
95
|
+
|
|
96
|
+
info "repo root: $APP_DIR"
|
|
97
|
+
info "this layer: $TEMPLATE_NAME"
|
|
98
|
+
|
|
99
|
+
echo
|
|
100
|
+
|
|
101
|
+
###############################################################################
|
|
102
|
+
# 1) Basic file presence
|
|
103
|
+
###############################################################################
|
|
104
|
+
|
|
105
|
+
if [[ -d "$APP_GIT" && -f "$APP_GIT/HEAD" ]]; then
|
|
106
|
+
ok "app repo present (.git exists)"
|
|
107
|
+
else
|
|
108
|
+
fail "app repo missing or invalid: $APP_GIT"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if [[ -f "$GEET_LIB/git.sh" ]]; then
|
|
112
|
+
ok "layer git wrapper present: $GEET_LIB/git.sh"
|
|
113
|
+
else
|
|
114
|
+
fail "missing layer git wrapper: $GEET_LIB/git.sh"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ -f "$GEET_LIB/init.sh" ]]; then
|
|
118
|
+
ok "layer init script present"
|
|
119
|
+
else
|
|
120
|
+
fail "missing layer init script"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Check for post-init hook
|
|
124
|
+
POST_INIT_SH="$LAYER_DIR/post-init.sh"
|
|
125
|
+
if [[ -f "$POST_INIT_SH" ]]; then
|
|
126
|
+
if [[ -x "$POST_INIT_SH" ]]; then
|
|
127
|
+
ok "post-init hook present and executable: $POST_INIT_SH"
|
|
128
|
+
else
|
|
129
|
+
warn "post-init hook exists but is NOT executable: $POST_INIT_SH"
|
|
130
|
+
info "fix with: chmod +x $POST_INIT_SH"
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
echo
|
|
136
|
+
|
|
137
|
+
###############################################################################
|
|
138
|
+
# 2) Layer initialization state
|
|
139
|
+
###############################################################################
|
|
140
|
+
|
|
141
|
+
if [[ -d "$DOTGIT" && -f "$DOTGIT/HEAD" ]]; then
|
|
142
|
+
ok "layer initialized (dot-git exists)"
|
|
143
|
+
else
|
|
144
|
+
warn "layer NOT initialized (missing $DOTGIT/HEAD)"
|
|
145
|
+
info "run: $LAYER_NAME init"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# If dot-git exists, make sure compiled exclude exists (after git.sh runs at least once)
|
|
149
|
+
if [[ -d "$DOTGIT" && -f "$DOTGIT/HEAD" ]]; then
|
|
150
|
+
if [[ -f "$TEMPLATE_DIR/.geetexclude" ]]; then
|
|
151
|
+
ok "compiled exclude present: $TEMPLATE_DIR/.geetexclude"
|
|
152
|
+
else
|
|
153
|
+
warn "compiled exclude missing: $TEMPLATE_DIR/.geetexclude"
|
|
154
|
+
info "run: $LAYER_NAME status (this compiles include/exclude rules)"
|
|
155
|
+
fi
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo
|
|
159
|
+
|
|
160
|
+
###############################################################################
|
|
161
|
+
# 3) Critical safety: dot-git must NOT be tracked by app repo
|
|
162
|
+
###############################################################################
|
|
163
|
+
|
|
164
|
+
# dot-git is a git database; committing it is disastrous.
|
|
165
|
+
# We warn hard if it's tracked or if ignore rules look missing.
|
|
166
|
+
if [[ -d "$DOTGIT" ]]; then
|
|
167
|
+
# If ANY files under dot-git are tracked, that's a serious problem.
|
|
168
|
+
# Check the exact path relative to root
|
|
169
|
+
rel_dotgit="${DOTGIT#"$ROOT/"}"
|
|
170
|
+
|
|
171
|
+
if app_tracks_path "$rel_dotgit" || git -C "$ROOT" ls-files -- "$rel_dotgit" | grep -q .; then
|
|
172
|
+
fail "SECURITY: app repo is tracking $rel_dotgit (must be ignored; remove from git history)"
|
|
173
|
+
info "fix (careful):"
|
|
174
|
+
info " git rm -r --cached -- \"$rel_dotgit\""
|
|
175
|
+
info " add to app .geetexclude: **/dot-git/"
|
|
176
|
+
else
|
|
177
|
+
ok "dot-git is not tracked by app repo"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Also check that app ignore rules contain something like dot-git
|
|
181
|
+
# This is a heuristic (gitignore can be split across files), so it's a warning not failure.
|
|
182
|
+
if [[ -f "$ROOT/.geetexclude" ]]; then
|
|
183
|
+
if grep -Eq '(^|\s)(\*\*/dot-git/|\.geet/dot-git/|dot-git/)\s*$' "$ROOT/.geetexclude"; then
|
|
184
|
+
ok "app .geetexclude appears to ignore dot-git/"
|
|
185
|
+
else
|
|
186
|
+
warn "app .geetexclude does not obviously ignore dot-git/ (recommended: **/dot-git/)"
|
|
187
|
+
fi
|
|
188
|
+
else
|
|
189
|
+
warn "app .geetexclude missing (recommended: ignore **/dot-git/)"
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
echo
|
|
194
|
+
|
|
195
|
+
###############################################################################
|
|
196
|
+
# 4) Light functional checks (read-only)
|
|
197
|
+
###############################################################################
|
|
198
|
+
# These checks make sure the layer git wrapper can talk to the layer repo
|
|
199
|
+
# WITHOUT modifying anything.
|
|
200
|
+
|
|
201
|
+
if [[ -d "$DOTGIT" && -f "$DOTGIT/HEAD" ]]; then
|
|
202
|
+
if "$GEET_LIB/git.sh" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
203
|
+
ok "layer git wrapper can run git commands"
|
|
204
|
+
else
|
|
205
|
+
fail "layer git wrapper failed to run git (check permissions, env, or dot-git validity)"
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# Check that HEAD resolves
|
|
209
|
+
if "$GEET_LIB/git.sh" rev-parse HEAD >/dev/null 2>&1; then
|
|
210
|
+
ok "layer HEAD resolves"
|
|
211
|
+
else
|
|
212
|
+
warn "layer HEAD does not resolve yet (maybe no commits in layer repo)"
|
|
213
|
+
info "after defining whitelist and committing, this will become OK"
|
|
214
|
+
fi
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
echo
|
|
218
|
+
|
|
219
|
+
###############################################################################
|
|
220
|
+
# 5) Report other detected layers (informational)
|
|
221
|
+
###############################################################################
|
|
222
|
+
info "detected layers at repo root:"
|
|
223
|
+
layers="$(detect_layers || true)"
|
|
224
|
+
if [[ -z "$layers" ]]; then
|
|
225
|
+
info " (none found)"
|
|
226
|
+
else
|
|
227
|
+
while IFS= read -r d; do
|
|
228
|
+
base="$(basename "$d")"
|
|
229
|
+
name="${base#.}"
|
|
230
|
+
dotgit="$d/dot-git"
|
|
231
|
+
if [[ -f "$dotgit/HEAD" ]]; then
|
|
232
|
+
info " .$name (initialized)"
|
|
233
|
+
else
|
|
234
|
+
info " .$name (not initialized)"
|
|
235
|
+
fi
|
|
236
|
+
done <<< "$layers"
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
echo
|
|
240
|
+
|
|
241
|
+
###############################################################################
|
|
242
|
+
# Exit status
|
|
243
|
+
###############################################################################
|
|
244
|
+
if [[ "$HAS_ERRORS" -eq 1 ]]; then
|
|
245
|
+
bad "doctor found errors"
|
|
246
|
+
return 1
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
ok "doctor looks good"
|
|
250
|
+
return 0
|
|
251
|
+
|
|
252
|
+
} # end of doctor()
|
package/lib/flags.sh
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# has-flag.sh
|
|
2
|
+
|
|
3
|
+
has_flag() {
|
|
4
|
+
local flag="$1"
|
|
5
|
+
local outvar="$2"
|
|
6
|
+
|
|
7
|
+
local -a cleaned=()
|
|
8
|
+
local found=""
|
|
9
|
+
|
|
10
|
+
for arg in "${GEET_ARGS[@]}"; do
|
|
11
|
+
if [[ $arg == "$flag" ]]; then
|
|
12
|
+
found="true"
|
|
13
|
+
else
|
|
14
|
+
cleaned+=("$arg")
|
|
15
|
+
fi
|
|
16
|
+
done
|
|
17
|
+
|
|
18
|
+
GEET_ARGS=("${cleaned[@]}")
|
|
19
|
+
local -n out="$outvar"
|
|
20
|
+
out="$found"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
extract_flag() {
|
|
24
|
+
local flag="$1"
|
|
25
|
+
local outvar="$2"
|
|
26
|
+
|
|
27
|
+
local -a cleaned=()
|
|
28
|
+
local value=""
|
|
29
|
+
local i=0
|
|
30
|
+
|
|
31
|
+
while (( i < ${#GEET_ARGS[@]} )); do
|
|
32
|
+
local arg="${GEET_ARGS[i]}"
|
|
33
|
+
|
|
34
|
+
if [[ $arg == "$flag" ]]; then
|
|
35
|
+
# consume optional value
|
|
36
|
+
if (( i+1 < ${#GEET_ARGS[@]} )); then
|
|
37
|
+
value="${GEET_ARGS[i+1]}"
|
|
38
|
+
i=$((i+2))
|
|
39
|
+
else
|
|
40
|
+
i=$((i+1))
|
|
41
|
+
fi
|
|
42
|
+
continue
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
cleaned+=("$arg")
|
|
46
|
+
i=$((i+1))
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
GEET_ARGS=("${cleaned[@]}")
|
|
50
|
+
local -n out="$outvar"
|
|
51
|
+
out="$value"
|
|
52
|
+
}
|
package/lib/ghcli.sh
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# ghcli.sh — GitHub CLI integration for geet
|
|
2
|
+
# Usage:
|
|
3
|
+
# source ghcli.sh
|
|
4
|
+
# ghcli <subcommand> [args...]
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# digest-and-locate.sh provides: APP_DIR, TEMPLATE_NAME, TEMPLATE_GH_USER,
|
|
8
|
+
# TEMPLATE_GH_NAME, TEMPLATE_GH_URL, GEET_ALIAS, die, log
|
|
9
|
+
|
|
10
|
+
info() { echo "[$TEMPLATE_NAME gh] $*" >&2; }
|
|
11
|
+
|
|
12
|
+
###############################################################################
|
|
13
|
+
# CHECKS
|
|
14
|
+
###############################################################################
|
|
15
|
+
|
|
16
|
+
# Check if gh is installed
|
|
17
|
+
check_gh_installed() {
|
|
18
|
+
command -v gh >/dev/null 2>&1
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Check if gh is authenticated
|
|
22
|
+
check_gh_authenticated() {
|
|
23
|
+
gh auth status >/dev/null 2>&1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Ensure gh is installed and authenticated (auto-setup if needed)
|
|
27
|
+
ensure_gh_ready() {
|
|
28
|
+
local needs_setup=0
|
|
29
|
+
|
|
30
|
+
if ! check_gh_installed; then
|
|
31
|
+
needs_setup=1
|
|
32
|
+
elif ! check_gh_authenticated; then
|
|
33
|
+
needs_setup=1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if [[ "$needs_setup" -eq 1 ]]; then
|
|
37
|
+
log "gh CLI not ready, running setup..."
|
|
38
|
+
setup_cmd
|
|
39
|
+
fi
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
###############################################################################
|
|
43
|
+
# COMMANDS
|
|
44
|
+
###############################################################################
|
|
45
|
+
|
|
46
|
+
setup_cmd() {
|
|
47
|
+
log "checking GitHub CLI setup..."
|
|
48
|
+
|
|
49
|
+
# Check if gh is installed
|
|
50
|
+
if ! check_gh_installed; then
|
|
51
|
+
log "gh CLI not found"
|
|
52
|
+
log "installing gh CLI..."
|
|
53
|
+
|
|
54
|
+
# Detect package manager
|
|
55
|
+
if command -v apt >/dev/null 2>&1; then
|
|
56
|
+
log "using apt package manager"
|
|
57
|
+
sudo apt update
|
|
58
|
+
sudo apt install -y gh
|
|
59
|
+
elif command -v brew >/dev/null 2>&1; then
|
|
60
|
+
log "using homebrew package manager"
|
|
61
|
+
brew install gh
|
|
62
|
+
elif command -v dnf >/dev/null 2>&1; then
|
|
63
|
+
log "using dnf package manager"
|
|
64
|
+
sudo dnf install -y gh
|
|
65
|
+
elif command -v yum >/dev/null 2>&1; then
|
|
66
|
+
log "using yum package manager"
|
|
67
|
+
sudo yum install -y gh
|
|
68
|
+
else
|
|
69
|
+
die "could not detect package manager (apt, brew, dnf, yum). Please install gh manually: https://cli.github.com/"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Verify installation
|
|
73
|
+
if ! check_gh_installed; then
|
|
74
|
+
die "gh installation failed"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
log "gh CLI installed successfully"
|
|
78
|
+
else
|
|
79
|
+
log "gh CLI already installed: $(gh --version | head -1)"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Check authentication
|
|
83
|
+
if ! check_gh_authenticated; then
|
|
84
|
+
log "gh CLI not authenticated"
|
|
85
|
+
log "starting authentication flow..."
|
|
86
|
+
gh auth login
|
|
87
|
+
|
|
88
|
+
# Verify authentication
|
|
89
|
+
if ! check_gh_authenticated; then
|
|
90
|
+
die "gh authentication failed"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
log "gh CLI authenticated successfully"
|
|
94
|
+
else
|
|
95
|
+
log "gh CLI already authenticated"
|
|
96
|
+
gh auth status
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
log "GitHub CLI setup complete!"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
publish_app() {
|
|
103
|
+
GEET_ARGS=("$@")
|
|
104
|
+
|
|
105
|
+
log "here in publish app with $APP_DIR and $APP_NAME"
|
|
106
|
+
# Auto-setup if needed
|
|
107
|
+
ensure_gh_ready
|
|
108
|
+
|
|
109
|
+
# Get GitHub username
|
|
110
|
+
get_gh_user
|
|
111
|
+
|
|
112
|
+
log "publishing app repository to GitHub..."
|
|
113
|
+
log " source: $APP_NAME"
|
|
114
|
+
log " name: $APP_NAME"
|
|
115
|
+
log " owner: $GH_USER"
|
|
116
|
+
|
|
117
|
+
local APP_HOMEPAGE=${APP_HOMEPAGE:-};
|
|
118
|
+
local APP_DESC=${APP_DESC:-};
|
|
119
|
+
extract_flag --homepage _HOMEPAGE
|
|
120
|
+
extract_flag --desc _DESC
|
|
121
|
+
|
|
122
|
+
local PUB_APP=""
|
|
123
|
+
local PRI_APP=""
|
|
124
|
+
local INT_APP=""
|
|
125
|
+
|
|
126
|
+
has_flag --public PUB_APP
|
|
127
|
+
has_flag --private PRI_APP
|
|
128
|
+
has_flag --internal INT_APP
|
|
129
|
+
|
|
130
|
+
# Determine publish visibility
|
|
131
|
+
local publish_visibility="public"
|
|
132
|
+
if [[ -n "$PUB_APP" ]]; then
|
|
133
|
+
publish_visibility="public"
|
|
134
|
+
elif [[ -n "$PRI_APP" ]]; then
|
|
135
|
+
publish_visibility="private"
|
|
136
|
+
elif [[ -n "$INT_APP" ]]; then
|
|
137
|
+
publish_visibility="internal"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
[[ -n "$_DESC" ]] && APP_DESC="$_DESC";
|
|
141
|
+
[[ -n "$_HOMEPAGE" ]] && APP_HOMEPAGE="$_HOMEPAGE";
|
|
142
|
+
|
|
143
|
+
# Build args for gh repo create
|
|
144
|
+
local -a gh_args=(
|
|
145
|
+
repo
|
|
146
|
+
create
|
|
147
|
+
"$APP_NAME"
|
|
148
|
+
--source
|
|
149
|
+
.
|
|
150
|
+
--description
|
|
151
|
+
"$APP_DESC"
|
|
152
|
+
"--$publish_visibility"
|
|
153
|
+
--push
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Add any extra args passed by user
|
|
157
|
+
if [[ $# -gt 0 ]]; then
|
|
158
|
+
gh_args+=("${GEET_ARGS[@]}")
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
if [[ -n "$APP_HOMEPAGE" ]]; then
|
|
162
|
+
gh_args+=("--homepage")
|
|
163
|
+
gh_args+=("$APP_HOMEPAGE")
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
log "calling \`${gh_args[@]}\`"
|
|
167
|
+
|
|
168
|
+
# Run gh repo create from APP_DIR
|
|
169
|
+
(
|
|
170
|
+
cd "$APP_DIR"
|
|
171
|
+
gh "${gh_args[@]}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
log "app repository published successfully"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
publish_template() {
|
|
178
|
+
GEET_ARGS=("$@")
|
|
179
|
+
# Auto-setup if needed
|
|
180
|
+
ensure_gh_ready
|
|
181
|
+
|
|
182
|
+
[[ -z "$TEMPLATE_DIR" ]] && die "TEMPLATE_DIR not set - cannot publish template"
|
|
183
|
+
|
|
184
|
+
# Get GitHub username
|
|
185
|
+
get_gh_user
|
|
186
|
+
|
|
187
|
+
# Get the repo name - use TEMPLATE_GH_NAME if set, otherwise derive from TEMPLATE_NAME
|
|
188
|
+
local template_repo_name
|
|
189
|
+
if [[ -n "$TEMPLATE_GH_NAME" ]]; then
|
|
190
|
+
template_repo_name="$TEMPLATE_GH_NAME"
|
|
191
|
+
elif [[ -n "$TEMPLATE_NAME" ]]; then
|
|
192
|
+
template_repo_name="$TEMPLATE_NAME"
|
|
193
|
+
else
|
|
194
|
+
template_repo_name="$(basename "$TEMPLATE_DIR")"
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
# Remove leading period if present
|
|
198
|
+
template_repo_name="${template_repo_name#.}"
|
|
199
|
+
|
|
200
|
+
log "publishing template repository to GitHub..."
|
|
201
|
+
log " source: $TEMPLATE_DIR"
|
|
202
|
+
log " name: $template_repo_name"
|
|
203
|
+
log " owner: $GH_USER"
|
|
204
|
+
|
|
205
|
+
TEMPLATE_HOMEPAGE=${TEMPLATE_HOMEPAGE:-};
|
|
206
|
+
TEMPLATE_DESC=${TEMPLATE_DESC:-};
|
|
207
|
+
TEMPLATE_TOPICS=${TEMPLATE_TOPICS:-"geet,template"};
|
|
208
|
+
extract_flag --topics _TOPICS
|
|
209
|
+
extract_flag --homepage _HOMEPAGE
|
|
210
|
+
extract_flag --desc _DESC
|
|
211
|
+
|
|
212
|
+
local PUB_APP=""
|
|
213
|
+
local PRI_APP=""
|
|
214
|
+
local INT_APP=""
|
|
215
|
+
|
|
216
|
+
has_flag --public PUB_APP
|
|
217
|
+
has_flag --private PRI_APP
|
|
218
|
+
has_flag --internal INT_APP
|
|
219
|
+
|
|
220
|
+
# Determine publish visibility
|
|
221
|
+
local publish_visibility="public"
|
|
222
|
+
if [[ -n "$PUB_APP" ]]; then
|
|
223
|
+
publish_visibility="public"
|
|
224
|
+
elif [[ -n "$PRI_APP" ]]; then
|
|
225
|
+
publish_visibility="private"
|
|
226
|
+
elif [[ -n "$INT_APP" ]]; then
|
|
227
|
+
publish_visibility="internal"
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
[[ -n "$_DESC" ]] && TEMPLATE_DESC="$_DESC";
|
|
231
|
+
[[ -n "$_HOMEPAGE" ]] && TEMPLATE_HOMEPAGE="$_HOMEPAGE";
|
|
232
|
+
[[ -n "$_TOPICS" ]] && TEMPLATE_TOPICS="$_TOPICS";
|
|
233
|
+
|
|
234
|
+
# Clean up topics: remove trailing commas, trim whitespace
|
|
235
|
+
TEMPLATE_TOPICS="${TEMPLATE_TOPICS%,}" # Remove trailing comma
|
|
236
|
+
TEMPLATE_TOPICS="${TEMPLATE_TOPICS#,}" # Remove leading comma
|
|
237
|
+
TEMPLATE_TOPICS="${TEMPLATE_TOPICS// /}" # Remove spaces
|
|
238
|
+
|
|
239
|
+
# Set default if empty
|
|
240
|
+
[[ -z "$TEMPLATE_TOPICS" ]] && TEMPLATE_TOPICS="geet,template"
|
|
241
|
+
|
|
242
|
+
# Build args for gh repo create (without --source and --push, we'll handle that manually)
|
|
243
|
+
local -a gh_args=(
|
|
244
|
+
repo
|
|
245
|
+
create
|
|
246
|
+
"$template_repo_name"
|
|
247
|
+
--description
|
|
248
|
+
"$TEMPLATE_DESC"
|
|
249
|
+
"--$publish_visibility"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Add any extra args passed by user
|
|
253
|
+
if [[ $# -gt 0 ]]; then
|
|
254
|
+
gh_args+=("$@")
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
if [[ -n "$TEMPLATE_HOMEPAGE" ]]; then
|
|
258
|
+
gh_args+=("--homepage")
|
|
259
|
+
gh_args+=("$TEMPLATE_HOMEPAGE")
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
log "calling \`${gh_args[@]}\`"
|
|
263
|
+
|
|
264
|
+
# Create the remote repo
|
|
265
|
+
gh "${gh_args[@]}"
|
|
266
|
+
|
|
267
|
+
log "configuring template settings..."
|
|
268
|
+
gh repo edit "$GH_USER/$template_repo_name" --template
|
|
269
|
+
gh repo edit "$GH_USER/$template_repo_name" --add-topic "$TEMPLATE_TOPICS"
|
|
270
|
+
|
|
271
|
+
log "adding remote and pushing using geet_git..."
|
|
272
|
+
|
|
273
|
+
# Get the remote URL
|
|
274
|
+
local remote_url
|
|
275
|
+
remote_url="$(gh repo view "$GH_USER/$template_repo_name" --json sshUrl -q .sshUrl)"
|
|
276
|
+
|
|
277
|
+
log "remote URL: $remote_url"
|
|
278
|
+
|
|
279
|
+
# Add remote and push using geet_git (which handles custom git setup)
|
|
280
|
+
geet_git remote add origin "$remote_url" 2>/dev/null || geet_git remote set-url origin "$remote_url"
|
|
281
|
+
geet_git push -u origin HEAD
|
|
282
|
+
|
|
283
|
+
log "template repository published successfully"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
publish_cmd() {
|
|
287
|
+
local subcommand="${1:-}"
|
|
288
|
+
|
|
289
|
+
case "$subcommand" in
|
|
290
|
+
app)
|
|
291
|
+
shift
|
|
292
|
+
publish_app "$@"
|
|
293
|
+
;;
|
|
294
|
+
template)
|
|
295
|
+
shift
|
|
296
|
+
publish_template "$@"
|
|
297
|
+
;;
|
|
298
|
+
-h|--help|help)
|
|
299
|
+
cat <<EOF
|
|
300
|
+
[$TEMPLATE_NAME gh] Publish command
|
|
301
|
+
|
|
302
|
+
Usage:
|
|
303
|
+
$GEET_ALIAS publish <type> [options...]
|
|
304
|
+
|
|
305
|
+
Types:
|
|
306
|
+
app Publish as a regular repository
|
|
307
|
+
template Publish as a template repository (with --template flag and topics)
|
|
308
|
+
|
|
309
|
+
Options:
|
|
310
|
+
--public Create as public repository (default)
|
|
311
|
+
--private Create as private repository
|
|
312
|
+
--internal Create as internal repository
|
|
313
|
+
--desc Repository description
|
|
314
|
+
--homepage Repository homepage URL
|
|
315
|
+
--topics Comma-separated topics (template only, default: "geet,template")
|
|
316
|
+
|
|
317
|
+
Examples:
|
|
318
|
+
$GEET_ALIAS publish app
|
|
319
|
+
$GEET_ALIAS publish template --private --desc "My template"
|
|
320
|
+
$GEET_ALIAS publish app --desc "My app" --homepage "https://example.com"
|
|
321
|
+
EOF
|
|
322
|
+
;;
|
|
323
|
+
"")
|
|
324
|
+
die "publish command requires a type argument: 'app' or 'template'. Use '$GEET_ALIAS publish help' for more info."
|
|
325
|
+
;;
|
|
326
|
+
*)
|
|
327
|
+
die "unknown publish type: '$subcommand'. Use 'app' or 'template'. Use '$GEET_ALIAS publish help' for more info."
|
|
328
|
+
;;
|
|
329
|
+
esac
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
usage() {
|
|
333
|
+
cat <<EOF
|
|
334
|
+
[$TEMPLATE_NAME gh] GitHub CLI integration
|
|
335
|
+
|
|
336
|
+
Usage:
|
|
337
|
+
$GEET_ALIAS gh <command> [args...]
|
|
338
|
+
|
|
339
|
+
Commands:
|
|
340
|
+
setup Install and authenticate GitHub CLI (runs automatically if needed)
|
|
341
|
+
publish <type> Create and push repo to GitHub (type: 'app' or 'template')
|
|
342
|
+
<any> Pass through to gh CLI (e.g., 'gh pr list' -> '$GEET_ALIAS gh pr list')
|
|
343
|
+
help Show this help
|
|
344
|
+
|
|
345
|
+
Examples:
|
|
346
|
+
$GEET_ALIAS publish app # Publish as regular repository
|
|
347
|
+
$GEET_ALIAS publish template # Publish as template repository
|
|
348
|
+
$GEET_ALIAS publish app --private --desc "My cool project"
|
|
349
|
+
$GEET_ALIAS gh pr list # Auto-setup if needed, then list PRs
|
|
350
|
+
$GEET_ALIAS gh repo view
|
|
351
|
+
|
|
352
|
+
Note:
|
|
353
|
+
All commands automatically run 'setup' if gh is not installed or authenticated.
|
|
354
|
+
You rarely need to run 'setup' manually.
|
|
355
|
+
Use '$GEET_ALIAS publish help' for detailed publish options.
|
|
356
|
+
EOF
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
handle(){
|
|
361
|
+
###############################################################################
|
|
362
|
+
# COMMAND DISPATCH
|
|
363
|
+
###############################################################################
|
|
364
|
+
cmd="${1:-help}"
|
|
365
|
+
shift || true
|
|
366
|
+
|
|
367
|
+
case "$cmd" in
|
|
368
|
+
help|-h|--help)
|
|
369
|
+
usage
|
|
370
|
+
;;
|
|
371
|
+
|
|
372
|
+
setup)
|
|
373
|
+
setup_cmd "$@"
|
|
374
|
+
;;
|
|
375
|
+
|
|
376
|
+
pub|publish)
|
|
377
|
+
publish_cmd "$@"
|
|
378
|
+
;;
|
|
379
|
+
|
|
380
|
+
*)
|
|
381
|
+
# Pass through to gh CLI (auto-setup if needed)
|
|
382
|
+
ensure_gh_ready
|
|
383
|
+
gh "$cmd" "$@"
|
|
384
|
+
;;
|
|
385
|
+
esac
|
|
386
|
+
}
|