Skip to content

Commit ea76d47

Browse files
committed
Allow specifying only a tag for images in config
1 parent 57cc9cf commit ea76d47

File tree

9 files changed

+130
-25
lines changed

9 files changed

+130
-25
lines changed

.changes/1491.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"description": "Allow specifying only a tag for images in config",
3+
"issues": [1169],
4+
"type": "changed"
5+
}

docs/config_file.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,19 @@ the default one. Normal Docker behavior applies, so:
221221
- If only `image:latest` is specified, then Docker won't look in Docker Hub.
222222
- If the tag is omitted, then Docker will use the `latest` tag.
223223

224+
If you specify a tag but no image name, `cross` will use the default image with
225+
the tag you provided:
226+
227+
```toml
228+
[target.aarch64-unknown-linux-gnu]
229+
# Translates to `ghcr.io/cross-rs/aarch64-unknown-linux-gnu:edge`
230+
image = ":edge"
231+
232+
[target.x86_64-unknown-linux-musl]
233+
# Translates to `ghcr.io/cross-rs/x86_64-unknown-linux-musl@sha256:77db671d8356a64ae72a3e1415e63f547f26d374fbe3c4762c1cd36c7eac7b99`
234+
image = "@sha256:77db671d8356a64ae72a3e1415e63f547f26d374fbe3c4762c1cd36c7eac7b99"
235+
```
236+
224237
The `image` key can also take the toolchains/platforms supported by the image:
225238

