Skip to content

Commit

Permalink
Lockfile support for addons
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAlan404 committed Jul 21, 2024
1 parent a6e2fee commit e14228d
Show file tree
Hide file tree
Showing 18 changed files with 255 additions and 49 deletions.
32 changes: 15 additions & 17 deletions src/api/app/actions/build/addons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ use std::{collections::HashSet, path::Path, sync::Arc};
use anyhow::{Context, Result};
use futures::{stream, StreamExt, TryStreamExt};

use crate::api::{app::App, models::Addon, step::Step};
use crate::api::{app::App, models::Addon};

impl App {
/// Installs new addons and removes old removed addons
pub async fn action_install_addons(self: Arc<Self>, base: &Path) -> Result<()> {
let addons = self.collect_addons().await?;
let base = Arc::new(base.to_owned());

println!("Found {} addons", addons.len());

let (addons_to_add, addons_to_remove): (Vec<Addon>, Vec<Addon>) = if let Some(lockfile) = &*self.existing_lockfile.read().await {
let mut old = HashSet::new();
old.extend(lockfile.addons.clone());
Expand All @@ -23,6 +25,8 @@ impl App {
(addons, vec![])
};

println!("Installing {} addons, removing {} addons", addons_to_add.len(), addons_to_remove.len());

for addon in &addons_to_remove {
self.clone().action_remove_addon(&base, addon).await?;
}
Expand All @@ -33,8 +37,14 @@ impl App {
let app = self.clone();
let base = base.clone();
async move {
app.action_install_addon(&base, &addon).await
.with_context(|| format!("{addon:#?}"))
let x = app.clone().action_install_addon(&base, &addon).await
.with_context(|| format!("{addon:#?}"));

if x.is_ok() {
app.add_addon_to_lockfile(addon).await;
}

x
}
}
).await?;
Expand All @@ -52,21 +62,9 @@ impl App {

/// Removes a single addon
pub async fn action_remove_addon(self: Arc<Self>, base: &Path, addon: &Addon) -> Result<()> {
let steps = addon.resolve_steps(&self).await?;
let steps = addon.resolve_remove_steps(&self).await?;
let dir = base.join(addon.target.as_str());

// TODO

if let Some(meta) = steps.iter().find_map(|x| match x {
Step::CacheCheck(meta) => Some(meta),
Step::Download { metadata, .. } => Some(metadata),
_ => None,
}) {
tokio::fs::remove_file(dir.join(&meta.filename)).await?;
} else {
log::error!("Couldn't remove addon: {addon:#?}");
}

self.execute_steps(&dir, &steps).await?;
Ok(())
}
}
4 changes: 4 additions & 0 deletions src/api/app/actions/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ pub mod bootstrap;
impl App {
/// Builds the entire server
pub async fn action_build(self: Arc<Self>, base: &Path) -> Result<()> {
self.read_existing_lockfile(base).await?;

self.action_install_jar(base).await?;
self.clone().action_install_addons(base).await?;
self.clone().action_bootstrap(base).await?;
self.action_generate_scripts(base).await?;

self.write_lockfile(base).await?;

Ok(())
}
}
38 changes: 38 additions & 0 deletions src/api/app/actions/lockfile/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::{path::Path, sync::Arc};

use anyhow::Result;

use crate::api::{app::App, models::{lockfile::{Lockfile, LOCKFILE}, Addon}};

impl App {
pub async fn reset_lockfile(&self) -> Result<()> {

Check warning on line 8 in src/api/app/actions/lockfile/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

method `reset_lockfile` is never used

warning: method `reset_lockfile` is never used --> src/api/app/actions/lockfile/mod.rs:8:18 | 7 | impl App { | -------- method in this implementation 8 | pub async fn reset_lockfile(&self) -> Result<()> { | ^^^^^^^^^^^^^^
let mut new_lockfile = self.new_lockfile.write().await;
*new_lockfile = Lockfile::default();

Ok(())
}

pub async fn read_existing_lockfile(&self, base: &Path) -> Result<()> {
let path = base.join(LOCKFILE);

if path.exists() {
let mut existing_lockfile = self.existing_lockfile.write().await;
*existing_lockfile = Some(serde_json::from_str::<Lockfile>(&tokio::fs::read_to_string(base.join(LOCKFILE)).await?)?);
}

Ok(())
}

pub async fn write_lockfile(&self, base: &Path) -> Result<()> {
let lockfile = self.new_lockfile.read().await;

tokio::fs::write(base.join(LOCKFILE), serde_json::to_vec(&*lockfile)?).await?;

Ok(())
}

pub async fn add_addon_to_lockfile(self: Arc<Self>, addon: Addon) {
println!("Added Addon to lockfile");
self.new_lockfile.write().await.addons.push(addon);
}
}
1 change: 1 addition & 0 deletions src/api/app/actions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ mod build;
mod init;
mod markdown;
mod script;
mod lockfile;
2 changes: 1 addition & 1 deletion src/api/app/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::Result;
use tokio::sync::RwLock;

use crate::api::{models::{network::{Network, NETWORK_TOML}, server::{Server, SERVER_TOML}}, utils::toml::{try_find_toml_upwards, write_toml}};
use crate::api::{models::{lockfile::Lockfile, network::{Network, NETWORK_TOML}, server::{Server, SERVER_TOML}}, utils::toml::{try_find_toml_upwards, write_toml}};

Check warning on line 6 in src/api/app/io.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `lockfile::Lockfile`

warning: unused import: `lockfile::Lockfile` --> src/api/app/io.rs:6:27 | 6 | use crate::api::{models::{lockfile::Lockfile, network::{Network, NETWORK_TOML}, server::{Server, SERVER_TOML}}, utils::toml::{try_find_to... | ^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

use super::App;

Expand Down
10 changes: 9 additions & 1 deletion src/api/app/step/remove_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ impl App {
dir: &Path,
metadata: &FileMeta,
) -> Result<StepResult> {

println!("Deleting {}", metadata.filename);

let path = dir.join(&metadata.filename);

if path.exists() {
tokio::fs::remove_file(path).await?;
} else {
println!("{path:?} does not exist, cant delete");
}

Ok(StepResult::Continue)
}
Expand Down
15 changes: 14 additions & 1 deletion src/api/models/addon/addon.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};

use crate::api::{app::App, models::Environment, sources::url::resolve_steps_for_url, step::Step};
use crate::api::{app::App, models::Environment, sources::url::{resolve_remove_steps_for_url, resolve_steps_for_url}, step::Step};

use super::{AddonTarget, AddonType};

Expand All @@ -27,4 +27,17 @@ impl Addon {
AddonType::MavenArtifact { url, group, artifact, version, filename } => app.maven().resolve_steps(url, group, artifact, version, filename).await,
}
}

pub async fn resolve_remove_steps(&self, app: &App) -> Result<Vec<Step>> {
match &self.addon_type {
AddonType::Url { url } => resolve_remove_steps_for_url(app, url, None).await,
AddonType::Modrinth { id, version } => app.modrinth().resolve_remove_steps(id, version).await,
AddonType::Curseforge { id, version } => app.curseforge().resolve_remove_steps(id, version).await,
AddonType::Spigot { id, version } => app.spigot().resolve_remove_steps(id, version).await,
AddonType::Hangar { id, version } => app.hangar().resolve_remove_steps(id, version).await,
AddonType::GithubRelease { repo, version, filename } => app.github().resolve_remove_steps(repo, version, filename).await,
AddonType::Jenkins { url, job, build, artifact } => app.jenkins().resolve_remove_steps(url, job, build, artifact, None).await,
AddonType::MavenArtifact { url, group, artifact, version, filename } => app.maven().resolve_remove_steps(url, group, artifact, version, filename).await,
}
}
}
2 changes: 2 additions & 0 deletions src/api/models/lockfile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub use bootstrapped_file::*;

use super::Addon;

pub const LOCKFILE: &str = ".mcman.lock";

#[derive(Debug, Serialize, Deserialize)]
pub struct Lockfile {
pub vars: HashMap<String, String>,
Expand Down
34 changes: 24 additions & 10 deletions src/api/sources/buildtools/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{anyhow, Result};

use crate::api::{app::App, step::Step, tools::java::JavaVersion};
use crate::api::{app::App, step::{FileMeta, Step}, tools::java::JavaVersion};

pub const BUILDTOOLS_JENKINS_URL: &str = "https://hub.spigotmc.org/jenkins";
pub const BUILDTOOLS_JENKINS_JOB: &str = "BuildTools";
Expand All @@ -12,21 +12,35 @@ pub async fn resolve_steps(
custom_args: &Vec<String>,
mc_version: &str,
) -> Result<Vec<Step>> {
let jar = resolve_steps_jar(app).await?;
let (url, metadata) = resolve_buildtools_jar(app).await?;

let meta = jar.iter().find_map(|v| if let Step::CacheCheck(meta) = v { Some(meta) } else { None }).unwrap();
let exec_steps = resolve_compile_steps(app, &metadata.filename, craftbukkit, custom_args, mc_version).await?;

let exec = resolve_steps_build(app, &meta.filename, craftbukkit, custom_args, mc_version).await?;
let mut steps = vec![
Step::CacheCheck(metadata.clone()),
Step::Download { url, metadata },
];

Ok(vec![jar, exec].concat())
steps.extend(exec_steps);

Ok(steps)
}

/// Resolve steps for the BuildTools.jar
pub async fn resolve_steps_jar(
app: &App,
pub async fn resolve_remove_steps(

Check warning on line 29 in src/api/sources/buildtools/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

function `resolve_remove_steps` is never used

warning: function `resolve_remove_steps` is never used --> src/api/sources/buildtools/mod.rs:29:14 | 29 | pub async fn resolve_remove_steps( | ^^^^^^^^^^^^^^^^^^^^
_app: &App,
_craftbukkit: bool,
_custom_args: &Vec<String>,

Check failure on line 32 in src/api/sources/buildtools/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

writing `&Vec` instead of `&[_]` involves a new object where a slice will do

error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do --> src/api/sources/buildtools/mod.rs:32:19 | 32 | _custom_args: &Vec<String>, | ^^^^^^^^^^^^ help: change this to: `&[String]` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
_mc_version: &str,
) -> Result<Vec<Step>> {
Ok(vec![Step::RemoveFile(FileMeta::filename(String::from("server.jar")))])
}

Check warning on line 36 in src/api/sources/buildtools/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unused `async` for function with no await statements

warning: unused `async` for function with no await statements --> src/api/sources/buildtools/mod.rs:29:1 | 29 | / pub async fn resolve_remove_steps( 30 | | _app: &App, 31 | | _craftbukkit: bool, 32 | | _custom_args: &Vec<String>, ... | 35 | | Ok(vec![Step::RemoveFile(FileMeta::filename(String::from("server.jar")))]) 36 | | } | |_^ | = help: consider removing the `async` from this function = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unused_async

/// Resolve BuildTools.jar from jenkins
pub async fn resolve_buildtools_jar(
app: &App,
) -> Result<(String, FileMeta)> {
app.jenkins()
.resolve_steps(
.resolve_artifact(
BUILDTOOLS_JENKINS_URL,
BUILDTOOLS_JENKINS_JOB,
"latest",
Expand All @@ -37,7 +51,7 @@ pub async fn resolve_steps_jar(
}

/// Resolve steps for using BuildTools to compile a server jar

Check warning on line 53 in src/api/sources/buildtools/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

item in documentation is missing backticks

warning: item in documentation is missing backticks --> src/api/sources/buildtools/mod.rs:53:29 | 53 | /// Resolve steps for using BuildTools to compile a server jar | ^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown help: try | 53 | /// Resolve steps for using `BuildTools` to compile a server jar | ~~~~~~~~~~~~
pub async fn resolve_steps_build(
pub async fn resolve_compile_steps(
_app: &App,
jar_name: &str,
craftbukkit: bool,
Expand Down
16 changes: 16 additions & 0 deletions src/api/sources/curseforge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ impl<'a> CurseforgeAPI<'a> {
Step::Download { url: file.download_url, metadata }
])
}

pub async fn resolve_remove_steps(&self, mod_id: &str, file_id: &str) -> Result<Vec<Step>> {
let file = self.fetch_file(mod_id, file_id).await?;

let metadata = FileMeta {
cache: Some(CacheLocation("curseforge".into(), format!("{mod_id}/{file_id}/{}", file.display_name))),
filename: file.display_name,
hashes: convert_hashes(file.hashes),
size: Some(file.file_length),
..Default::default()

Check failure on line 67 in src/api/sources/curseforge/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

struct update has no effect, all the fields in the struct have already been specified

error: struct update has no effect, all the fields in the struct have already been specified --> src/api/sources/curseforge/mod.rs:67:15 | 67 | ..Default::default() | ^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
};

Ok(vec![
Step::RemoveFile(metadata)
])
}
}

pub fn convert_hashes(hashes: Vec<CurseforgeFileHash>) -> HashMap<HashFormat, String> {
Expand Down
31 changes: 25 additions & 6 deletions src/api/sources/github/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,12 @@ impl<'a> GithubAPI<'a> {
Ok((release, asset))
}

pub async fn resolve_steps(
pub async fn resolve(
&self,
repo: &str,
release_tag: &str,
asset_name: &str,
) -> Result<Vec<Step>> {
) -> Result<(String, FileMeta)> {
let (release, asset) = self.fetch_asset(repo, release_tag, asset_name).await?;

let metadata = FileMeta {
Expand All @@ -161,12 +161,31 @@ impl<'a> GithubAPI<'a> {
release.tag_name, asset.name
);

Ok((url, metadata))
}

pub async fn resolve_steps(
&self,
repo: &str,
release_tag: &str,
asset_name: &str,
) -> Result<Vec<Step>> {
let (url, metadata) = self.resolve(repo, release_tag, asset_name).await?;

Ok(vec![
Step::CacheCheck(metadata.clone()),
Step::Download {
url,
metadata: metadata.clone(),
},
Step::Download { url, metadata }
])
}

pub async fn resolve_remove_steps(
&self,
repo: &str,
release_tag: &str,
asset_name: &str,
) -> Result<Vec<Step>> {
let (_, metadata) = self.resolve(repo, release_tag, asset_name).await?;

Ok(vec![Step::RemoveFile(metadata)])
}
}
18 changes: 15 additions & 3 deletions src/api/sources/hangar/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, Context, Ok, Result};

mod models;
pub use models::*;
Expand Down Expand Up @@ -34,7 +34,7 @@ impl<'a> HangarAPI<'a> {
)
}

pub async fn resolve_steps(&self, id: &str, version_id: &str) -> Result<Vec<Step>> {
pub async fn resolve(&self, id: &str, version_id: &str) -> Result<(String, FileMeta)> {
let version = self
.fetch_project_version(id, version_id)
.await
Expand Down Expand Up @@ -69,9 +69,21 @@ impl<'a> HangarAPI<'a> {

let url = download.get_url();

Ok((url, metadata))
}

pub async fn resolve_steps(&self, id: &str, version_id: &str) -> Result<Vec<Step>> {
let (url, metadata) = self.resolve(id, version_id).await?;

Ok(vec![
Step::CacheCheck(metadata.clone()),
Step::Download { url, metadata },
Step::Download { url, metadata }
])
}

pub async fn resolve_remove_steps(&self, id: &str, version_id: &str) -> Result<Vec<Step>> {
let (_, metadata) = self.resolve(id, version_id).await?;

Ok(vec![Step::RemoveFile(metadata)])
}
}
Loading

0 comments on commit e14228d

Please sign in to comment.