Skip to content

Commit c718053

Browse files
author
Test
committed
Add Project Type Configuration & Metadata Collection
feat: Add GIV_PROJECT_TYPE config variable feat: Implement metadata collection framework for projects feat: Add Node.js package provider for metadata detection
1 parent 78500cb commit c718053

File tree

7 files changed

+339
-1
lines changed

7 files changed

+339
-1
lines changed

.giv/config

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ GIV_VERSION_PATTERN=""
3636
GIV_TODO_PATTERN=""
3737

3838
### Comma-separated list of files or glob patterns to search for TODOs (e.g., "*.py,src/**/*.js")
39-
GIV_TODO_FILES="docs/todos.md"
39+
GIV_TODO_FILES="docs/todos.md"
40+
41+
GIV_PROJECT_TYPE=custom

.giv/project_metadata.env

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
title=giv
2+
description="giv is a CLI utility for generating changelogs and summaries."
3+
author=ITLackey
4+
url=https://github.com/giv-cli
5+
repository_url=https://github.com/giv-cli/giv
6+
license=CC-BY

docs/features/project_metadata.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
Lets implement this feature:
2+
3+
Extendable Project-Level Metadata Architecture for giv
4+
5+
Objective
6+
7+
Augment giv so that rich project metadata is collected once per invocation (early in the run pipeline) from:
8+
9+
1. Known project types via provider scripts (e.g. Node, Python, Rust, Go).
10+
11+
2. User configuration (override or supplement autodetected values) via .giv/config.
12+
13+
3. Custom project type definitions allowing users to specify metadata or files to parse.
14+
15+
The collected metadata—cached under .giv/cache—will be exposed to all prompt-building logic, enabling templates (announcements, release notes, README generation, etc.) to incorporate:
16+
17+
title
18+
19+
url (homepage or documentation)
20+
21+
description
22+
23+
repository\_url
24+
25+
latest\_version
26+
27+
language
28+
29+
license
30+
31+
author / authors
32+
33+
dependencies (list)
34+
35+
dev\_dependencies (list)
36+
37+
scripts (build, test, etc.)
38+
39+
vcs info (default branch, commit SHA, tag count)
40+
41+
This POSIX-compliant design ensures portability across /bin/sh implementations.
42+
43+
---
44+
45+
High-Level Architecture Changes
46+
47+
1. Metadata Phase: Add a metadata\_init call immediately after argument parsing in giv.sh.
48+
49+
2. Provider Registry: Under project/providers/, each provider\_<id>.sh implements detection and collection functions in POSIX shell.
50+
51+
3. Orchestrator: New project/metadata.sh sources providers, detects, prioritizes, collects, merges, caches, and exports metadata.
52+
53+
4. Prompt Integration: Extend llm.sh’s build\_prompt to inject metadata from cache and support \${{meta.<key>}} tokens.
54+
55+
5. Configuration: Add keys in .giv/config:
56+
57+
GIV\_PROJECT\_TYPE to force a provider or set to "auto" for autodetection
58+
59+
GIV\_PROJECT\_METADATA\_FILE for external .env file
60+
61+
---
62+
63+
Provider Interface (POSIX Shell)
64+
65+
Each provider\_<id>.sh must define:
66+
67+
# Detect presence (0 = yes, >0 = no)
68+
69+
provider\_<id>\_detect() {
70+
71+
# e.g. \[ -f "package.json" \]
72+
73+
return 1
74+
}
75+
76+
# Collect metadata: output KEY\tVALUE per line
77+
78+
provider\_<id>\_collect() {
79+
80+
# echo "title\tMy Project"
81+
82+
}
83+
84+
---
85+
86+
Directory Layout
87+
88+
project/
89+
metadata.sh # orchestrator
90+
providers/
91+
provider\_node\_pkg.sh
92+
provider\_python\_pep621.sh
93+
provider\_generic\_git.sh
94+
.giv/
95+
cache/
96+
project\_metadata.env
97+
config # overrides
98+
99+
---
100+
101+
Simplified Orchestration Flow (POSIX Steps)
102+
103+
1. Determine Provider:
104+
105+
if [ "$GIV\_PROJECT\_TYPE" = "custom" ]; then
106+
[ -f "$GIV\_HOME/project\_provider.sh" ] && . "$GIV\_HOME/project\_provider.sh"
107+
elif [ "$GIV\_PROJECT\_TYPE" = "auto" ]; then
108+
for f in "$GIV\_LIB\_DIR/project/providers"/*.sh; do
109+
. "$f"
110+
provider\_detect=$(set | awk -F'=' '/^provider\_.\*\_detect=/ { sub("()","",\$1); print \$1 }')
111+
for fn in \$provider\_detect; do
112+
\$fn && DETECTED\_PROVIDER="\$fn" && break
113+
done
114+
[ -n "$DETECTED_PROVIDER" ] && break
115+
done
116+
else
117+
. "$GIV\_LIB\_DIR/project/providers/provider\_${GIV\_PROJECT\_TYPE}.sh"
118+
fi
119+
120+
2. Collect Metadata:
121+
122+
if [ -n "$DETECTED\_PROVIDER" ]; then
123+
coll=${DETECTED\_PROVIDER%_detect}\_collect
124+
$coll | while IFS="\t" read -r key val; do
125+
printf '%s=%s\n' "$key" "${val//"/\\"}" >> "$GIV\_CACHE\_DIR/project\_metadata.env"
126+
done
127+
fi
128+
129+
3. Apply Overrides:
130+
131+
[ -f "$GIV\_HOME/project\_metadata.env" ] && . "$GIV\_HOME/project\_metadata.env"
132+
133+
4. Export:
134+
135+
# shell export
136+
137+
set -a
138+
. "$GIV\_CACHE\_DIR/project\_metadata.env"
139+
set +a
140+
141+
---
142+
143+
Cache Format
144+
145+
project\_metadata.env: simple KEY=value lines
146+
147+
Example project\_metadata.env:
148+
149+
title=My Project
150+
author=Jane Doe
151+
latest\_version=1.2.3
152+
repository\_url=https://github.com/org/repo.git
153+
154+
---
155+
156+
Prompt Token Usage
157+
158+
Templates can use tokens like:
159+
160+
Project: [title] (v[latest_version])
161+
Home: [url]
162+
Repo: [repository_url]
163+
Desc: [description]
164+
License: [license]
165+
166+
---
167+
168+
Configuration (.giv/config)
169+
170+
GIV\_PROJECT\_TYPE=auto
171+
GIV\_PROJECT\_METADATA\_FILE=metadata.env
172+
GIV\_PROJECT\_METADATA\_EXTRA<<'EOF'
173+
owner.team=platform
174+
tier=gold
175+
EOF
176+
177+
---
178+
179+
Testing & Validation Checklist
180+
181+
\[ \] Providers detect correctly in mixed-type repos
182+
183+
\[ \] Keys include title, url, description, repository\_url, latest\_version
184+
185+
\[ \] Overrides apply with correct precedence
186+
187+
\[ \] Cache files (.env) generate and load properly
188+
189+
\[ \] Prompt tokens replace as expected
190+
191+
\[ \] POSIX shell lint (shellcheck -s sh)
192+
193+
\[ \] --refresh-metadata flag forces reload
194+
195+
---
196+
197+
Implementation Checklist
198+
199+
\[ \] Create project/metadata.sh orchestrator (POSIX)
200+
201+
\[ \] Build built-in providers: node\_pkg, python\_pep621, generic\_git
202+
203+
\[ \] Source custom providers from $GIV\_HOME/project\_provider.sh
204+
205+
\[ \] Write metadata to .giv/cache/project\_metadata.env
206+
207+
\[ \] Update giv.sh to call metadata\_init
208+
209+
\[ \] Extend llm.sh build\_prompt for \${{meta.\*}} tokens
210+
211+
\[ \] Add config parsing in args.sh or config.sh
212+
213+
\[ \] Add --refresh-metadata support
214+
215+
\[ \] Write Bats tests for detection, caching, overrides
216+
217+
\[ \] Document usage in README

src/config.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export GIV_API_URL="${GIV_API_URL:-}"
2727
export GIV_API_KEY="${GIV_API_KEY:-}"
2828

2929
## Project details
30+
export GIV_PROJECT_TYPE="${GIV_PROJECT_TYPE:-auto}"
3031
export GIV_VERSION_FILE="${GIV_VERSION_FILE:-}"
3132
export GIV_VERSION_PATTERN="${GIV_VERSION_PATTERN:-}"
3233
export GIV_TODO_PATTERN="${GIV_TODO_PATTERN:-}"

src/giv.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ GIV_DOCS_DIR="${DOCS_DIR}"
118118
. "${LIB_DIR}/llm.sh"
119119
# shellcheck source=project.sh
120120
. "${LIB_DIR}/project.sh"
121+
# shellcheck source=project/metadata.sh
122+
. "${LIB_DIR}/project/metadata.sh"
121123
# shellcheck source=history.sh
122124
. "${LIB_DIR}/history.sh"
123125
# shellcheck source=commands.sh
@@ -130,6 +132,7 @@ if [ "${is_sourced}" -eq 0 ]; then
130132
ensure_giv_dir_init
131133
portable_mktemp_dir
132134
parse_args "$@"
135+
metadata_init
133136

134137
# # Verify the PWD is a valid git repository
135138
# if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then

src/project/metadata.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/bin/sh
2+
# project/metadata.sh: Orchestrator for collecting project metadata in POSIX shell
3+
# Produces: .giv/cache/project_metadata.env
4+
# Usage: call metadata_init early; then source the env file or rely on exported vars.
5+
6+
metadata_init() {
7+
: "${GIV_CACHE_DIR:?GIV_CACHE_DIR not set}"
8+
: "${GIV_LIB_DIR:?GIV_LIB_DIR not set}"
9+
: "${GIV_HOME:?GIV_HOME not set}"
10+
11+
mkdir -p "${GIV_CACHE_DIR}/" || return 1
12+
13+
DETECTED_PROVIDER=""
14+
# -------------------------
15+
# Determine Provider
16+
# -------------------------
17+
# If the project type is "custom", source a user-defined provider script if it exists.
18+
if [ "${GIV_PROJECT_TYPE}" = "custom" ]; then
19+
[ -f "${GIV_HOME}/project_provider.sh" ] && . "${GIV_HOME}/project_provider.sh"
20+
21+
# If the project type is "auto", attempt to automatically detect the provider.
22+
elif [ "${GIV_PROJECT_TYPE}" = "auto" ]; then
23+
# Source all provider scripts from the providers directory to register their detect functions.
24+
provider_detect_fns=""
25+
for f in "${GIV_LIB_DIR}/project/providers"/*.sh; do
26+
[ -f "$f" ] && . "$f"
27+
done
28+
# List all shell functions matching provider_*_detect and collect their names.
29+
provider_detect_fns=$(set | awk -F'=' '/^provider_.*_detect=/ { sub("()","",$1); print $1 }')
30+
# Iterate over each detect function and call it; the first one that returns true is selected.
31+
for fn in $provider_detect_fns; do
32+
if "$fn"; then
33+
DETECTED_PROVIDER="$fn"
34+
break
35+
fi
36+
done
37+
38+
# Otherwise, source the provider script matching the specified project type and set the detect function.
39+
else
40+
. "${GIV_LIB_DIR}/project/providers/provider_${GIV_PROJECT_TYPE}.sh"
41+
DETECTED_PROVIDER="provider_${GIV_PROJECT_TYPE}_detect"
42+
fi
43+
44+
# -------------------------
45+
# Collect Metadata
46+
# -------------------------
47+
ENV_FILE="${GIV_CACHE_DIR}/project_metadata.env"
48+
: > "$ENV_FILE"
49+
50+
if [ -n "$DETECTED_PROVIDER" ]; then
51+
coll="${DETECTED_PROVIDER%_detect}_collect"
52+
"$coll" | while IFS="$(printf '\t')" read -r key val; do
53+
[ -z "$key" ] && continue
54+
esc_val=$(printf '%s' "$val" | sed 's/"/\\"/g')
55+
printf 'GIV_METADATA_%s=%s\n' "$(printf '%s' "$key" | tr '[:lower:]' '[:upper:]')" "$esc_val" >> "$ENV_FILE"
56+
done
57+
fi
58+
59+
# -------------------------
60+
# Apply Overrides
61+
# -------------------------
62+
if [ -f "${GIV_HOME}/project_metadata.env" ]; then
63+
tmp=$(portable_mktemp "tmp.env.XXXXXX")
64+
cat "${GIV_HOME}/project_metadata.env" > "$tmp"
65+
# shellcheck disable=SC1090
66+
. "$tmp"
67+
print_debug "Applying overrides from ${GIV_HOME}/project_metadata.env"
68+
awk -F= '!/^#/ && /^[A-Za-z0-9_]+=/ {print $1}' "${GIV_HOME}/project_metadata.env" | while read -r k; do
69+
print_debug "Applying override for $k"
70+
v="$(eval "printf '%s' \"\${$k}\"")"
71+
printf 'GIV_METADATA_%s="%s"\n' "$(printf '%s' "$k" | tr '[:lower:]' '[:upper:]')" "$v" >> "$ENV_FILE"
72+
done
73+
fi
74+
75+
# -------------------------
76+
# Export for current shell
77+
# -------------------------
78+
set -a
79+
. "$ENV_FILE"
80+
set +a
81+
}
82+
83+
# get_metadata_value: retrieve a metadata value by key
84+
get_metadata_value() {
85+
key="$1"
86+
eval "printf '%s\n' \"\${$key:-}\""
87+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
# provider_node_pkg.sh: Node.js project metadata provider
3+
4+
# Detect presence (0 = yes, >0 = no)
5+
provider_node_pkg_detect() {
6+
[ -f "package.json" ]
7+
}
8+
9+
# Collect metadata: output KEY\tVALUE per line
10+
provider_node_pkg_collect() {
11+
title=$(jq -r '.name' package.json 2>/dev/null)
12+
description=$(jq -r '.description' package.json 2>/dev/null)
13+
version=$(jq -r '.version' package.json 2>/dev/null)
14+
repository=$(jq -r '.repository.url' package.json 2>/dev/null)
15+
author=$(jq -r '.author' package.json 2>/dev/null)
16+
17+
[ -n "$title" ] && printf 'title\t%s\n' "$title"
18+
[ -n "$description" ] && printf 'description\t%s\n' "$description"
19+
[ -n "$version" ] && printf 'latest_version\t%s\n' "$version"
20+
[ -n "$repository" ] && printf 'repository_url\t%s\n' "$repository"
21+
[ -n "$author" ] && printf 'author\t%s\n' "$author"
22+
}

0 commit comments

Comments
 (0)