Repository: candlecorp/wick (language: rust)

Issue:
latest tag to registry push should be an array, not a second push
**Describe the bug**
The current implementation uploads twice, but the second upload is not valid.

**To Reproduce**
`wick registry push app.wick --latest`

**Expected behavior**
upload the same file with 2 tags.

**Actual behavior**
uploads two different files one of them (with latest tag) is not able to be parsed by registry.candle.dev.

**Environment:**
 - OS
 - Architecture
 - Build Version

**Additional context**
Add any other context about the problem here.



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 91bbb5a18..46f2e8fef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6262,7 +6262,7 @@ dependencies = [
 
 [[package]]
 name = "wick-cli"
-version = "0.7.0"
+version = "0.8.0"
 dependencies = [
  "anyhow",
  "atty",
diff --git a/crates/bins/wick/src/commands/invoke.rs b/crates/bins/wick/src/commands/invoke.rs
index f79033697..210d64fa9 100644
--- a/crates/bins/wick/src/commands/invoke.rs
+++ b/crates/bins/wick/src/commands/invoke.rs
@@ -7,10 +7,11 @@ use clap::Args;
 use seeded_random::Seed;
 use wick_component_cli::options::DefaultCliOptions;
 use wick_component_cli::parse_args;
-use wick_config::WickConfiguration;
+use wick_config::{FetchOptions, WickConfiguration};
 use wick_host::ComponentHostBuilder;
 use wick_packet::{InherentData, Packet, PacketStream};
 
+use crate::options::get_auth_for_scope;
 use crate::utils::{self, merge_config};
 
 #[derive(Debug, Clone, Args)]
@@ -28,7 +29,7 @@ pub(crate) struct InvokeCommand {
 
   /// Path or OCI url to manifest or wasm file.
   #[clap(action)]
-  location: String,
+  path: String,
 
   /// Name of the operation to execute.
   #[clap(default_value = "default", action)]
@@ -63,7 +64,7 @@ pub(crate) struct InvokeCommand {
   args: Vec<String>,
 }
 
-pub(crate) async fn handle(opts: InvokeCommand, _settings: wick_settings::Settings, span: tracing::Span) -> Result<()> {
+pub(crate) async fn handle(opts: InvokeCommand, settings: wick_settings::Settings, span: tracing::Span) -> Result<()> {
   // let mut logging = &mut opts.logging;
   // if !(opts.info || logging.trace || logging.debug) {
   //   logging.quiet = true;
@@ -71,16 +72,29 @@ pub(crate) async fn handle(opts: InvokeCommand, _settings: wick_settings::Settin
 
   // let _guard = wick_logger::init(&logging.name(crate::BIN_NAME).into());
 
-  let fetch_options = if PathBuf::from(&opts.location).exists() {
-    wick_config::config::FetchOptions::new()
-  } else {
-    wick_config::config::FetchOptions::new().artifact_dir(wick_xdg::Directories::GlobalCache.basedir()?)
+  let configured_creds = settings.credentials.iter().find(|c| opts.path.starts_with(&c.scope));
+
+  let (username, password) = get_auth_for_scope(
+    configured_creds,
+    opts.oci.username.as_deref(),
+    opts.oci.password.as_deref(),
+  );
+
+  let mut fetch_opts = FetchOptions::default()
+    .allow_insecure(opts.oci.insecure_registries.clone())
+    .allow_latest(true);
+  if let Some(username) = username {
+    fetch_opts = fetch_opts.oci_username(username);
+  }
+  if let Some(password) = password {
+    fetch_opts = fetch_opts.oci_password(password);
+  }
+
+  if !PathBuf::from(&opts.path).exists() {
+    fetch_opts = fetch_opts.artifact_dir(wick_xdg::Directories::GlobalCache.basedir()?);
   };
-  let fetch_options = fetch_options
-    .allow_latest(opts.oci.allow_latest)
-    .allow_insecure(&opts.oci.insecure_registries);
 
-  let manifest = WickConfiguration::fetch_all(&opts.location, fetch_options)
+  let manifest = WickConfiguration::fetch_all(&opts.path, fetch_opts)
     .await?
     .try_component_config()?;
 
diff --git a/crates/bins/wick/src/commands/registry/push.rs b/crates/bins/wick/src/commands/registry/push.rs
index e00139d1c..8362dbbd0 100644
--- a/crates/bins/wick/src/commands/registry/push.rs
+++ b/crates/bins/wick/src/commands/registry/push.rs
@@ -56,19 +56,21 @@ pub(crate) async fn handle(
   span.in_scope(|| info!(reference, "pushing artifact"));
   span.in_scope(|| debug!(options=?oci_opts, reference= &reference, "pushing reference"));
 
-  let url = package.push(&reference, &oci_opts).await?;
-  span.in_scope(|| info!(%url, "artifact pushed"));
-
-  if opts.latest {
-    let reference = package.tagged_reference("latest").unwrap();
+  let url = if opts.latest {
+    // there must be a better way than cloning the package here, feel free to fix it.
+    let url = package.clone().push(&reference, &oci_opts).await?;
 
-    span.in_scope(|| info!(reference, "pushing artifact"));
+    span.in_scope(|| info!(%url, "artifact pushed"));
 
-    span.in_scope(|| debug!(options=?oci_opts, reference= &reference, "pushing reference"));
+    let reference = package.tagged_reference("latest").unwrap();
+    span.in_scope(|| info!(reference, "pushing latest tag"));
 
-    let url = package.push(&reference, &oci_opts).await?;
-    span.in_scope(|| info!(%url, "artifact pushed"));
-  }
+    package.push(&reference, &oci_opts).await?;
+    url
+  } else {
+    package.push(&reference, &oci_opts).await?
+  };
+  span.in_scope(|| info!(%url, "artifact pushed"));
 
   Ok(())
 }
diff --git a/crates/bins/wick/src/commands/run.rs b/crates/bins/wick/src/commands/run.rs
index 712eef9ee..67eaec79d 100644
--- a/crates/bins/wick/src/commands/run.rs
+++ b/crates/bins/wick/src/commands/run.rs
@@ -1,9 +1,13 @@
+use std::path::PathBuf;
+
 use anyhow::Result;
 use clap::Args;
 use tracing::Instrument;
-use wick_config::WickConfiguration;
+use wick_config::{FetchOptions, WickConfiguration};
 use wick_host::AppHostBuilder;
 
+use crate::options::get_auth_for_scope;
+
 #[derive(Debug, Clone, Args)]
 #[clap(rename_all = "kebab-case")]
 pub(crate) struct RunCommand {
@@ -23,9 +27,32 @@ pub(crate) struct RunCommand {
   args: Vec<String>,
 }
 
-pub(crate) async fn handle(opts: RunCommand, _settings: wick_settings::Settings, span: tracing::Span) -> Result<()> {
+pub(crate) async fn handle(opts: RunCommand, settings: wick_settings::Settings, span: tracing::Span) -> Result<()> {
   span.in_scope(|| trace!(args = ?opts.args, "rest args"));
-  let app_config = WickConfiguration::fetch_all(&opts.path, opts.oci.into())
+
+  let configured_creds = settings.credentials.iter().find(|c| opts.path.starts_with(&c.scope));
+
+  let (username, password) = get_auth_for_scope(
+    configured_creds,
+    opts.oci.username.as_deref(),
+    opts.oci.password.as_deref(),
+  );
+
+  let mut fetch_opts = FetchOptions::default()
+    .allow_insecure(opts.oci.insecure_registries)
+    .allow_latest(true);
+  if let Some(username) = username {
+    fetch_opts = fetch_opts.oci_username(username);
+  }
+  if let Some(password) = password {
+    fetch_opts = fetch_opts.oci_password(password);
+  }
+
+  if !PathBuf::from(&opts.path).exists() {
+    fetch_opts = fetch_opts.artifact_dir(wick_xdg::Directories::GlobalCache.basedir()?);
+  };
+
+  let app_config = WickConfiguration::fetch_all(&opts.path, fetch_opts)
     .instrument(span.clone())
     .await?
     .try_app_config()?;
diff --git a/crates/wick/wick-asset-reference/src/asset_reference.rs b/crates/wick/wick-asset-reference/src/asset_reference.rs
index 95c84b01c..5fa959247 100644
--- a/crates/wick/wick-asset-reference/src/asset_reference.rs
+++ b/crates/wick/wick-asset-reference/src/asset_reference.rs
@@ -216,6 +216,13 @@ impl AssetReference {
     Box::pin(UnboundedReceiverStream::new(rx))
   }
 
+  /// Check if the asset exists on disk.
+  #[must_use]
+  pub fn exists_locally(&self) -> bool {
+    let path = self.path();
+    path.is_ok() && path.unwrap().exists()
+  }
+
   fn retrieve_as_oci_with_progress(
     &self,
     options: FetchOptions,
@@ -292,14 +299,14 @@ impl Asset for AssetReference {
     let location = self.location().to_owned();
 
     let cache_location = self.cache_location.clone();
+    let exists = self.exists_locally();
     Box::pin(async move {
       let path = path.map_err(|e| assets::Error::Parse(e.to_string()))?;
       let pb = PathBuf::from(&path);
       if pb.is_dir() {
         return Err(assets::Error::IsDirectory(path.clone()));
       }
-      let file = tokio::fs::File::open(&pb).await;
-      if file.is_ok() {
+      if exists {
         debug!(path = ?path, "fetching local asset");
         let mut file = tokio::fs::File::open(&path)
           .await
diff --git a/crates/wick/wick-package/src/package.rs b/crates/wick/wick-package/src/package.rs
index 13ea9fb64..d0eff47fb 100644
--- a/crates/wick/wick-package/src/package.rs
+++ b/crates/wick/wick-package/src/package.rs
@@ -90,10 +90,12 @@ fn process_assets(
         }
       }
 
-      let file_bytes = asset.bytes(&options).await?;
-      let hash = format!("sha256:{}", digest(file_bytes.as_ref()));
-      let wick_file = PackageFile::new(relative_path.to_path_buf(), hash, media_type.to_owned(), file_bytes);
-      wick_files.push(wick_file);
+      if asset.exists_locally() {
+        let file_bytes = asset.bytes(&options).await?;
+        let hash = format!("sha256:{}", digest(file_bytes.as_ref()));
+        let wick_file = PackageFile::new(relative_path.to_path_buf(), hash, media_type.to_owned(), file_bytes);
+        wick_files.push(wick_file);
+      }
     }
 
     Ok((seen_assets, wick_files))


Test patch: