feat(config): use specified gitdir when supplied
All checks were successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
All checks were successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
The user can specify a gitdir for a repo in the `git-next-server.toml` file. When they do, then we should use that directory. Closes kemitix/git-next#71
This commit is contained in:
parent
bb67b7c66d
commit
31ef0c19fb
5 changed files with 90 additions and 58 deletions
|
@ -21,7 +21,15 @@ pub struct RepoConfigUnknownError(pub network::StatusCode);
|
|||
pub async fn load(
|
||||
details: &RepoDetails,
|
||||
forge: &gitforge::Forge,
|
||||
) -> Result<RepoConfig, OneOf<(ForgeFileError, toml::de::Error, RepoConfigValidationErrors)>> {
|
||||
) -> Result<
|
||||
RepoConfig,
|
||||
OneOf<(
|
||||
ForgeFileError,
|
||||
crate::server::config::Error,
|
||||
toml::de::Error,
|
||||
RepoConfigValidationErrors,
|
||||
)>,
|
||||
> {
|
||||
let contents = forge
|
||||
.file_contents_get(&details.branch, ".git-next.toml")
|
||||
.await
|
||||
|
|
|
@ -17,6 +17,9 @@ pub enum Error {
|
|||
KxIoFs(kxio::fs::Error),
|
||||
TomlDe(toml::de::Error),
|
||||
}
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
/// Mapped from the `git-next-server.toml` file
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||
|
@ -26,7 +29,7 @@ pub struct ServerConfig {
|
|||
forge: HashMap<String, ForgeConfig>,
|
||||
}
|
||||
impl ServerConfig {
|
||||
pub(crate) fn load(fs: &FileSystem) -> Result<Self, Error> {
|
||||
pub(crate) fn load(fs: &FileSystem) -> Result<Self> {
|
||||
let str = fs.file_read_to_string(&fs.base().join("git-next-server.toml"))?;
|
||||
toml::from_str(&str).map_err(Into::into)
|
||||
}
|
||||
|
@ -90,8 +93,8 @@ impl RepoConfig {
|
|||
Self { branches }
|
||||
}
|
||||
// #[cfg(test)]
|
||||
pub fn load(toml: &str) -> Result<Self, toml::de::Error> {
|
||||
toml::from_str(toml)
|
||||
pub fn load(toml: &str) -> Result<Self> {
|
||||
toml::from_str(toml).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub const fn branches(&self) -> &RepoBranches {
|
||||
|
@ -186,7 +189,7 @@ impl Display for ForgeConfig {
|
|||
pub struct ServerRepoConfig {
|
||||
repo: String,
|
||||
branch: String,
|
||||
gitdir: Option<PathBuf>, // TODO: (#71) use this when supplied
|
||||
gitdir: Option<PathBuf>,
|
||||
main: Option<String>,
|
||||
next: Option<String>,
|
||||
dev: Option<String>,
|
||||
|
@ -352,18 +355,21 @@ impl RepoDetails {
|
|||
forge_name: &ForgeName,
|
||||
forge_config: &ForgeConfig,
|
||||
server_storage: &ServerStorage,
|
||||
) -> Self {
|
||||
Self {
|
||||
) -> Result<Self> {
|
||||
let path_buf = server_repo_config.gitdir.clone().unwrap_or_else(|| {
|
||||
server_storage
|
||||
.path
|
||||
.join(forge_name.to_string())
|
||||
.join(name.to_string())
|
||||
});
|
||||
let path_buf = std::fs::canonicalize(path_buf)?;
|
||||
let gitdir = GitDir(path_buf);
|
||||
Ok(Self {
|
||||
repo_alias: name.clone(),
|
||||
repo_path: RepoPath(server_repo_config.repo.clone()),
|
||||
repo_config: server_repo_config.repo_config(),
|
||||
branch: BranchName(server_repo_config.branch.clone()),
|
||||
gitdir: GitDir(
|
||||
server_storage
|
||||
.path
|
||||
.join(forge_name.to_string())
|
||||
.join(name.to_string()),
|
||||
),
|
||||
gitdir,
|
||||
forge: ForgeDetails {
|
||||
forge_name: forge_name.clone(),
|
||||
forge_type: forge_config.forge_type.clone(),
|
||||
|
@ -371,7 +377,7 @@ impl RepoDetails {
|
|||
user: forge_config.user(),
|
||||
token: forge_config.token(),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn origin(&self) -> secrecy::Secret<String> {
|
||||
let repo_details = self;
|
||||
|
@ -386,11 +392,11 @@ impl RepoDetails {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn validate_repo(&self) -> Result<(), RepoValidationError> {
|
||||
pub fn validate_repo(&self) -> ValidationResult<()> {
|
||||
self.gitdir.validate(self)
|
||||
}
|
||||
|
||||
pub fn find_default_push_remote(&self) -> Result<GitRemote, RepoValidationError> {
|
||||
pub fn find_default_push_remote(&self) -> ValidationResult<GitRemote> {
|
||||
let repository = gix::open(self.gitdir.clone())
|
||||
.map_err(|e| RepoValidationError::UnableToOpenRepo(e.to_string()))?;
|
||||
let Some(Ok(remote)) = repository.find_default_remote(gix::remote::Direction::Push) else {
|
||||
|
@ -431,6 +437,7 @@ impl Display for RepoDetails {
|
|||
}
|
||||
}
|
||||
|
||||
type ValidationResult<T> = core::result::Result<T, RepoValidationError>;
|
||||
#[derive(Debug)]
|
||||
pub enum RepoValidationError {
|
||||
NoDefaultPushRemote,
|
||||
|
@ -443,6 +450,7 @@ pub enum RepoValidationError {
|
|||
configured: GitRemote,
|
||||
},
|
||||
}
|
||||
impl std::error::Error for RepoValidationError {}
|
||||
impl Display for RepoValidationError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -484,12 +492,12 @@ impl GitDir {
|
|||
pub(crate) fn new(pathbuf: &std::path::Path) -> Self {
|
||||
Self(pathbuf.to_path_buf())
|
||||
}
|
||||
#[allow(dead_code)] // TODO:
|
||||
|
||||
pub const fn pathbuf(&self) -> &PathBuf {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn validate(&self, repo_details: &RepoDetails) -> Result<(), RepoValidationError> {
|
||||
pub fn validate(&self, repo_details: &RepoDetails) -> ValidationResult<()> {
|
||||
let configured = repo_details.git_remote();
|
||||
let found = repo_details.find_default_push_remote()?;
|
||||
if configured != found {
|
||||
|
|
|
@ -7,8 +7,10 @@ use kxio::fs;
|
|||
|
||||
use super::*;
|
||||
|
||||
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
#[test]
|
||||
fn load_should_parse_server_config() -> Result<(), crate::server::config::Error> {
|
||||
fn load_should_parse_server_config() -> Result<()> {
|
||||
let fs = fs::temp()?;
|
||||
fs.file_write(
|
||||
&fs.base().join("git-next-server.toml"),
|
||||
|
@ -111,7 +113,7 @@ fn load_should_parse_server_config() -> Result<(), crate::server::config::Error>
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_repo_config_load() -> Result<(), crate::server::config::Error> {
|
||||
fn test_repo_config_load() -> Result<()> {
|
||||
let toml = r#"
|
||||
[branches]
|
||||
main = "main"
|
||||
|
@ -150,7 +152,7 @@ fn gitdir_should_display_as_pathbuf() {
|
|||
// NOTE: this test assumes it is being run in a cloned worktree from the project's home repo:
|
||||
// git.kemitix.net:kemitix/git-next
|
||||
// If the default push remote is something else, then this test will fail
|
||||
fn repo_details_find_default_push_remote_finds_correct_remote() -> Result<(), RepoValidationError> {
|
||||
fn repo_details_find_default_push_remote_finds_correct_remote() -> Result<()> {
|
||||
let cwd = std::env::current_dir().map_err(RepoValidationError::Io)?;
|
||||
let mut repo_details = common::repo_details(
|
||||
1,
|
||||
|
@ -172,7 +174,7 @@ fn repo_details_find_default_push_remote_finds_correct_remote() -> Result<(), Re
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn gitdir_validate_should_pass_a_valid_git_repo() -> Result<(), RepoValidationError> {
|
||||
fn gitdir_validate_should_pass_a_valid_git_repo() -> Result<()> {
|
||||
let cwd = std::env::current_dir().map_err(RepoValidationError::Io)?;
|
||||
let mut repo_details = common::repo_details(
|
||||
1,
|
||||
|
|
|
@ -102,13 +102,13 @@ impl super::ForgeLike for ForgeJoEnv {
|
|||
|
||||
fn repo_clone(&self, gitdir: GitDir) -> Result<(), RepoCloneError> {
|
||||
if gitdir.exists() {
|
||||
info!(?gitdir, "Gitdir already exists - validating...");
|
||||
info!(%gitdir, "Gitdir already exists - validating...");
|
||||
gitdir
|
||||
.validate(&self.repo_details)
|
||||
.map_err(|e| RepoCloneError::Validation(e.to_string()))
|
||||
.inspect(|_| info!(?gitdir, "Validation - OK"))
|
||||
.inspect(|_| info!(%gitdir, "Validation - OK"))
|
||||
} else {
|
||||
info!(?gitdir, "Gitdir doesn't exists - cloning...");
|
||||
info!(%gitdir, "Gitdir doesn't exists - cloning...");
|
||||
repo::clone(&self.repo_details, gitdir).inspect(|_| info!("Cloned - OK"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,11 @@ pub enum Error {
|
|||
FailedToCreateDataDirectory(kxio::fs::Error),
|
||||
|
||||
#[display("The forge data path is not a directory: {path:?}")]
|
||||
ForgeDirIsNotDirectory { path: PathBuf },
|
||||
ForgeDirIsNotDirectory {
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
Config(crate::server::config::Error),
|
||||
}
|
||||
type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
|
@ -81,14 +85,25 @@ pub async fn start(fs: FileSystem, net: Network) {
|
|||
return;
|
||||
}
|
||||
|
||||
server_config
|
||||
.forges()
|
||||
.flat_map(|(forge_name, forge_config)| {
|
||||
create_forge_repos(forge_config, forge_name, server_storage, webhook, &net)
|
||||
})
|
||||
.map(start_actor)
|
||||
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
|
||||
.for_each(|msg| webhook_router.do_send(msg));
|
||||
for (forge_name, forge_config) in server_config.forges() {
|
||||
if let Err(err) = create_forge_repos(
|
||||
forge_config,
|
||||
forge_name.clone(),
|
||||
server_storage,
|
||||
webhook,
|
||||
&net,
|
||||
)
|
||||
.map(|repos| {
|
||||
repos
|
||||
.into_iter()
|
||||
.map(start_actor)
|
||||
.map(|(alias, addr)| webhook::AddWebhookRecipient(alias, addr.recipient()))
|
||||
.for_each(|msg| webhook_router.do_send(msg));
|
||||
}) {
|
||||
error!(?err, ?forge_name, "Failed to create forge repo actor");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let webhook_server = webhook::WebhookActor::new(webhook_router.recipient()).start();
|
||||
let _ = actix_rt::signal::ctrl_c().await;
|
||||
info!("Ctrl-C received, shutting down...");
|
||||
|
@ -121,20 +136,22 @@ fn create_forge_repos(
|
|||
server_storage: &ServerStorage,
|
||||
webhook: &Webhook,
|
||||
net: &Network,
|
||||
) -> Vec<(ForgeName, RepoAlias, RepoActor)> {
|
||||
) -> Result<Vec<(ForgeName, RepoAlias, RepoActor)>> {
|
||||
let span = tracing::info_span!("Forge", %forge_name, %forge_config);
|
||||
let _guard = span.enter();
|
||||
info!("Creating Forge");
|
||||
forge_config
|
||||
.repos()
|
||||
.map(create_actor(
|
||||
forge_name,
|
||||
forge_config.clone(),
|
||||
server_storage,
|
||||
webhook,
|
||||
net,
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
let mut repos = vec![];
|
||||
let creator = create_actor(
|
||||
forge_name,
|
||||
forge_config.clone(),
|
||||
server_storage,
|
||||
webhook,
|
||||
net,
|
||||
);
|
||||
for (repo_alias, server_repo_config) in forge_config.repos() {
|
||||
repos.push(creator((repo_alias, server_repo_config))?);
|
||||
}
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
fn create_actor(
|
||||
|
@ -143,7 +160,7 @@ fn create_actor(
|
|||
server_storage: &ServerStorage,
|
||||
webhook: &Webhook,
|
||||
net: &Network,
|
||||
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> (ForgeName, RepoAlias, RepoActor) {
|
||||
) -> impl Fn((RepoAlias, &ServerRepoConfig)) -> Result<(ForgeName, RepoAlias, RepoActor)> {
|
||||
let server_storage = server_storage.clone();
|
||||
let webhook = webhook.clone();
|
||||
let net = net.clone();
|
||||
|
@ -151,19 +168,16 @@ fn create_actor(
|
|||
let span = tracing::info_span!("Repo", %repo_name, %server_repo_config);
|
||||
let _guard = span.enter();
|
||||
info!("Creating Repo");
|
||||
let actor = actors::repo::RepoActor::new(
|
||||
config::RepoDetails::new(
|
||||
&repo_name,
|
||||
server_repo_config,
|
||||
&forge_name,
|
||||
&forge_config,
|
||||
&server_storage,
|
||||
),
|
||||
webhook.clone(),
|
||||
net.clone(),
|
||||
);
|
||||
let repo_details = config::RepoDetails::new(
|
||||
&repo_name,
|
||||
server_repo_config,
|
||||
&forge_name,
|
||||
&forge_config,
|
||||
&server_storage,
|
||||
)?;
|
||||
let actor = actors::repo::RepoActor::new(repo_details, webhook.clone(), net.clone());
|
||||
info!("Created Repo");
|
||||
(forge_name.clone(), repo_name, actor)
|
||||
Ok((forge_name.clone(), repo_name, actor))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue