diff --git a/.changes/1491.json b/.changes/1491.json new file mode 100644 index 000000000..8ac3fc67a --- /dev/null +++ b/.changes/1491.json @@ -0,0 +1,5 @@ +{ + "description": "Allow specifying only a tag for images in config", + "issues": [1169], + "type": "changed" +} diff --git a/docs/config_file.md b/docs/config_file.md index 2c004720a..451575e89 100644 --- a/docs/config_file.md +++ b/docs/config_file.md @@ -221,6 +221,19 @@ the default one. Normal Docker behavior applies, so: - If only `image:latest` is specified, then Docker won't look in Docker Hub. - If the tag is omitted, then Docker will use the `latest` tag. +If you specify a tag but no image name, `cross` will use the default image with +the tag you provided: + +```toml +[target.aarch64-unknown-linux-gnu] +# Translates to `ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge` +image = ":edge" + +[target.x86_64-unknown-linux-musl] +# Translates to `ghcr.io/cross-rs/x86_64-unknown-linux-musl@sha256:77db671d8356a64ae72a3e1415e63f547f26d374fbe3c4762c1cd36c7eac7b99` +image = "@sha256:77db671d8356a64ae72a3e1415e63f547f26d374fbe3c4762c1cd36c7eac7b99" +``` + The `image` key can also take the toolchains/platforms supported by the image: ```toml diff --git a/src/config.rs b/src/config.rs index 853745cff..0979ef7dc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,10 +41,10 @@ impl PartialEq<(Option, Option)> for ConfVal { } #[derive(Debug)] -struct Environment(&'static str, Option>); +pub(crate) struct Environment(&'static str, Option>); impl Environment { - fn new(map: Option>) -> Self { + pub(crate) fn new(map: Option>) -> Self { Environment("CROSS", map) } @@ -352,7 +352,7 @@ impl Config { } #[cfg(test)] - fn new_with(toml: Option, env: Environment) -> Self { + pub(crate) fn new_with(toml: Option, env: Environment) -> Self { Config { toml, env } } diff --git a/src/docker/image.rs b/src/docker/image.rs index 6f45b5d3f..aeb6edb51 100644 --- a/src/docker/image.rs +++ b/src/docker/image.rs @@ -27,6 +27,12 @@ pub struct PossibleImage { } impl PossibleImage { + pub fn target_is_specified(&self) -> bool { + // Hostnames cannot start with either of these characters, + // nor can image names. + !(self.name.starts_with(':') || self.name.starts_with('@')) + } + pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image { if self.toolchain.is_empty() { Image { diff --git a/src/docker/shared.rs b/src/docker/shared.rs index 8a9e4394c..9f7c0877c 100644 --- a/src/docker/shared.rs +++ b/src/docker/shared.rs @@ -1251,6 +1251,15 @@ fn get_user_image( image = config.zig_image(target).map_err(GetImageError::Other)?; } + if let Some(image) = &mut image { + if !image.target_is_specified() { + // The provided image name is just the tag or hash part, + // so add the default registry and image name before it. + let target_name = get_target_name(target, uses_zig); + image.name = format!("{CROSS_IMAGE}/{target_name}{}", image.name); + } + } + Ok(image) } @@ -1542,8 +1551,10 @@ pub fn path_hash(path: &Path, count: usize) -> Result { #[cfg(test)] mod tests { + use std::collections::HashMap; + use super::*; - use crate::id; + use crate::{config::Environment, id}; #[cfg(not(target_os = "windows"))] use crate::file::PathExt; @@ -1598,6 +1609,48 @@ mod tests { } } + #[test] + fn test_tag_only_image() -> Result<()> { + let target: Target = TargetTriple::X86_64UnknownLinuxGnu.into(); + let test = |map, expected_ver: &str, expected_ver_zig: &str| -> Result<()> { + let env = Environment::new(Some(map)); + let config = Config::new_with(None, env); + for (uses_zig, expected_ver) in [(false, expected_ver), (true, expected_ver_zig)] { + let expected_image_target = if uses_zig { + "zig" + } else { + "x86_64-unknown-linux-gnu" + }; + let expected = format!("ghcr.io/cross-rs/{expected_image_target}{expected_ver}"); + + let image = get_image(&config, &target, uses_zig)?; + assert_eq!(image.name, expected); + let image_name = get_image_name(&config, &target, uses_zig)?; + assert_eq!(image_name, expected); + } + Ok(()) + }; + + let default_ver = format!(":{DEFAULT_IMAGE_VERSION}"); + let mut map = HashMap::new(); + test(map.clone(), &default_ver, &default_ver)?; + + map.insert("CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE", ":edge"); + test(map.clone(), ":edge", ":edge")?; + + // `image` always takes precedence over `zig.image`, even when `uses_zig` is `true` + map.insert( + "CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_ZIG_IMAGE", + "@sha256:foobar", + ); + test(map.clone(), ":edge", ":edge")?; + + map.remove("CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE"); + test(map.clone(), &default_ver, "@sha256:foobar")?; + + Ok(()) + } + mod directories { use super::*; use crate::cargo::cargo_metadata_with_args;