Skip to content

Commit 78357ea

Browse files
authored
Allow specifying only a tag or subtarget for images in config (#1491)
Closes #1169. Also supports specifying only a hash (`@sha256:...`) or subtarget (`-centos`).
2 parents 53a45db + 350e314 commit 78357ea

File tree

10 files changed

+223
-76
lines changed

10 files changed

+223
-76
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 or subtarget for images in config",
3+
"issues": [1169],
4+
"type": "changed"
5+
}

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ search = "<!-- next-url -->"
8686
replace = "<!-- next-url -->\n\n[Unreleased]: https://github.com/cross-rs/{{crate_name}}/compare/v{{version}}...HEAD"
8787
exactly = 1
8888

89+
[[package.metadata.release.pre-release-replacements]]
90+
file = "docs/config_file.md"
91+
search = "(# Translates to `.*?:).*?(-centos`)"
92+
replace = "${1}{{version}}$2"
93+
exactly = 1
94+
8995
[package.metadata.binstall]
9096
pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ target }.tar.gz"
9197
bin-dir = "{ bin }{ binary-ext }"

docs/config_file.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,27 @@ 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+
237+
You can also specify a subtarget with no tag nor image name:
238+
239+
```toml
240+
[target.x86_64-unknown-linux-gnu]
241+
# Translates to `ghcr.io/cross-rs/x86_64-unknown-linux-gnu:0.3.0-centos`
242+
image = "-centos"
243+
```
244+
224245
The `image` key can also take the toolchains/platforms supported by the image:
225246

226247
```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: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ use std::str::FromStr;
22

33
use serde::{Deserialize, Serialize};
44

5-
use crate::{errors::*, shell::MessageInfo, TargetTriple};
5+
use crate::{
6+
docker::{CROSS_IMAGE, DEFAULT_IMAGE_VERSION},
7+
errors::*,
8+
shell::MessageInfo,
9+
TargetTriple,
10+
};
611

712
use super::Engine;
813

@@ -21,18 +26,23 @@ impl std::fmt::Display for Image {
2126

2227
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
2328
pub struct PossibleImage {
24-
pub name: String,
29+
#[serde(rename = "name")]
30+
pub reference: ImageReference,
2531
// The toolchain triple the image is built for
2632
pub toolchain: Vec<ImagePlatform>,
2733
}
2834

2935
impl PossibleImage {
30-
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
36+
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Result<Image> {
37+
let ImageReference::Name(name) = self.reference.clone() else {
38+
eyre::bail!("cannot make definite Image from unqualified PossibleImage");
39+
};
40+
3141
if self.toolchain.is_empty() {
32-
Image {
33-
name: self.name.clone(),
42+
Ok(Image {
43+
name,
3444
platform: ImagePlatform::DEFAULT,
35-
}
45+
})
3646
} else {
3747
let platform = if self.toolchain.len() == 1 {
3848
self.toolchain.first().expect("should contain at least one")
@@ -71,18 +81,18 @@ impl PossibleImage {
7181
platform
7282
}
7383
};
74-
Image {
84+
Ok(Image {
7585
platform: platform.clone(),
76-
name: self.name.clone(),
77-
}
86+
name,
87+
})
7888
}
7989
}
8090
}
8191

8292
impl<T: AsRef<str>> From<T> for PossibleImage {
8393
fn from(s: T) -> Self {
8494
PossibleImage {
85-
name: s.as_ref().to_owned(),
95+
reference: s.as_ref().to_owned().into(),
8696
toolchain: vec![],
8797
}
8898
}
@@ -98,9 +108,57 @@ impl FromStr for PossibleImage {
98108

99109
impl std::fmt::Display for PossibleImage {
100110
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101-
f.write_str(&self.name)
111+
f.write_str(self.reference.get())
102112
}
103113
}
114+
115+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
116+
#[serde(from = "String", untagged)]
117+
pub enum ImageReference {
118+
/// Partially qualified reference, with or without tag/digest
119+
Name(String),
120+
/// Unqualified reference, only a tag or digest
121+
Identifier(String),
122+
/// Unqualified reference, only a subtarget
123+
Subtarget(String),
124+
}
125+
126+
impl ImageReference {
127+
pub fn get(&self) -> &str {
128+
match self {
129+
Self::Name(s) => s,
130+
Self::Identifier(s) => s,
131+
Self::Subtarget(s) => s,
132+
}
133+
}
134+
135+
pub fn ensure_qualified(&mut self, target_name: &str) {
136+
let image_name = match self {
137+
Self::Name(_) => return,
138+
Self::Identifier(id) => {
139+
format!("{CROSS_IMAGE}/{target_name}{id}")
140+
}
141+
Self::Subtarget(sub) => {
142+
format!("{CROSS_IMAGE}/{target_name}:{DEFAULT_IMAGE_VERSION}{sub}")
143+
}
144+
};
145+
146+
*self = Self::Name(image_name);
147+
}
148+
}
149+
150+
impl From<String> for ImageReference {
151+
fn from(s: String) -> Self {
152+
if s.starts_with('-') {
153+
Self::Subtarget(s)
154+
} else if s.starts_with(':') || s.starts_with('@') {
155+
Self::Identifier(s)
156+
} else {
157+
Self::Name(s)
158+
}
159+
}
160+
}
161+
104162
/// The architecture/platform to use in the image
105163
///
106164
/// <https://github.com/containerd/containerd/blob/release/1.6/platforms/platforms.go#L63>

src/docker/mod.rs

Lines changed: 7 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

@@ -31,6 +33,10 @@ impl ProvidedImage {
3133
pub fn image_name(&self, repository: &str, tag: &str) -> String {
3234
image_name(self.name, self.sub, repository, tag)
3335
}
36+
37+
pub fn default_image_name(&self) -> String {
38+
self.image_name(CROSS_IMAGE, DEFAULT_IMAGE_VERSION)
39+
}
3440
}
3541

3642
pub fn image_name(target: &str, sub: Option<&str>, repository: &str, tag: &str) -> String {

0 commit comments

Comments
 (0)