v2 #2
3 changed files with 1264 additions and 68 deletions
commit
f83df3c874
1100
Cargo.lock
generated
1100
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
19
Cargo.toml
19
Cargo.toml
|
|
@ -5,16 +5,21 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anode = { git = "https://git.dzuchun.ing/dzu/anode.git", version = "0.1.0" }
|
anode = { git = "https://git.dzuchun.ing/dzu/anode.git", version = "0.1.0" }
|
||||||
|
anyhow = "1.0.101"
|
||||||
|
flate2 = "1.1.9"
|
||||||
|
isahc = { version = "1.7.2", features = ["static-ssl", "static-curl"] }
|
||||||
secrecy = "0.10.3"
|
secrecy = "0.10.3"
|
||||||
|
serde_json = "1.0.149"
|
||||||
|
tar = "0.4.44"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anode-build = { git = "https://git.dzuchun.ing/dzu/anode.git", version = "0.1.0" }
|
anode-build = { git = "https://git.dzuchun.ing/dzu/anode.git", version = "0.1.0" }
|
||||||
|
|
||||||
[profile.release]
|
# [profile.release]
|
||||||
opt-level = "s"
|
# opt-level = "s"
|
||||||
lto = true
|
# lto = true
|
||||||
codegen-units = 1
|
# codegen-units = 1
|
||||||
strip = true
|
# strip = true
|
||||||
panic = "abort"
|
# panic = "abort"
|
||||||
incremental = false
|
# incremental = false
|
||||||
|
|
|
||||||
97
src/main.rs
97
src/main.rs
|
|
@ -1,6 +1,15 @@
|
||||||
use std::{ffi::OsString, path::PathBuf, str::FromStr, string::FromUtf8Error};
|
use std::{ffi::OsString, path::PathBuf, str::FromStr, string::FromUtf8Error};
|
||||||
|
|
||||||
use secrecy::SecretString;
|
use anyhow::{Context, Result, bail};
|
||||||
|
use flate2::{Compression, GzBuilder};
|
||||||
|
use isahc::{
|
||||||
|
HttpClient, HttpClientBuilder, Request,
|
||||||
|
auth::{Authentication, Credentials},
|
||||||
|
config::Configurable,
|
||||||
|
http::{Method, StatusCode, Uri},
|
||||||
|
};
|
||||||
|
use secrecy::{ExposeSecret, SecretString};
|
||||||
|
use tar::{Archive, Builder, HeaderMode};
|
||||||
|
|
||||||
fn paths_parser(input: OsString) -> Result<Vec<PathBuf>, FromUtf8Error> {
|
fn paths_parser(input: OsString) -> Result<Vec<PathBuf>, FromUtf8Error> {
|
||||||
let input = String::from_utf8(input.into_encoded_bytes())?;
|
let input = String::from_utf8(input.into_encoded_bytes())?;
|
||||||
|
|
@ -56,6 +65,9 @@ struct Input {
|
||||||
/// Must be in UTF-8
|
/// Must be in UTF-8
|
||||||
#[parser(key_parser)]
|
#[parser(key_parser)]
|
||||||
key: CacheKey,
|
key: CacheKey,
|
||||||
|
/// A server url to upload the cache to
|
||||||
|
#[default_str("${{ forgejo.api_url }}")]
|
||||||
|
base_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
@ -73,8 +85,87 @@ fn actions_token() -> Result<SecretString, ActionsTokenError> {
|
||||||
Ok(SecretString::new(str.into_boxed_str()))
|
Ok(SecretString::new(str.into_boxed_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Client {
|
||||||
|
http_client: HttpClient,
|
||||||
|
base_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct CacheId(u64);
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
fn reserve(&self, key: &CacheKey, cacheSize: usize) -> Result<CacheId> {
|
||||||
|
let request = Request::builder()
|
||||||
|
.uri(format!(
|
||||||
|
"{}/_apis/artifactcache/reverveCache",
|
||||||
|
self.base_url
|
||||||
|
))
|
||||||
|
.method(Method::POST)
|
||||||
|
.body(
|
||||||
|
serde_json::json! {{
|
||||||
|
"key": &key.0,
|
||||||
|
"version": "0",
|
||||||
|
"cacheSize": cacheSize,
|
||||||
|
}}
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.context("building the request")?;
|
||||||
|
let response = self
|
||||||
|
.http_client
|
||||||
|
.send(request)
|
||||||
|
.context("sending the request")?;
|
||||||
|
let status = response.status();
|
||||||
|
if status != StatusCode::OK {
|
||||||
|
bail!(
|
||||||
|
"Failed reserving a cache (status {status}): {:?}",
|
||||||
|
response.body()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let input = Input::get();
|
// parse inputs
|
||||||
|
let Input {
|
||||||
|
path,
|
||||||
|
key,
|
||||||
|
base_url,
|
||||||
|
} = Input::get();
|
||||||
let actions_token = actions_token().unwrap();
|
let actions_token = actions_token().unwrap();
|
||||||
println!("Inputs: {input:#?}, token: {actions_token:?}");
|
|
||||||
|
// set up http client
|
||||||
|
let http_client = HttpClientBuilder::new()
|
||||||
|
.authentication(Authentication::basic())
|
||||||
|
.credentials(Credentials::new("Bearer", actions_token.expose_secret()))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let client = Client {
|
||||||
|
http_client,
|
||||||
|
base_url,
|
||||||
|
};
|
||||||
|
|
||||||
|
// collect and compress the cached files
|
||||||
|
let mut output = Vec::<u8>::with_capacity(1024 * 1024); // assume large cache size
|
||||||
|
let out_comp = GzBuilder::new().write(&mut output, Compression::best());
|
||||||
|
{
|
||||||
|
let mut archive = Builder::new(out_comp);
|
||||||
|
archive.mode(HeaderMode::Complete);
|
||||||
|
archive.follow_symlinks(false);
|
||||||
|
for path in path {
|
||||||
|
if path.is_dir() {
|
||||||
|
// we actually want to write out all the directory contents
|
||||||
|
archive.append_dir_all("", path).unwrap();
|
||||||
|
} else if path.is_file() {
|
||||||
|
// rest of the file types are handled by the existing inner code
|
||||||
|
archive.append_path(path).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
archive.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let cache_id = client.reserve(&key, output.len()).unwrap();
|
||||||
|
|
||||||
|
println!("Reserved cache with id {cache_id:?}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue