Skip to content

Commit b7d4f56

Browse files
author
Test
committed
Refactor argument parsing and configuration handling
- Improved the `parse_args` function in `args.sh` to handle configuration file loading more robustly and to ensure that the correct values are set for API model, URL, and key. - Enhanced the `config.sh` command to normalize keys and improve error handling for missing configuration files. - Updated `history.sh` to source configuration and project metadata conditionally based on the test environment. - Modified `llm.sh` to handle API responses more gracefully, including improved error messages and content extraction using `jq` if available. - Added a utility function in `markdown.sh` to conditionally use `glow` for markdown output. - Improved project metadata extraction logic in `project_metadata.sh` to ensure project type detection is consistent and robust. - Enhanced temporary file creation in `system.sh` to ensure proper cleanup and handling of temporary files. - Updated tests in `summarize_commit.bats`, `summarize_target.bats`, `test_parse_args.bats`, and `test_version_extraction.bats` to ensure proper setup and teardown, including configuration file creation for consistent test behavior.
1 parent f1bd745 commit b7d4f56

File tree

17 files changed

+421
-269
lines changed

17 files changed

+421
-269
lines changed

.github/workflows/dash-shell.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Dash Shell CI
2+
3+
on:
4+
push:
5+
branches: [ "main", "spike/config" ]
6+
pull_request:
7+
branches: [ "main", "spike/config" ]
8+
9+
jobs:
10+
dash-shell:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
- name: Install dash and bats
15+
run: |
16+
sudo apt-get update
17+
sudo apt-get install -y dash bats
18+
- name: Run Bats tests with dash
19+
run: |
20+
SHELL=/bin/dash bats tests/

.shellcheckrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ external-sources=true
77

88
# Exclude files under the tests folder
99
exclude-path=tests/*
10+
disable=SC3043
1011

1112
# # Disable notices about things that are intentionally non-POSIX or acceptable
1213
# disable = \
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ install_from_github() {
4545
esac
4646

4747
tag=$(curl -fsSL https://api.github.com/repos/charmbracelet/glow/releases/latest |
48-
grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
48+
grep '"tag_name":' | sed -r 's/.*"([^"]+)".*/\1/')
4949
file="glow_${tag#v}_${os}_${arch}.tar.gz"
5050

