Added features
Added: Project Overview TUI Build Cache / Dirty Detection Post-Build Hooks gbuild status
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
#ifndef CACHE_H
|
||||
#define CACHE_H
|
||||
|
||||
/*
|
||||
* Build cache — per-project dirty detection.
|
||||
*
|
||||
* A small dotfile (.gbuild_cache) is kept inside each cloned repo dir.
|
||||
* It stores the git HEAD hash that was current at the last *successful*
|
||||
* build. On the next run gbuild compares the live HEAD to the cached
|
||||
* one; if they match the build is skipped unless --force was passed.
|
||||
*
|
||||
* Format of .gbuild_cache (plain text, one line):
|
||||
* <40-char sha1>\n
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Maximum length of a stored hash (SHA-1 hex + NUL). */
|
||||
#define CACHE_HASH_LEN 64
|
||||
|
||||
/*
|
||||
* Read the cached hash for the repo at repo_path into out (>= CACHE_HASH_LEN
|
||||
* bytes). Returns 0 on success, -1 if the file does not exist or is
|
||||
* unreadable (out is set to an empty string in that case).
|
||||
*/
|
||||
int cache_read(const char *repo_path, char *out, size_t n);
|
||||
|
||||
/*
|
||||
* Write hash as the new cached HEAD for the repo at repo_path.
|
||||
* Creates or overwrites .gbuild_cache inside repo_path.
|
||||
* Returns 0 on success, -1 on error.
|
||||
*/
|
||||
int cache_write(const char *repo_path, const char *hash);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
@@ -6,6 +6,18 @@
|
||||
|
||||
#define CFG_MAX 512
|
||||
|
||||
/*
|
||||
* Per-project hook override loaded from a [project:<name>] section.
|
||||
* Up to CFG_MAX_PROJECT_OVERRIDES overrides are supported.
|
||||
*/
|
||||
#define CFG_MAX_PROJECT_OVERRIDES 64
|
||||
#define CFG_PROJECT_NAME_LEN 128
|
||||
|
||||
typedef struct {
|
||||
char name[CFG_PROJECT_NAME_LEN]; /* project name, e.g. "myproject" */
|
||||
char post_build_hook[CFG_MAX]; /* hook command for this project */
|
||||
} GProjectOverride;
|
||||
|
||||
typedef struct {
|
||||
char git_url[CFG_MAX];
|
||||
char git_user[256];
|
||||
@@ -15,8 +27,24 @@ typedef struct {
|
||||
char clone_dir[CFG_MAX];
|
||||
bool log_enabled;
|
||||
char log_dir[CFG_MAX];
|
||||
|
||||
/* Post-build hook run after every successful make invocation.
|
||||
* The hook is executed with the repo directory as its CWD.
|
||||
* An empty string means "no hook". */
|
||||
char post_build_hook[CFG_MAX];
|
||||
|
||||
/* Per-project hook overrides from [project:<name>] sections. */
|
||||
GProjectOverride project_overrides[CFG_MAX_PROJECT_OVERRIDES];
|
||||
int project_override_count;
|
||||
} GConfig;
|
||||
|
||||
/*
|
||||
* Return the effective post-build hook for a given project name.
|
||||
* Checks [project:<name>] overrides first; falls back to the global hook.
|
||||
* Returns a pointer into cfg — do not free.
|
||||
*/
|
||||
const char *config_hook_for(const GConfig *cfg, const char *project);
|
||||
|
||||
void config_defaults(GConfig *cfg);
|
||||
int config_load(GConfig *cfg, const char *path);
|
||||
int config_save(const GConfig *cfg, const char *path);
|
||||
|
||||
@@ -15,4 +15,20 @@ int git_clone(const char *base_url, const char *user,
|
||||
/* Pull latest in repo_path */
|
||||
int git_pull(const char *repo_path, Logger *log);
|
||||
|
||||
/* Read the current HEAD hash of repo_path into out (at least 41 bytes).
|
||||
* Returns 0 on success, -1 on failure. */
|
||||
int git_head_hash(const char *repo_path, char *out, size_t n);
|
||||
|
||||
/* Write the current branch name into out.
|
||||
* Returns 0 on success, -1 on failure (detached HEAD writes "(detached)"). */
|
||||
int git_current_branch(const char *repo_path, char *out, size_t n);
|
||||
|
||||
/* Returns 1 if the working tree has uncommitted changes, 0 if clean,
|
||||
* -1 on error. Does NOT stage anything. */
|
||||
int git_is_dirty(const char *repo_path);
|
||||
|
||||
/* Silently fetches from origin then returns the number of commits
|
||||
* the local branch is behind its upstream, or -1 on error. */
|
||||
int git_behind_count(const char *repo_path);
|
||||
|
||||
#endif /* GIT_OPS_H */
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef HOOKS_H
|
||||
#define HOOKS_H
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
/*
|
||||
* Run cmd in a shell with working directory set to working_dir.
|
||||
* stdout and stderr from the hook are streamed through log.
|
||||
*
|
||||
* Returns the hook's exit status (0 = success, >0 = hook failure),
|
||||
* or -1 if the shell could not be launched.
|
||||
*
|
||||
* The caller is responsible for distinguishing hook failure from build
|
||||
* failure — this function never touches build state.
|
||||
*/
|
||||
int hook_run(const char *cmd, const char *working_dir, Logger *log);
|
||||
|
||||
#endif /* HOOKS_H */
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef INDEX_H
|
||||
#define INDEX_H
|
||||
|
||||
#include <time.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define IDX_MAX_PROJECTS 256
|
||||
#define IDX_NAME_LEN 128
|
||||
#define IDX_HASH_LEN 64
|
||||
#define IDX_PATH_LEN 512
|
||||
|
||||
/*
|
||||
* Per-project record stored in ~/.gbuild_index.
|
||||
*
|
||||
* File format (INI-style, one section per project):
|
||||
*
|
||||
* [myproject]
|
||||
* last_build_rc = 0
|
||||
* last_build_ts = 1716000000
|
||||
* last_head_hash = abc123def456
|
||||
*/
|
||||
typedef struct {
|
||||
char name[IDX_NAME_LEN];
|
||||
int last_build_rc; /* exit code of last make_build(); -1 = never built */
|
||||
time_t last_build_ts; /* unix timestamp of last build attempt */
|
||||
char last_head_hash[IDX_HASH_LEN]; /* git HEAD hash at last build */
|
||||
} ProjectRecord;
|
||||
|
||||
typedef struct {
|
||||
ProjectRecord projects[IDX_MAX_PROJECTS];
|
||||
int count;
|
||||
} ProjectIndex;
|
||||
|
||||
/* Load ~/.gbuild_index into idx. Returns 0 on success, -1 if not found
|
||||
* (idx is still initialised to an empty index). */
|
||||
int index_load(ProjectIndex *idx, const char *path);
|
||||
|
||||
/* Persist idx to path, creating or overwriting the file. */
|
||||
int index_save(const ProjectIndex *idx, const char *path);
|
||||
|
||||
/* Find a record by name. Returns a pointer into idx->projects, or NULL. */
|
||||
ProjectRecord *index_find(ProjectIndex *idx, const char *name);
|
||||
|
||||
/* Find or create a record by name. Returns NULL only if the index is full. */
|
||||
ProjectRecord *index_upsert(ProjectIndex *idx, const char *name);
|
||||
|
||||
/* Walk clone_dir, find every subdir that contains .git, and upsert each one
|
||||
* into idx. Does NOT overwrite existing build metadata — only adds new
|
||||
* entries for projects that are not yet tracked.
|
||||
* Returns the number of new entries added. */
|
||||
int index_scan(ProjectIndex *idx, const char *clone_dir);
|
||||
|
||||
/* Canonical path for the index file (~/.gbuild_index). */
|
||||
void index_get_path(char *out, size_t n);
|
||||
|
||||
/* ---------------------------------------------------------------- status scan */
|
||||
|
||||
/*
|
||||
* Per-project status gathered by project_scan_all().
|
||||
* All string fields are NUL-terminated; numeric fields are -1 when unknown.
|
||||
*/
|
||||
typedef struct {
|
||||
char name[IDX_NAME_LEN];
|
||||
char branch[128]; /* current branch / "(detached:HASH)" */
|
||||
int behind; /* commits behind upstream; -1 = no upstream/error */
|
||||
int dirty; /* 1 = dirty, 0 = clean, -1 = error */
|
||||
int last_build_rc; /* from index; -1 = never built */
|
||||
time_t last_build_ts; /* from index; 0 = never built */
|
||||
} StatusResult;
|
||||
|
||||
/*
|
||||
* Scan every subdirectory of clone_dir that contains .git.
|
||||
* For each one, fetch the three git metrics and join them with build
|
||||
* metadata from idx. Results are written into results[0..max-1].
|
||||
* Worker threads run the git queries in parallel (pool of SCAN_THREADS).
|
||||
* Returns the number of projects found (may be 0, capped at max).
|
||||
*/
|
||||
#define SCAN_THREADS 6
|
||||
int project_scan_all(const char *clone_dir, const ProjectIndex *idx,
|
||||
StatusResult *results, int max);
|
||||
|
||||
#endif /* INDEX_H */
|
||||
@@ -13,4 +13,17 @@ int tui_pick_target(const MakeTargets *targets, char *selected, size_t sel_size
|
||||
* Keys: ↑↓ navigate Enter open in less d delete q quit */
|
||||
void tui_log_browser(const char *log_dir);
|
||||
|
||||
#include "index.h"
|
||||
|
||||
/* Full-screen project overview.
|
||||
* Lists all projects in idx with last-build status, timestamp and HEAD hash.
|
||||
* On Enter, fills selected_project and returns the row index.
|
||||
* Returns -1 if the user quit without selecting.
|
||||
* Keys: ↑↓ navigate Enter build l log-browser q/ESC quit */
|
||||
int tui_project_overview(ProjectIndex *idx,
|
||||
const char *clone_dir,
|
||||
const char *log_dir,
|
||||
char *selected_project,
|
||||
size_t sel_size);
|
||||
|
||||
#endif /* TUI_H */
|
||||
|
||||
Reference in New Issue
Block a user