Skip to content

Commit

Permalink
Allow specifying only a tag for images in config
Browse files Browse the repository at this point in the history
  • Loading branch information
thirteenowls committed May 12, 2024
1 parent 57cc9cf commit ea76d47
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changes/1491.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"description": "Allow specifying only a tag for images in config",
"issues": [1169],
"type": "changed"
}
13 changes: 13 additions & 0 deletions docs/config_file.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/bin/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl Run {
}
};

let image = image.to_definite_with(&engine, msg_info);
let image = image.to_definite_with(&engine, msg_info)?;

let paths = docker::DockerPaths::create(&engine, metadata, cwd, toolchain, msg_info)?;
let options = docker::DockerOptions::new(
Expand Down
6 changes: 3 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ impl<T: PartialEq> PartialEq<(Option<T>, Option<T>)> for ConfVal<T> {
}

#[derive(Debug)]
struct Environment(&'static str, Option<HashMap<&'static str, &'static str>>);
pub(crate) struct Environment(&'static str, Option<HashMap<&'static str, &'static str>>);

impl Environment {
fn new(map: Option<HashMap<&'static str, &'static str>>) -> Self {
pub(crate) fn new(map: Option<HashMap<&'static str, &'static str>>) -> Self {
Environment("CROSS", map)
}

Expand Down Expand Up @@ -352,7 +352,7 @@ impl Config {
}

#[cfg(test)]
fn new_with(toml: Option<CrossToml>, env: Environment) -> Self {
pub(crate) fn new_with(toml: Option<CrossToml>, env: Environment) -> Self {
Config { toml, env }
}

Expand Down
12 changes: 6 additions & 6 deletions src/cross_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ where

#[cfg(test)]
mod tests {
use crate::docker::ImagePlatform;
use crate::docker::{ImagePlatform, ImageReference};

use super::*;
use crate::shell;
Expand Down Expand Up @@ -741,7 +741,7 @@ mod tests {
build_std: None,
zig: None,
image: Some(PossibleImage {
name: "test-image".to_owned(),
reference: ImageReference::Name("test-image".to_owned()),
toolchain: vec![ImagePlatform::from_target(
"aarch64-unknown-linux-musl".into(),
)?],
Expand Down Expand Up @@ -773,7 +773,7 @@ mod tests {
enable: None,
version: None,
image: Some(PossibleImage {
name: "zig:local".to_owned(),
reference: ImageReference::Name("zig:local".to_owned()),
toolchain: vec![ImagePlatform::from_target(
"aarch64-unknown-linux-gnu".into(),
)?],
Expand Down Expand Up @@ -939,7 +939,7 @@ mod tests {
[target.target3]
xargo = false
build-std = true
image = "test-image3"
image = "@sha256:test-image3"
[target.target3.env]
volumes = ["VOL3_ARG"]
Expand Down Expand Up @@ -978,7 +978,7 @@ mod tests {
[target.target3]
xargo = false
build-std = true
image = "test-image3"
image = "@sha256:test-image3"
[target.target3.env]
volumes = ["VOL3_ARG"]
Expand Down Expand Up @@ -1042,7 +1042,7 @@ mod tests {
let target3 = &targets[&Target::new_custom("target3")];
assert_eq!(target3.build_std, Some(BuildStd::Bool(true)));
assert_eq!(target3.xargo, Some(false));
assert_eq!(target3.image, Some(p!("test-image3")));
assert_eq!(target3.image, Some(p!("@sha256:test-image3")));
assert_eq!(target3.pre_build, None);
assert_eq!(target3.dockerfile, None);
assert_eq!(target3.env.passthrough, Some(vec![p!("VAR3")]));
Expand Down
54 changes: 44 additions & 10 deletions src/docker/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@ impl std::fmt::Display for Image {

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct PossibleImage {
pub name: String,
#[serde(rename = "name")]
pub reference: ImageReference,
// The toolchain triple the image is built for
pub toolchain: Vec<ImagePlatform>,
}

impl PossibleImage {
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Result<Image> {
let ImageReference::Name(name) = self.reference.clone() else {
eyre::bail!("cannot make definite Image from unqualified PossibleImage");
};

if self.toolchain.is_empty() {
Image {
name: self.name.clone(),
Ok(Image {
name,
platform: ImagePlatform::DEFAULT,
}
})
} else {
let platform = if self.toolchain.len() == 1 {
self.toolchain.first().expect("should contain at least one")
Expand Down Expand Up @@ -71,18 +76,18 @@ impl PossibleImage {
platform
}
};
Image {
Ok(Image {
platform: platform.clone(),
name: self.name.clone(),
}
name,
})
}
}
}

impl<T: AsRef<str>> From<T> for PossibleImage {
fn from(s: T) -> Self {
PossibleImage {
name: s.as_ref().to_owned(),
reference: s.as_ref().to_owned().into(),
toolchain: vec![],
}
}
Expand All @@ -98,9 +103,38 @@ impl FromStr for PossibleImage {

impl std::fmt::Display for PossibleImage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.name)
f.write_str(self.reference.get())
}
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(from = "String", untagged)]
pub enum ImageReference {
/// Partially qualified reference, with or without tag/digest
Name(String),
/// Unqualified reference, only a tag or digest
Identifier(String),
}

impl ImageReference {
pub fn get(&self) -> &str {
match self {
Self::Name(s) => s,
Self::Identifier(s) => s,
}
}
}

impl From<String> for ImageReference {
fn from(s: String) -> Self {
if s.starts_with(':') || s.starts_with('@') {
Self::Identifier(s)
} else {
Self::Name(s)
}
}
}

/// The architecture/platform to use in the image
///
/// <https://github.com/containerd/containerd/blob/release/1.6/platforms/platforms.go#L63>
Expand Down
4 changes: 3 additions & 1 deletion src/docker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ pub use self::engine::*;
pub use self::provided_images::PROVIDED_IMAGES;
pub use self::shared::*;

pub use image::{Architecture, Image, ImagePlatform, Os as ContainerOs, PossibleImage};
pub use image::{
Architecture, Image, ImagePlatform, ImageReference, Os as ContainerOs, PossibleImage,
};

use std::process::ExitStatus;

Expand Down
57 changes: 54 additions & 3 deletions src/docker/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::{env, fs, time};

use super::custom::{Dockerfile, PreBuild};
use super::image::PossibleImage;
use super::image::{ImageReference, PossibleImage};
use super::Image;
use super::PROVIDED_IMAGES;
use super::{engine::*, ProvidedImage};
Expand Down Expand Up @@ -1251,6 +1251,13 @@ fn get_user_image(
image = config.zig_image(target).map_err(GetImageError::Other)?;
}

if let Some(image) = &mut image {
if let ImageReference::Identifier(id) = &image.reference {
let target_name = get_target_name(target, uses_zig);
image.reference = ImageReference::Name(format!("{CROSS_IMAGE}/{target_name}{id}"));
}
}

Ok(image)
}

Expand All @@ -1276,7 +1283,7 @@ pub fn get_image_name(
uses_zig: bool,
) -> Result<String, GetImageError> {
if let Some(image) = get_user_image(config, target, uses_zig)? {
return Ok(image.name);
return Ok(image.reference.get().to_owned());
}

let target_name = get_target_name(target, uses_zig);
Expand Down Expand Up @@ -1542,8 +1549,10 @@ pub fn path_hash(path: &Path, count: usize) -> Result<String> {

#[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;
Expand Down Expand Up @@ -1598,6 +1607,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.reference.get(), 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;
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,7 @@ To override the toolchain mounted in the image, set `target.{target}.image.toolc
};
let is_remote = docker::Engine::is_remote();
let engine = docker::Engine::new(None, Some(is_remote), msg_info)?;
let image = image.to_definite_with(&engine, msg_info);
let image = image.to_definite_with(&engine, msg_info)?;
toolchain.replace_host(&image.platform);
Ok(Some(CrossSetup {
config,
Expand Down

0 comments on commit ea76d47

Please sign in to comment.