Skip to content

Commit

Permalink
feat: Publish
Browse files Browse the repository at this point in the history
Packages can now be published :)

This also includes support for percent-encoded values for the `registry`
path
  • Loading branch information
AllexVeldman committed May 31, 2024
1 parent 71b5377 commit 74057e6
Show file tree
Hide file tree
Showing 10 changed files with 540 additions and 66 deletions.
80 changes: 80 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ wasm-bindgen = "0.2.92"
worker = {version = "0.3.0"}
tracing-web = "0.1.3"
futures = "0.3.30"
sha2 = "0.10.8"
base16ct = { version = "0.2.0", features = ["alloc"] }
urlencoding = "2.1.3"

[dependencies.web-sys]
version = "0.3.63"
Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cli-list:
cargo run -- -u AllexVeldman -p $GH_TOKEN list ghcr.io/allexveldman/pyoci

cf-worker:
NO_MINIFY=1 npx wrangler dev --port 8090
cf-worker *args:
NO_MINIFY=1 npx wrangler dev --port 8090 --local-upstream localhost:8090 {{args}}
7 changes: 5 additions & 2 deletions py/docker/config/config.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
version: 0.1
log:
level: debug
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
inmemory:
# inmemory:
filesystem:
rootdirectory: /var/lib/registry
#auth:
# silly:
# realm: http://localhost:5000/
# service: silly-service
http:
addr: :5000
secret: "foobarbazqaz"
# secret: "foobarbazqaz"
# tls:
# certificate: /etc/docker/registry/certs/tls.crt
# key: /etc/docker/registry/certs/tls.key
Expand Down
1 change: 0 additions & 1 deletion py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ version = "0.1.0"
description = "Publish and download python packages using OCI compliant registries"
authors = ["Allex Veldman <[email protected]>"]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.11"
Expand Down
60 changes: 49 additions & 11 deletions src/cf.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use askama::Template;
use std::str::FromStr;
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::fmt::format::Pretty;
use tracing_subscriber::fmt::time::UtcTime;
use tracing_subscriber::prelude::*;
use tracing_web::{performance_layer, MakeWebConsoleWriter};
use worker::{event, Context, Env, Request, Response, ResponseBuilder, RouteContext, Router};
use worker::{
event, Context, Env, FormEntry, Request, Response, ResponseBuilder, RouteContext, Router,
};

use crate::{package, pyoci, templates, PyOci};

Expand All @@ -19,9 +22,15 @@ macro_rules! wrap {
}

fn wrap(res: Result<Response, pyoci::Error>) -> worker::Result<Response> {
match res {
Ok(response) => Ok(response),
Err(e) => Response::error(e.to_string(), 400),
let err = match res {
Ok(response) => return Ok(response),
Err(e) => e,
};
match err {
pyoci::Error::OciErrorResponse(resp) => {
Response::error(serde_json::to_string(&resp).expect("valid json"), 400)
}
err => Response::error(err.to_string(), 400),
}
}

Expand All @@ -35,7 +44,8 @@ fn start() {
let fmt_layer = tracing_subscriber::fmt::layer()
.with_ansi(false) // Only partially supported across browsers
.with_timer(UtcTime::rfc_3339())
.with_writer(MakeWebConsoleWriter::new());
.with_writer(MakeWebConsoleWriter::new())
.with_filter(LevelFilter::DEBUG);
let perf_layer = performance_layer().with_details_from_fields(Pretty::default());

tracing_subscriber::registry()
Expand All @@ -53,11 +63,12 @@ async fn main(req: Request, env: Env, _ctx: Context) -> worker::Result<Response>
/// Request Router
fn router<'a>() -> Router<'a, ()> {
Router::new()
.get_async("/:registry/:namespace/:package", wrap!(list_package))
.get_async("/:registry/:namespace/:package/", wrap!(list_package))
.get_async(
"/:registry/:namespace/:package/:filename",
wrap!(download_package),
)
.post_async("/:registry/:namespace", wrap!(publish_package))
}

/// List package request handler
Expand All @@ -66,14 +77,15 @@ async fn list_package(req: Request, _ctx: RouteContext<()>) -> Result<Response,
let auth = req.headers().get("Authorization").expect("valid header");
let package = package::Info::from_str(&req.path())?;
let client = PyOci::new(package.registry.clone(), auth);
let files = client
.list_package_files(&package)
.await
.expect("valid files");
let files = client.list_package_files(&package).await?;
let mut host = req.url().expect("valid url");
host.set_path("");
// TODO: swap to application/vnd.pypi.simple.v1+json
let template = templates::ListPackageTemplate { host, files };
Ok(Response::ok(template.render().expect("valid template")).expect("valid response"))
Ok(
Response::from_html(template.render().expect("valid template"))
.expect("valid html response"),
)
}

/// Download package request handler
Expand All @@ -100,3 +112,29 @@ async fn download_package(req: Request, _ctx: RouteContext<()>) -> Result<Respon
.expect("valid response");
Ok(response)
}

#[tracing::instrument(skip(req, ctx))]
async fn publish_package(
mut req: Request,
ctx: RouteContext<()>,
) -> Result<Response, pyoci::Error> {
let auth = req.headers().get("Authorization").expect("valid header");
let Some(form_data) = req.form_data().await.expect("valid form data").get("file") else {
return Err(pyoci::Error::Other("Missing file".to_string()));
};
let FormEntry::File(file) = form_data else {
return Err(pyoci::Error::Other("Expected file".to_string()));
};
let (Some(registry), Some(namespace)) = (ctx.param("registry"), ctx.param("namespace")) else {
return Err(pyoci::Error::Other(
"Missing registry or namespace".to_string(),
));
};
let package = package::Info::new(registry, namespace, &file.name())?;
let client = PyOci::new(package.registry.clone(), auth);

let data = file.bytes().await.expect("valid bytes");

client.publish_package_file(&package, data).await?;
Ok(Response::ok("Published").unwrap())
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ mod transport;

// Re-export the PyOci client
pub use pyoci::PyOci;

// crate constants
const USER_AGENT: &str = concat!("pyoci ", env!("CARGO_PKG_VERSION"), " (cloudflare worker)");
const ARTIFACT_TYPE: &str = "application/pyoci.package.v1";
Loading

0 comments on commit 74057e6

Please sign in to comment.