5151
tmpdir=$(mktemp -d)
@@ -82,8 +82,8 @@ install_from_github() {
8282
# installs it from GitHub. It then verifies whether the installation was successful.
8383
#
8484
# Exits with status 1 if the installation fails.
85-
ensure_glow() {
86-
if is_installed; then
85+
install_glow() {
86+
if is_glow_installed; then
8787
echo "✔ glow already installed: $(command -v glow)"
8888
return
8989
fi
@@ -95,7 +95,7 @@ ensure_glow() {
9595
install_from_github
9696
fi
9797

98-
if ! is_installed; then
98+
if ! is_glow_installed; then
9999
echo "Installation failed. See https://github.com/charmbracelet/glow#installation"
100100
exit 1
101101
fi

config

Lines changed: 0 additions & 35 deletions
This file was deleted.
202 KB
Binary file not shown.

docs/architecture.md

Lines changed: 72 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# Architecture
22

3-
A quick overview: giv is a POSIX-compliant shell script suite whose main entrypoint (giv.sh) initializes environment and dispatches subcommands. It sources modular libraries for configuration, system utilities, argument parsing, Markdown handling, LLM integration, project metadata, history extraction, and subcommand implementations. Major workflows include commit summarization (via summarize\_commit), changelog generation (cmd\_changelog), release-notes and announcements (via cmd\_document), and a generic document driver. Data domains span Git metadata (commits, diffs, tags), project metadata (version files, project titles), AI prompt templates, and generated summaries. Caching under `.giv/cache` avoids redundant work. Below are the details.
3+
A quick overview: giv is a POSIX-compliant shell script suite w### Changelog Generation
4+
5+
The `changelog.sh` subcommand follows:
6+
7+
1. Summarize commits/ranges with `summarize_target`.
8+
2. Build the changelog prompt (`changelog_prompt.md`) via `build_prompt`.
9+
3. Generate content (`generate_from_prompt`).
10+
4. Update `CHANGELOG.md` using `manage_section` and append a "Managed by giv" link.n entrypoint (`giv.sh`) initializes the environment and dispatches subcommands. It sources modular libraries for configuration, system utilities, argument parsing, Markdown handling, LLM integration, project metadata, history extraction, and subcommand implementations. Major workflows include commit summarization (via `summarize_commit`), changelog generation (via `changelog.sh`), release-notes and announcements (via `document.sh`), and a generic document driver. Data domains span Git metadata (commits, diffs, tags), project metadata (version files, project titles), AI prompt templates, and generated summaries. Caching under `.giv/cache` avoids redundant work. Below are the details.
411

512
## Repository Structure
613

@@ -10,7 +17,7 @@ The main script, **giv.sh**, locates the library, template, and docs directories
1017

1118
### Configuration Module
1219

13-
**config.sh** exports globals for version (`__VERSION`), directory paths (`GIV_HOME`, `GIV_TMP_DIR`, `GIV_CACHE_DIR`), debugging flags, default Git revision/pathspec, AI model and API settings, project tokens, and default output filenames (`CHANGELOG.md`, etc.) ([config.sh][2]).
20+
**config.sh** centralizes all configuration logic. It exports globals for version (`__VERSION`), directory paths (`GIV_HOME`, `GIV_TMP_DIR`, `GIV_CACHE_DIR`), debugging flags, default Git revision/pathspec, AI model and API settings, project tokens, and default output filenames (`CHANGELOG.md`, etc.). All configuration keys are normalized via a dedicated `normalize_key()` function to ensure consistent access and override precedence. The module also handles loading from `.giv/config`, environment variables, and CLI arguments, applying a strict order of precedence and robust error handling for missing or malformed config values. This guarantees predictable, portable configuration across all scripts and environments. ([config.sh][2])
1421

1522
### System Utilities
1623

@@ -22,32 +29,56 @@ The main script, **giv.sh**, locates the library, template, and docs directories
2229

2330
### Markdown Handling
2431

25-
**markdown.sh** implements `manage_section` for append/prepend/update of Markdown sections, `append_link` to add links, utilities for stripping and normalizing Markdown (`strip_markdown`, `normalize_blank_lines`), and Markdown viewing via `glow` ([markdown.sh][5]).
32+
**markdown.sh** implements `manage_section` for append/prepend/update of Markdown sections, `append_link` to add links, utilities for stripping and normalizing Markdown (`strip_markdown`, `normalize_blank_lines`), and Markdown viewing via `glow` (with fallback to `cat` if unavailable) ([markdown.sh][5]).
2633

2734
### LLM Integration
2835

29-
**llm.sh** handles JSON-escaping (`json_escape`), remote API calls (`generate_remote` via curl), local inference (`run_local` via Ollama), response parsing (`extract_content_from_response`), and high-level `generate_response`, along with prompt token replacement (`replace_tokens`), prompt building (`build_prompt`), and execution (`generate_from_prompt`) ([llm.sh][6]).
30-
31-
### Project Metadata
32-
33-
**metadata.sh** extracts project titles and version information from common project files like `package.json`, `pyproject.toml`, and `setup.py`. It also supports custom version file detection ([metadata.sh][7]).
36+
**llm.sh** handles JSON-escaping (`json_escape`), remote API calls (`generate_remote` via curl), local inference (`run_local` via Ollama), response parsing (`extract_content_from_response`), and high-level `generate_response`, along with prompt token replacement (`replace_tokens`), prompt building (`build_prompt`), and execution (`generate_from_prompt`). It includes robust error handling and a fallback to `jq` for JSON parsing ([llm.sh][6]).
3437

3538
### Centralized Metadata Retrieval
3639

37-
**project_metadata.sh** now includes a centralized function, `get_metadata_value`, which retrieves metadata values (e.g., version, title) based on the project type. This function is used across scripts like `history.sh` and `llm.sh` to ensure consistent and modular metadata management. The project type is detected during initialization and stored in the configuration for runtime use.
40+
**project_metadata.sh** includes a centralized function, `get_metadata_value`, which retrieves metadata values (e.g., version, title) based on the project type. This function is used across scripts like `history.sh` and `llm.sh` to ensure consistent and modular metadata management. The project type is detected during initialization and stored in the configuration for runtime use.
3841

3942
### History Extraction
4043

41-
**history.sh** provides utilities for summarizing Git history, extracting TODO changes, and caching summaries ([history.sh][8]).
44+
**history.sh** provides utilities for summarizing Git history, extracting TODO changes, and caching summaries. It consolidates diff logic in a single `get_diff` function and ensures strict error handling and cleanup of temporary files ([history.sh][8]).
4245

4346
### Subcommand Implementations
4447

45-
**commands.sh** ties everything into user-facing commands: `cmd_message` for AI draft commit messages, `cmd_changelog` for changelog updates (using `manage_section`), `cmd_document` as a generic driver for release-notes, announcements, custom docs, and maintenance commands (`show_version`, `get_available_releases`, `run_update`) ([commands.sh][9]).
48+
Each subcommand in the `giv` CLI is implemented as a separate `.sh` script located in the `src/commands/` folder. The main `giv.sh` script detects the subcommand and delegates execution to the corresponding script. The architecture has recently evolved to support a more generic and modular approach:
49+
50+
1. **Generic Document Driver (`document.sh`)**:
51+
- Subcommands like `announcement.sh`, `release-notes.sh`, and `summary.sh` now act as thin wrappers that delegate their functionality to `document.sh`.
52+
- These scripts pass specific templates (e.g., `announcement_prompt.md`, `release_notes_prompt.md`, `final_summary_prompt.md`) to `document.sh` for processing.
53+
- The `document` subcommand itself allows arbitrary prompt files via `--prompt-file`, supporting custom document types and workflows.
54+
55+
2. **Direct Implementations**:
56+
- Scripts like `changelog.sh` and `message.sh` currently implement their logic directly, but there is an ongoing migration to unify all document-like subcommands under the generic driver for consistency and maintainability.
57+
- These scripts handle argument parsing, Git history summarization, and AI prompt generation within the script.
58+
59+
3. **Shared Functionality**:
60+
- Common argument parsing and utility functions are provided by `document_args.sh` and other shared scripts. The new `parse_document_args` function is used for all document-related subcommands to ensure consistent flag handling (e.g., `--prompt-file`, `--project-type`).
61+
62+
4. **Execution Flow and Error Handling**:
63+
- The main `giv.sh` script identifies the subcommand and executes the corresponding `.sh` file from the `commands` folder.
64+
- If the subcommand script is not found, an error message is displayed with a list of available subcommands.
65+
- All subcommands now include improved error handling: missing dependencies, invalid config, or failed AI calls are surfaced to the user with clear messages and exit codes. Optional dependencies (e.g., Glow, Ollama, GitHub CLI) are checked at runtime, and warnings are issued if unavailable.
66+
67+
This modular structure ensures that each subcommand is self-contained and easy to maintain, while shared functionality is centralized for reuse. The ongoing migration aims to further unify subcommand logic and reduce duplication.
68+
## Testing and Error Handling
69+
70+
- The project includes an extensive test suite under `tests/`, covering all major workflows and edge cases. Tests are run in sandboxed environments to ensure reliability and portability.
71+
- Error handling is robust: missing dependencies, invalid config, or failed AI calls result in clear user-facing messages and non-zero exit codes. Warnings are issued for optional but recommended settings.
72+
- Users can opt for manual review before saving generated content, providing an additional layer of safety.
73+
74+
---
75+
76+
These updates ensure the documentation accurately reflects the current and planned architecture, workflows, error handling, and testing strategy of the giv CLI tool.
4677

4778
## Data Domains
4879

4980
1. **Git Data**: Commits, diffs, staged/unstaged changes, untracked files, commit dates, tags and ranges (handled by `build_diff`, `get_commit_date`, Git plumbing) ([history.sh][8]).
50-
2. **Project Metadata**: Version files (`package.json`, etc.), extracted versions (`get_version_info`), and project titles (`get_project_title`) ([metadata.sh][7]).
81+
2. **Project Metadata**: Version files (`package.json`, etc.), extracted versions (`get_version_info`), and project titles (`get_project_title`) ([project_metadata.sh][7]).
5182
3. **AI Prompt Templates**: Markdown templates stored under `templates/` (e.g. `summary_prompt.md`, `changelog_prompt.md`, `release_notes_prompt.md`, `announcement_prompt.md`) ([giv.sh][1]).
5283
4. **Generated Content**: Summary Markdown, commit messages, changelogs, release notes, announcements, managed under `.giv/cache` and output files in project root ([system.sh][3]) ([history.sh][8]).
5384
5. **Configuration & State**: Stored in `.giv/config`, `.giv/cache`, `.giv/.tmp`, and optionally `.giv/templates` (after `init`) ([system.sh][3]).
@@ -75,16 +106,16 @@ The `summarize_commit` function in **history.sh** orchestrates:
75106

76107
### Release-Notes & Announcements
77108

78-
Both use the generic `cmd_document` driver:
109+
Both use the generic `document.sh` subcommand as their driver:
79110

80111
1. Summarize history into temp file.
81112
2. Build prompt from `release_notes_prompt.md` or `announcement_prompt.md`.
82-
3. Call `generate_from_prompt` with tailored temperature and context window ([giv.sh][1]).
113+
3. Call `generate_from_prompt` with tailored temperature and context window.
83114
4. Output to `RELEASE_NOTES.md` or `ANNOUNCEMENT.md`.
84115

85116
### Generic Document Generation
86117

87-
The `document` subcommand invokes `cmd_document` with a user-supplied `--prompt-file`, enabling arbitrary AI-driven reports over any revision/pathspec ([commands.sh][9]).
118+
The `document` subcommand invokes `document.sh` with a user-supplied `--prompt-file`, enabling arbitrary AI-driven reports over any revision/pathspec.
88119

89120
## Architecture Diagrams
90121

@@ -110,7 +141,7 @@ sequenceDiagram
110141
History-->>giv.sh: summaries file
111142
giv.sh->>Markdown: manage_section(...)
112143
Markdown-->>giv.sh: updated Markdown
113-
giv.sh->>Output: write CHANGELOG.md
144+
giv.sh->>Output: write
114145
```
115146

116147
### Class Diagram
@@ -142,33 +173,37 @@ classDiagram
142173
+generate_response()
143174
+build_prompt()
144175
}
145-
class metadata.sh {
176+
class project_metadata.sh {
146177
+get_project_title()
147178
+get_version_info()
179+
+get_metadata_value()
148180
}
149181
class history.sh {
150182
+build_history()
151183
+summarize_commit()
152184
}
153-
class commands.sh {
154-
+cmd_changelog()
155-
+cmd_document()
156-
+cmd_message()
185+
class "commands/*.sh" {
186+
+changelog.sh
187+
+document.sh
188+
+message.sh
189+
+announcement.sh
190+
+release-notes.sh
191+
+summary.sh
157192
}
158193
159194
giv.sh --> config.sh
160195
giv.sh --> system.sh
161196
giv.sh --> args.sh
162197
giv.sh --> markdown.sh
163198
giv.sh --> llm.sh
164-
giv.sh --> metadata.sh
199+
giv.sh --> project_metadata.sh
165200
giv.sh --> history.sh
166-
giv.sh --> commands.sh
167-
commands.sh --> history.sh
168-
commands.sh --> llm.sh
169-
commands.sh --> markdown.sh
170-
history.sh --> metadata.sh
171-
llm.sh --> metadata.sh
201+
giv.sh --> "commands/*.sh"
202+
"commands/*.sh" --> history.sh
203+
"commands/*.sh" --> llm.sh
204+
"commands/*.sh" --> markdown.sh
205+
history.sh --> project_metadata.sh
206+
llm.sh --> project_metadata.sh
172207
markdown.sh --> system.sh
173208
```
174209

@@ -180,9 +215,9 @@ This should give you a clear view of how the scripts interconnect, the data each
180215
[4]: /src/args.sh "args.sh"
181216
[5]: /src/markdown.sh "markdown.sh"
182217
[6]: /src/llm.sh
183-
[7]: /src/metadata.sh
218+
[7]: /src/project_metadata.sh
184219
[8]: /src/history.sh
185-
[9]: /src/commands.sh
220+
[9]: /src/commands/
186221

187222
Across the giv-CLI tool, there are five primary **data domains**—each holding specific values—and the `document` subcommand orchestrates several modules in a well-defined call sequence. Below is a data-structure diagram showing the domains and their key contents, then a detailed sequence diagram illustrating exactly how `giv document` runs under the hood.
188223

@@ -250,7 +285,7 @@ sequenceDiagram
250285
participant args.sh
251286
participant system.sh
252287
participant history.sh
253-
participant project.sh
288+
participant project_metadata.sh
254289
participant llm.sh
255290
participant markdown.sh
256291
participant OutputFile as "DOCUMENT.md"
@@ -265,10 +300,10 @@ sequenceDiagram
265300
266301
giv.sh->>history.sh: summarize_target("v1.2.0..HEAD")
267302
history.sh->>history.sh: build_history("v1.2.0..HEAD")
268-
history.sh->>project.sh: get_project_title()
269-
project.sh-->>history.sh: "My Project"
270-
history.sh->>project.sh: get_version_info("v1.2.0")
271-
project.sh-->>history.sh: "1.2.0"
303+
history.sh->>project_metadata.sh: get_project_title()
304+
project_metadata.sh-->>history.sh: "My Project"
305+
history.sh->>project_metadata.sh: get_version_info("v1.2.0")
306+
project_metadata.sh-->>history.sh: "1.2.0"
272307
273308
history.sh->>llm.sh: build_prompt(templates/document_prompt.md,history.md,title, version)
274309
llm.sh-->>history.sh: fullPrompt
@@ -282,9 +317,9 @@ sequenceDiagram
282317
giv.sh->>OutputFile: display("DOCUMENT.md")
283318
```
284319

285-
1. **Initialization** (once): creates `.giv/` directories.
320+
1. **Initialization** (once): creates `.giv/` directory.
286321
2. **Argument Parsing**: `args.sh` sets up global vars (prompt file, revision range).
287-
3. **History Extraction**: `history.sh` builds a unified history for the given range, invoking `metadata.sh` for title/version.
322+
3. **History Extraction**: `history.sh` builds a unified history for the given range, invoking `project_metadata.sh` for title/version.
288323
4. **Prompt Assembly**: `llm.sh` merges the template, history, and metadata into a single prompt.
289324
5. **AI Generation**: same module calls out to remote/local LLM, returns the document text.
290325
6. **Output**: `markdown.sh` writes the result to `DOCUMENT.md` and the CLI presents it.

0 commit comments

Comments
 (0)