226239
```toml

src/bin/commands/run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl Run {
7676
}
7777
};
7878

79-
let image = image.to_definite_with(&engine, msg_info);
79+
let image = image.to_definite_with(&engine, msg_info)?;
8080

8181
let paths = docker::DockerPaths::create(&engine, metadata, cwd, toolchain, msg_info)?;
8282
let options = docker::DockerOptions::new(

src/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ impl<T: PartialEq> PartialEq<(Option<T>, Option<T>)> for ConfVal<T> {
4141
}
4242

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

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

@@ -352,7 +352,7 @@ impl Config {
352352
}
353353

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

src/cross_toml.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ where
591591

592592
#[cfg(test)]
593593
mod tests {
594-
use crate::docker::ImagePlatform;
594+
use crate::docker::{ImagePlatform, ImageReference};
595595

596596
use super::*;
597597
use crate::shell;
@@ -741,7 +741,7 @@ mod tests {
741741
build_std: None,
742742
zig: None,
743743
image: Some(PossibleImage {
744-
name: "test-image".to_owned(),
744+
reference: ImageReference::Name("test-image".to_owned()),
745745
toolchain: vec![ImagePlatform::from_target(
746746
"aarch64-unknown-linux-musl".into(),
747747
)?],
@@ -773,7 +773,7 @@ mod tests {
773773
enable: None,
774774
version: None,
775775
image: Some(PossibleImage {
776-
name: "zig:local".to_owned(),
776+
reference: ImageReference::Name("zig:local".to_owned()),
777777
toolchain: vec![ImagePlatform::from_target(
778778
"aarch64-unknown-linux-gnu".into(),
779779
)?],
@@ -939,7 +939,7 @@ mod tests {
939939
[target.target3]
940940
xargo = false
941941
build-std = true
942-
image = "test-image3"
942+
image = "@sha256:test-image3"
943943
944944
[target.target3.env]
945945
volumes = ["VOL3_ARG"]
@@ -978,7 +978,7 @@ mod tests {
978978
[target.target3]
979979
xargo = false
980980
build-std = true
981-
image = "test-image3"
981+
image = "@sha256:test-image3"
982982
983983
[target.target3.env]
984984
volumes = ["VOL3_ARG"]
@@ -1042,7 +1042,7 @@ mod tests {
10421042
let target3 = &targets[&Target::new_custom("target3")];
10431043
assert_eq!(target3.build_std, Some(BuildStd::Bool(true)));
10441044
assert_eq!(target3.xargo, Some(false));
1045-
assert_eq!(target3.image, Some(p!("test-image3")));
1045+
assert_eq!(target3.image, Some(p!("@sha256:test-image3")));
10461046
assert_eq!(target3.pre_build, None);
10471047
assert_eq!(target3.dockerfile, None);
10481048
assert_eq!(target3.env.passthrough, Some(vec![p!("VAR3")]));

src/docker/image.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,23 @@ impl std::fmt::Display for Image {
2121

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

2930
impl PossibleImage {
30-
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
31+
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Result<Image> {
32+
let ImageReference::Name(name) = self.reference.clone() else {
33+
eyre::bail!("cannot make definite Image from unqualified PossibleImage");
34+
};
35+
3136
if self.toolchain.is_empty() {
32-
Image {
33-
name: self.name.clone(),
37+
Ok(Image {
38+
name,
3439
platform: ImagePlatform::DEFAULT,
35-
}
40+
})
3641
} else {
3742
let platform = if self.toolchain.len() == 1 {
3843
self.toolchain.first().expect("should contain at least one")
@@ -71,18 +76,18 @@ impl PossibleImage {
7176
platform
7277
}
7378
};
74-
Image {
79+
Ok(Image {
7580
platform: platform.clone(),
76-
name: self.name.clone(),
77-
}
81+
name,
82+
})
7883
}
7984
}
8085
}
8186

8287
impl<T: AsRef<str>> From<T> for PossibleImage {
8388
fn from(s: T) -> Self {
8489
PossibleImage {
85-
name: s.as_ref().to_owned(),
90+
reference: s.as_ref().to_owned().into(),
8691
toolchain: vec![],
8792
}
8893
}
@@ -98,9 +103,38 @@ impl FromStr for PossibleImage {
98103

99104
impl std::fmt::Display for PossibleImage {
100105
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101-
f.write_str(&self.name)
106+
f.write_str(self.reference.get())
102107
}
103108
}
109+
110+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
111+
#[serde(from = "String", untagged)]
112+
pub enum ImageReference {
113+
/// Partially qualified reference, with or without tag/digest
114+
Name(String),
115+
/// Unqualified reference, only a tag or digest
116+
Identifier(String),
117+
}
118+
119+
impl ImageReference {
120+
pub fn get(&self) -> &str {
121+
match self {
122+
Self::Name(s) => s,
123+
Self::Identifier(s) => s,
124+
}
125+
}
126+
}
127+
128+
impl From<String> for ImageReference {
129+
fn from(s: String) -> Self {
130+
if s.starts_with(':') || s.starts_with('@') {
131+
Self::Identifier(s)
132+
} else {
133+
Self::Name(s)
134+
}
135+
}
136+
}
137+
104138
/// The architecture/platform to use in the image
105139
///
106140
/// <https://github.com/containerd/containerd/blob/release/1.6/platforms/platforms.go#L63>

src/docker/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ pub use self::engine::*;
1212
pub use self::provided_images::PROVIDED_IMAGES;
1313
pub use self::shared::*;
1414

15-
pub use image::{Architecture, Image, ImagePlatform, Os as ContainerOs, PossibleImage};
15+
pub use image::{
16+
Architecture, Image, ImagePlatform, ImageReference, Os as ContainerOs, PossibleImage,
17+
};
1618

1719
use std::process::ExitStatus;
1820

src/docker/shared.rs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
55
use std::{env, fs, time};
66

77
use super::custom::{Dockerfile, PreBuild};
8-
use super::image::PossibleImage;
8+
use super::image::{ImageReference, PossibleImage};
99
use super::Image;
1010
use super::PROVIDED_IMAGES;
1111
use super::{engine::*, ProvidedImage};
@@ -1251,6 +1251,13 @@ fn get_user_image(
12511251
image = config.zig_image(target).map_err(GetImageError::Other)?;
12521252
}
12531253

1254+
if let Some(image) = &mut image {
1255+
if let ImageReference::Identifier(id) = &image.reference {
1256+
let target_name = get_target_name(target, uses_zig);
1257+
image.reference = ImageReference::Name(format!("{CROSS_IMAGE}/{target_name}{id}"));
1258+
}
1259+
}
1260+
12541261
Ok(image)
12551262
}
12561263

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

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

15431550
#[cfg(test)]
15441551
mod tests {
1552+
use std::collections::HashMap;
1553+
15451554
use super::*;
1546-
use crate::id;
1555+
use crate::{config::Environment, id};
15471556

15481557
#[cfg(not(target_os = "windows"))]
15491558
use crate::file::PathExt;
@@ -1598,6 +1607,48 @@ mod tests {
15981607
}
15991608
}
16001609

1610+
#[test]
1611+
fn test_tag_only_image() -> Result<()> {
1612+
let target: Target = TargetTriple::X86_64UnknownLinuxGnu.into();
1613+
let test = |map, expected_ver: &str, expected_ver_zig: &str| -> Result<()> {
1614+
let env = Environment::new(Some(map));
1615+
let config = Config::new_with(None, env);
1616+
for (uses_zig, expected_ver) in [(false, expected_ver), (true, expected_ver_zig)] {
1617+
let expected_image_target = if uses_zig {
1618+
"zig"
1619+
} else {
1620+
"x86_64-unknown-linux-gnu"
1621+
};
1622+
let expected = format!("ghcr.io/cross-rs/{expected_image_target}{expected_ver}");
1623+
1624+
let image = get_image(&config, &target, uses_zig)?;
1625+
assert_eq!(image.reference.get(), expected);
1626+
let image_name = get_image_name(&config, &target, uses_zig)?;
1627+
assert_eq!(image_name, expected);
1628+
}
1629+
Ok(())
1630+
};
1631+
1632+
let default_ver = format!(":{DEFAULT_IMAGE_VERSION}");
1633+
let mut map = HashMap::new();
1634+
test(map.clone(), &default_ver, &default_ver)?;
1635+
1636+
map.insert("CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE", ":edge");
1637+
test(map.clone(), ":edge", ":edge")?;
1638+
1639+
// `image` always takes precedence over `zig.image`, even when `uses_zig` is `true`
1640+
map.insert(
1641+
"CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_ZIG_IMAGE",
1642+
"@sha256:foobar",
1643+
);
1644+
test(map.clone(), ":edge", ":edge")?;
1645+
1646+
map.remove("CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE");
1647+
test(map.clone(), &default_ver, "@sha256:foobar")?;
1648+
1649+
Ok(())
1650+
}
1651+
16011652
mod directories {
16021653
use super::*;
16031654
use crate::cargo::cargo_metadata_with_args;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ To override the toolchain mounted in the image, set `target.{target}.image.toolc
810810
};
811811
let is_remote = docker::Engine::is_remote();
812812
let engine = docker::Engine::new(None, Some(is_remote), msg_info)?;
813-
let image = image.to_definite_with(&engine, msg_info);
813+
let image = image.to_definite_with(&engine, msg_info)?;
814814
toolchain.replace_host(&image.platform);
815815
Ok(Some(CrossSetup {
816816
config,

0 commit comments

Comments
 (0)