...
This commit is contained in:
11
README.md
11
README.md
@@ -72,6 +72,17 @@ Herolib provides a wide range of functionality:
|
||||
|
||||
- Cloud automation tools
|
||||
- Git operations and management
|
||||
### Offline Mode for Git Operations
|
||||
|
||||
Herolib now supports an `offline` mode for Git operations, which prevents automatic fetching from remote repositories. This can be useful in environments with limited or no internet connectivity, or when you want to avoid network calls during development or testing.
|
||||
|
||||
To enable offline mode:
|
||||
|
||||
- **Via `GitStructureConfig`**: Set the `offline` field to `true` in the `GitStructureConfig` struct.
|
||||
- **Via `GitStructureArgsNew`**: When creating a new `GitStructure` instance using `gittools.new()`, set the `offline` parameter to `true`.
|
||||
- **Via Environment Variable**: Set the `OFFLINE` environment variable to any value (e.g., `export OFFLINE=true`).
|
||||
|
||||
When offline mode is active, `git fetch --all` operations will be skipped, and a debug message "fetch skipped (offline)" will be printed.
|
||||
- Documentation building
|
||||
- Hero AI integration
|
||||
- System management utilities
|
||||
|
||||
@@ -192,17 +192,17 @@ pub fn path_fix(path_ string) string {
|
||||
if starts_with_dot_slash {
|
||||
result_components << '.'
|
||||
// Skip the first component which is '.'
|
||||
components = components[1..]
|
||||
components = components[1..].clone()
|
||||
} else if starts_with_dot_dot_slash {
|
||||
result_components << '..'
|
||||
// Skip the first component which is '..'
|
||||
components = components[1..]
|
||||
components = components[1..].clone()
|
||||
} else if is_absolute {
|
||||
// Keep the empty component for absolute paths
|
||||
result_components << ''
|
||||
// Skip the first empty component
|
||||
if components.len > 0 && components[0] == '' {
|
||||
components = components[1..]
|
||||
components = components[1..].clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ pub mut:
|
||||
ssh_key_name string // name of ssh key to be used when loading the gitstructure
|
||||
ssh_key_path string
|
||||
reload bool
|
||||
offline bool = false
|
||||
}
|
||||
|
||||
// Retrieve or create a new GitStructure instance with the given configuration.
|
||||
@@ -37,6 +38,7 @@ pub fn new(args_ GitStructureArgsNew) !&GitStructure {
|
||||
debug: args.debug
|
||||
ssh_key_name: args.ssh_key_name
|
||||
ssh_key_path: args.ssh_key_path
|
||||
offline: args.offline
|
||||
}
|
||||
|
||||
return get(coderoot: args.coderoot, reload: args.reload, cfg: cfg)
|
||||
|
||||
@@ -15,6 +15,7 @@ pub mut:
|
||||
debug bool = true
|
||||
ssh_key_name string
|
||||
ssh_key_path string
|
||||
offline bool = false
|
||||
}
|
||||
|
||||
// GitStructure holds information about repositories within a specific code root.
|
||||
@@ -53,13 +54,13 @@ pub fn (mut gitstructure GitStructure) load(reload bool) ! {
|
||||
redisclient.checkempty()
|
||||
|
||||
for _, mut repo in gitstructure.repos {
|
||||
// mut myfunction := fn (mut repo GitRepo) ! {
|
||||
// }
|
||||
// ths << spawn myfunction(mut repo_)
|
||||
repo.status_update(reload: reload) or {
|
||||
msg := 'Error in git repo: ${repo.path()}\n${err}'
|
||||
console.print_stderr(msg)
|
||||
return error(msg)
|
||||
// If status_update fails, the error is already captured within the repo object.
|
||||
// We log it here and continue to process other repositories.
|
||||
console.print_stderr('Error updating status for repo ${repo.path()}: ${err}')
|
||||
// Ensure last_load is reset to 0 if there was an error, so it's re-checked next time.
|
||||
repo.last_load = 0
|
||||
repo.cache_set()! // Persist the updated last_load and error state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module gittools
|
||||
|
||||
import freeflowuniverse.herolib.core.redisclient
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import time
|
||||
|
||||
// ReposGetArgs defines arguments to retrieve repositories from the git structure.
|
||||
@@ -61,7 +62,13 @@ pub fn (mut gitstructure GitStructure) get_repos(args_ ReposGetArgs) ![]&GitRepo
|
||||
repo.cache_last_load_clear()!
|
||||
}
|
||||
if args.status_update {
|
||||
repo.status_update()!
|
||||
repo.status_update() or {
|
||||
// Log the error but continue processing other repositories
|
||||
console.print_stderr('Error updating status for repo ${repo.path()}: ${err}')
|
||||
// Ensure last_load is reset to 0 if there was an error, so it's re-checked next time.
|
||||
repo.last_load = 0
|
||||
repo.cache_set()! // Persist the updated last_load and error state
|
||||
}
|
||||
}
|
||||
if args.reset {
|
||||
repo.reset()!
|
||||
|
||||
@@ -7,6 +7,21 @@ fn get_repo_status(gr GitRepo) !string {
|
||||
mut repo := gr
|
||||
mut statuses := []string{}
|
||||
|
||||
if repo.status_local.error.len > 0 {
|
||||
mut err_msg := repo.status_local.error
|
||||
if err_msg.len > 40 {
|
||||
err_msg = err_msg[0..40] + '...'
|
||||
}
|
||||
statuses << 'ERROR (Local): ${err_msg}'
|
||||
}
|
||||
if repo.status_remote.error.len > 0 {
|
||||
mut err_msg := repo.status_remote.error
|
||||
if err_msg.len > 40 {
|
||||
err_msg = err_msg[0..40] + '...'
|
||||
}
|
||||
statuses << 'ERROR (Remote): ${err_msg}'
|
||||
}
|
||||
|
||||
if repo.has_changes {
|
||||
statuses << 'COMMIT'
|
||||
}
|
||||
@@ -38,32 +53,22 @@ fn format_repo_info(repo GitRepo) ![]string {
|
||||
|
||||
// Print repositories based on the provided criteria, showing their statuses
|
||||
pub fn (mut gitstructure GitStructure) repos_print(args ReposGetArgs) ! {
|
||||
// console.print_debug('#### Overview of repositories:')
|
||||
// console.print_debug('')
|
||||
|
||||
mut repo_data := [][]string{}
|
||||
|
||||
// Collect repository information based on the provided criteria
|
||||
for _, repo in gitstructure.get_repos(args)! {
|
||||
// repo.status_update()!
|
||||
repo_data << format_repo_info(repo)!
|
||||
}
|
||||
|
||||
// Clear the console and start printing the formatted repository information
|
||||
console.clear()
|
||||
console.print_lf(1)
|
||||
// console.print_lf(1) // Removed to reduce newlines
|
||||
|
||||
// Display header with optional argument filtering information
|
||||
// header := if args.str().len > 0 {
|
||||
// 'Repositories: ${gitstructure.config()!.coderoot} [${args.str()}]'
|
||||
// } else {
|
||||
// 'Repositories: ${gitstructure.config()!.coderoot}'
|
||||
// }
|
||||
header := 'Repositories: ${gitstructure.config()!.coderoot}'
|
||||
console.print_header(header)
|
||||
console.print_lf(1) // Keep one newline after header
|
||||
|
||||
// Print the repository information in a formatted array
|
||||
console.print_lf(1)
|
||||
console.print_array(repo_data, ' ', true) // true -> aligned for better readability
|
||||
console.print_lf(5)
|
||||
// console.print_lf(5) // Removed to reduce newlines
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ pub mut:
|
||||
ref_default string // is the default branch hash
|
||||
branches map[string]string // Branch name -> commit hash
|
||||
tags map[string]string // Tag name -> commit hash
|
||||
error string // Error message if remote status update fails
|
||||
}
|
||||
|
||||
// GitRepoStatusLocal holds local status information for a repository.
|
||||
@@ -44,6 +45,7 @@ pub mut:
|
||||
branches map[string]string // Branch name -> commit hash
|
||||
branch string // the current branch
|
||||
tag string // If the local branch is not set, the tag may be set
|
||||
error string // Error message if local status update fails
|
||||
}
|
||||
|
||||
// GitRepoConfig holds repository-specific configuration options.
|
||||
|
||||
@@ -7,13 +7,33 @@ import os
|
||||
@[params]
|
||||
pub struct StatusUpdateArgs {
|
||||
reload bool
|
||||
force bool // Add force flag to bypass cache when callers need it.
|
||||
}
|
||||
|
||||
pub fn (mut repo GitRepo) status_update(args StatusUpdateArgs) ! {
|
||||
// Clear previous errors
|
||||
repo.status_local.error = ''
|
||||
repo.status_remote.error = ''
|
||||
|
||||
// Check current time vs last check, if needed (check period) then load
|
||||
repo.cache_get() or { return error('Failed to get cache for repo ${repo.name}: ${err}') } // Ensure we have the situation from redis
|
||||
repo.init() or { return error('Failed to initialize repo ${repo.name}: ${err}') }
|
||||
if 'OFFLINE' !in os.environ() {
|
||||
repo.cache_get() or {
|
||||
repo.status_local.error = 'Failed to get cache: ${err}'
|
||||
return error('Failed to get cache for repo ${repo.name}: ${err}')
|
||||
} // Ensure we have the situation from redis
|
||||
repo.init() or {
|
||||
repo.status_local.error = 'Failed to initialize: ${err}'
|
||||
return error('Failed to initialize repo ${repo.name}: ${err}')
|
||||
}
|
||||
|
||||
// If there's an existing error, skip loading and just return.
|
||||
// This prevents repeated attempts to load a problematic repo.
|
||||
if repo.status_local.error.len > 0 || repo.status_remote.error.len > 0 {
|
||||
console.print_debug('Skipping load for ${repo.name} due to existing error.')
|
||||
return
|
||||
}
|
||||
|
||||
if 'OFFLINE' in os.environ() || (repo.gs.config()!.offline) {
|
||||
console.print_debug('fetch skipped (offline)')
|
||||
return
|
||||
}
|
||||
current_time := int(time.now().unix())
|
||||
@@ -21,7 +41,10 @@ pub fn (mut repo GitRepo) status_update(args StatusUpdateArgs) ! {
|
||||
|| current_time - repo.last_load >= repo.config.remote_check_period {
|
||||
// console.print_debug('${repo.name} ${current_time}-${repo.last_load} (${current_time - repo.last_load >= repo.config.remote_check_period}): ${repo.config.remote_check_period} +++')
|
||||
// if true{exit(0)}
|
||||
repo.load() or { return error('Failed to load repository ${repo.name}: ${err}') }
|
||||
repo.load() or {
|
||||
repo.status_remote.error = 'Failed to load repository: ${err}'
|
||||
return error('Failed to load repository ${repo.name}: ${err}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,27 +52,40 @@ pub fn (mut repo GitRepo) status_update(args StatusUpdateArgs) ! {
|
||||
// Does not check cache, it is the callers responsibility to check cache and load accordingly.
|
||||
fn (mut repo GitRepo) load() ! {
|
||||
console.print_header('load ${repo.print_key()}')
|
||||
repo.init() or { return error('Failed to initialize repo during load operation: ${err}') }
|
||||
repo.init() or {
|
||||
repo.status_local.error = 'Failed to initialize repo during load operation: ${err}'
|
||||
return error('Failed to initialize repo during load operation: ${err}')
|
||||
}
|
||||
|
||||
git_path := '${repo.path()}/.git'
|
||||
if os.exists(git_path) == false {
|
||||
repo.status_local.error = 'Repository not found: missing .git directory'
|
||||
return error('Repository not found: ${repo.path()} is not a valid git repository (missing .git directory)')
|
||||
}
|
||||
|
||||
repo.exec('git fetch --all') or {
|
||||
repo.status_remote.error = 'Failed to fetch updates: ${err}'
|
||||
return error('Failed to fetch updates for ${repo.name} at ${repo.path()}: ${err}. Please check network connection and repository access.')
|
||||
}
|
||||
|
||||
repo.load_branches() or { return error('Failed to load branches for ${repo.name}: ${err}') }
|
||||
repo.load_branches() or {
|
||||
repo.status_remote.error = 'Failed to load branches: ${err}'
|
||||
return error('Failed to load branches for ${repo.name}: ${err}')
|
||||
}
|
||||
|
||||
repo.load_tags() or { return error('Failed to load tags for ${repo.name}: ${err}') }
|
||||
repo.load_tags() or {
|
||||
repo.status_remote.error = 'Failed to load tags: ${err}'
|
||||
return error('Failed to load tags for ${repo.name}: ${err}')
|
||||
}
|
||||
|
||||
repo.last_load = int(time.now().unix())
|
||||
|
||||
repo.has_changes = repo.detect_changes() or {
|
||||
repo.status_local.error = 'Failed to detect changes: ${err}'
|
||||
return error('Failed to detect changes in repository ${repo.name}: ${err}')
|
||||
}
|
||||
repo.cache_set() or {
|
||||
repo.status_local.error = 'Failed to update cache: ${err}'
|
||||
return error('Failed to update cache for repository ${repo.name}: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user