Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying only a tag or subtarget for images in config #1491

Merged
merged 3 commits into from
May 14, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Allow specifying only a tag for images in config
thirteenowls committed May 12, 2024
commit ea76d47cf291ad132a983dd3fbe916ca5fbdd0a0
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
@@ -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
2 changes: 1 addition & 1 deletion src/bin/commands/run.rs
Original file line number Diff line number Diff line change
@@ -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(
6 changes: 3 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -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)
}

@@ -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 }
}

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

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

use super::*;
use crate::shell;
@@ -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(),
)?],
@@ -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(),
)?],
@@ -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"]
@@ -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"]
@@ -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")]));
54 changes: 44 additions & 10 deletions src/docker/image.rs
Original file line number Diff line number Diff line change
@@ -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")
@@ -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![],
}
}
@@ -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>
4 changes: 3 additions & 1 deletion src/docker/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

57 changes: 54 additions & 3 deletions src/docker/shared.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -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)
}

@@ -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);
@@ -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;
@@ -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;
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -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,