ci: Preparing the repo to go public

This commit is contained in:
2025-09-11 17:19:52 -06:00
parent 1849e11063
commit ca990f460e
31 changed files with 1229 additions and 46 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::command::preview_command;
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result, anyhow};
use gman::config::{Config, ProviderConfig, RunConfig};
use gman::providers::SecretProvider;
use heck::ToSnakeCase;
+2 -2
View File
@@ -1,12 +1,12 @@
use clap::{
crate_authors, crate_description, crate_name, crate_version, CommandFactory, Parser, ValueEnum,
CommandFactory, Parser, ValueEnum, crate_authors, crate_description, crate_name, crate_version,
};
use std::ffi::OsString;
use anyhow::{Context, Result};
use clap::Subcommand;
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, LeaveAlternateScreen};
use crossterm::terminal::{LeaveAlternateScreen, disable_raw_mode};
use gman::config::load_config;
use heck::ToSnakeCase;
use std::io::{self, IsTerminal, Read, Write};
+18 -14
View File
@@ -25,7 +25,7 @@ use anyhow::Result;
use log::debug;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde_with::{skip_serializing_none, DisplayFromStr};
use serde_with::{DisplayFromStr, skip_serializing_none};
use std::borrow::Cow;
use std::path::PathBuf;
use validator::{Validate, ValidationError};
@@ -188,19 +188,23 @@ pub struct Config {
}
fn default_provider_exists(config: &Config) -> Result<(), ValidationError> {
if let Some(default) = &config.default_provider {
if config.providers.iter().any(|p| p.name.as_deref() == Some(default)) {
Ok(())
} else {
let mut err = ValidationError::new("default_provider_missing");
err.message = Some(Cow::Borrowed(
"The default_provider does not match any configured provider names",
));
Err(err)
}
} else {
Ok(())
}
if let Some(default) = &config.default_provider {
if config
.providers
.iter()
.any(|p| p.name.as_deref() == Some(default))
{
Ok(())
} else {
let mut err = ValidationError::new("default_provider_missing");
err.message = Some(Cow::Borrowed(
"The default_provider does not match any configured provider names",
));
Err(err)
}
} else {
Ok(())
}
}
impl Default for Config {
+4 -4
View File
@@ -20,15 +20,15 @@
//! The `config` and `providers` modules power the CLI. They can be embedded
//! in other programs, but many functions interact with the user or the
//! filesystem. Prefer `no_run` doctests for those.
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{Context, Result, anyhow, bail};
use argon2::{
password_hash::{rand_core::RngCore, SaltString},
Algorithm, Argon2, Params, Version,
password_hash::{SaltString, rand_core::RngCore},
};
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use base64::{Engine as _, engine::general_purpose::STANDARD as B64};
use chacha20poly1305::{
aead::{Aead, KeyInit, OsRng},
Key, XChaCha20Poly1305, XNonce,
aead::{Aead, KeyInit, OsRng},
};
use secrecy::{ExposeSecret, SecretString};
use zeroize::Zeroize;
+25 -14
View File
@@ -4,9 +4,9 @@ use dialoguer::Confirm;
use dialoguer::theme::ColorfulTheme;
use indoc::formatdoc;
use log::debug;
use std::{env, fs};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::{env, fs};
use validator::Validate;
#[derive(Debug, Validate, Clone)]
@@ -41,11 +41,17 @@ pub fn sync_and_push(opts: &SyncOpts<'_>) -> Result<()> {
.with_context(|| "get default vault path")?;
let repo_vault = repo_dir.join("vault.yml");
if default_vault.exists() && !repo_vault.exists() {
fs::rename(&default_vault, &repo_vault)
.with_context(|| format!("move {} -> {}", default_vault.display(), repo_vault.display()))?;
fs::rename(&default_vault, &repo_vault).with_context(|| {
format!(
"move {} -> {}",
default_vault.display(),
repo_vault.display()
)
})?;
} else if !repo_vault.exists() {
// Ensure an empty vault exists to allow initial commits
fs::write(&repo_vault, "{}\n").with_context(|| format!("create {}", repo_vault.display()))?;
fs::write(&repo_vault, "{}\n")
.with_context(|| format!("create {}", repo_vault.display()))?;
}
let git = resolve_git(opts.git_executable.as_ref())?;
@@ -250,7 +256,7 @@ fn stage_vault_only(git: &Path, repo: &Path) -> Result<()> {
fn fetch_and_pull(git: &Path, repo: &Path, branch: &str) -> Result<()> {
// Fetch all refs from origin (safe even if branch doesn't exist remotely)
run_git(git, repo, &["fetch", "origin", "--prune"])
run_git(git, repo, &["fetch", "origin", "--prune"])
.with_context(|| "Failed to fetch changes from remote")?;
let origin_ref = format!("origin/{branch}");
@@ -265,19 +271,16 @@ fn fetch_and_pull(git: &Path, repo: &Path, branch: &str) -> Result<()> {
.with_context(|| "Failed to checkout remote branch over local state")?;
run_git(git, repo, &["reset", "--hard", &origin_ref])
.with_context(|| "Failed to hard reset to remote branch")?;
run_git(git, repo, &["clean", "-fd"]).with_context(|| "Failed to clean untracked files")?;
run_git(git, repo, &["clean", "-fd"])
.with_context(|| "Failed to clean untracked files")?;
}
return Ok(());
}
// If we have local history and the remote branch exists, fast-forward.
if remote_has_branch {
run_git(
git,
repo,
&["merge", "--ff-only", &origin_ref],
)
.with_context(|| "Failed to merge remote changes")?;
run_git(git, repo, &["merge", "--ff-only", &origin_ref])
.with_context(|| "Failed to merge remote changes")?;
}
Ok(())
}
@@ -286,7 +289,12 @@ fn has_remote_branch(git: &Path, repo: &Path, branch: &str) -> bool {
Command::new(git)
.arg("-C")
.arg(repo)
.args(["show-ref", "--verify", "--quiet", &format!("refs/remotes/origin/{}", branch)])
.args([
"show-ref",
"--verify",
"--quiet",
&format!("refs/remotes/origin/{}", branch),
])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
@@ -399,7 +407,10 @@ mod tests {
#[test]
fn test_repo_name_from_url() {
assert_eq!(repo_name_from_url("git@github.com:user/vault.git"), "vault");
assert_eq!(repo_name_from_url("https://github.com/user/test-vault.git"), "test-vault");
assert_eq!(
repo_name_from_url("https://github.com/user/test-vault.git"),
"test-vault"
);
assert_eq!(repo_name_from_url("ssh://git@example.com/x/y/z.git"), "z");
assert_eq!(repo_name_from_url("git@example.com:ns/repo"), "repo");
}
+5 -5
View File
@@ -1,4 +1,4 @@
use anyhow::{anyhow, bail, Context};
use anyhow::{Context, anyhow, bail};
use secrecy::{ExposeSecret, SecretString};
use std::collections::HashMap;
use std::fs;
@@ -6,20 +6,20 @@ use std::path::{Path, PathBuf};
use zeroize::Zeroize;
use crate::config::ProviderConfig;
use crate::providers::git_sync::{repo_name_from_url, sync_and_push, SyncOpts};
use crate::providers::SecretProvider;
use crate::providers::git_sync::{SyncOpts, repo_name_from_url, sync_and_push};
use crate::{
ARGON_M_COST_KIB, ARGON_P, ARGON_T_COST, HEADER, KDF, KEY_LEN, NONCE_LEN, SALT_LEN, VERSION,
};
use anyhow::Result;
use argon2::{Algorithm, Argon2, Params, Version};
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use base64::{Engine as _, engine::general_purpose::STANDARD as B64};
use chacha20poly1305::aead::rand_core::RngCore;
use chacha20poly1305::{
aead::{Aead, KeyInit, OsRng},
Key, XChaCha20Poly1305, XNonce,
aead::{Aead, KeyInit, OsRng},
};
use dialoguer::{theme, Input};
use dialoguer::{Input, theme};
use log::{debug, error};
use serde::Deserialize;
use theme::ColorfulTheme;
+1 -1
View File
@@ -16,7 +16,7 @@ pub mod local;
use crate::config::ProviderConfig;
use crate::providers::local::LocalProvider;
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use serde::Deserialize;
use std::fmt::{Display, Formatter};
use std::str::FromStr;