Skip to content

Commit 99fb11f

Browse files
committed
update ci tools to publish arm64 docker images
1 parent 49338b1 commit 99fb11f

File tree

6 files changed

+211
-37
lines changed

6 files changed

+211
-37
lines changed

.github/workflows/ci.yml

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,27 @@ jobs:
123123
env:
124124
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
125125

126+
generate-matrix-for-build:
127+
runs-on: ubuntu-latest
128+
outputs:
129+
matrix: ${{ steps.generate-matrix.outputs.matrix }}
130+
tests: ${{ steps.generate-matrix.outputs.tests }}
131+
steps:
132+
- uses: actions/checkout@v3
133+
with:
134+
ref: ${{ inputs.checkout-ref }}
135+
- uses: ./.github/actions/setup-rust
136+
137+
- name: Generate matrix
138+
id: generate-matrix
139+
run: cargo xtask ci-job target-matrix ${{ github.event_name == 'merge_group' && format('--merge-group {0}', github.ref) || '' }} ${{ inputs.matrix-args || '' }} --for-build
140+
env:
141+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
142+
126143
build:
127144
name: target (${{ matrix.pretty }},${{ matrix.os }})
128145
runs-on: ${{ matrix.os }}
129-
needs: [shellcheck, test, generate-matrix, check]
146+
needs: [shellcheck, test, generate-matrix-for-build, check]
130147
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' || github.event_name == 'issue_comment' || github.event_name == 'schedule') && needs.generate-matrix.outputs.matrix != '{}' && needs.generate-matrix.outputs.matrix != '[]' && needs.generate-matrix.outputs.matrix != ''
131148
concurrency:
132149
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.pretty }}
@@ -186,7 +203,7 @@ jobs:
186203
id: build-docker-image
187204
if: steps.prepare-meta.outputs.has-image
188205
timeout-minutes: 120
189-
run: cargo xtask build-docker-image -v "${TARGET}${SUB:+.$SUB}" ${{ matrix.verbose && '-v' || '' }}
206+
run: cargo xtask build-docker-image -v "${TARGET}${SUB:+.$SUB}" ${{ matrix.verbose && '-v' || '' }} --platform ${{ matrix.platforms }}
190207
env:
191208
TARGET: ${{ matrix.target }}
192209
SUB: ${{ matrix.sub }}
@@ -254,6 +271,88 @@ jobs:
254271
LATEST: ${{ needs.check.outputs.is-latest || 'false' }}
255272
shell: bash
256273

274+
push_image:
275+
name: target (${{ matrix.pretty }},${{ matrix.os }})
276+
runs-on: ${{ matrix.os }}
277+
needs: [shellcheck, test, generate-matrix, check, build]
278+
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' || github.event_name == 'issue_comment' || github.event_name == 'schedule') && needs.generate-matrix.outputs.matrix != '{}' && needs.generate-matrix.outputs.matrix != '[]' && needs.generate-matrix.outputs.matrix != ''
279+
concurrency:
280+
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.pretty }}
281+
cancel-in-progress: false
282+
strategy:
283+
fail-fast: false
284+
matrix:
285+
include: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
286+
outputs:
287+
has-image: ${{ steps.prepare-meta.outputs.has-image }}
288+
images: ${{ steps.build-docker-image.outputs.images && fromJSON(steps.build-docker-image.outputs.images) }}
289+
coverage-artifact: ${{ steps.cov.outputs.artifact-name }}
290+
steps:
291+
- uses: actions/checkout@v3
292+
with:
293+
ref: ${{ inputs.checkout-ref }}
294+
295+
- uses: ./.github/actions/setup-rust
296+
297+
- name: Set up Docker Buildx
298+
if: runner.os == 'Linux'
299+
uses: docker/setup-buildx-action@v2
300+
301+
- name: Build xtask
302+
run: cargo build -p xtask
303+
304+
- name: Prepare Meta
305+
id: prepare-meta
306+
timeout-minutes: 60
307+
run: cargo xtask ci-job prepare-meta "${TARGET}${SUB:+.$SUB}"
308+
env:
309+
TARGET: ${{ matrix.target }}
310+
SUB: ${{ matrix.sub }}
311+
PLATFORMS: ${{ join(matrix.platforms, ',') }}
312+
shell: bash
313+
314+
- name: LLVM instrument coverage
315+
id: cov
316+
uses: ./.github/actions/cargo-llvm-cov
317+
if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant != 'zig'
318+
with:
319+
name: cross-${{matrix.pretty}}
320+
321+
- name: Install cross
322+
if: matrix.deploy
323+
run: cargo install --path . --force --debug
324+
325+
- name: Docker Meta
326+
if: steps.prepare-meta.outputs.has-image
327+
id: docker-meta
328+
uses: docker/metadata-action@v4
329+
with:
330+
images: |
331+
name=${{ steps.prepare-meta.outputs.image }}
332+
labels: |
333+
${{ fromJSON(steps.prepare-meta.outputs.labels) }}
334+
- name: Login to GitHub Container Registry
335+
if: steps.prepare-meta.outputs.has-image
336+
uses: docker/login-action@v2
337+
with:
338+
registry: ghcr.io
339+
username: ${{ github.actor }}
340+
password: ${{ secrets.GITHUB_TOKEN }}
341+
- name: Push image to GitHub Container Registry
342+
if: >
343+
(github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') &&
344+
steps.prepare-meta.outputs.has-image && (
345+
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) ||
346+
startsWith(github.ref, 'refs/tags/v')
347+
)
348+
run: cargo xtask build-docker-image -v --push "${TARGET}${SUB:+.$SUB}"
349+
env:
350+
TARGET: ${{ matrix.target }}
351+
SUB: ${{ matrix.sub }}
352+
LABELS: ${{ steps.docker-meta.outputs.labels }}
353+
LATEST: ${{ needs.check.outputs.is-latest || 'false' }}
354+
shell: bash
355+
257356
# we should always have an artifact from a previous build.
258357
remote:
259358
needs: [test, check, generate-matrix]

src/docker/build.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub trait BuildCommandExt {
4141
fn progress(&mut self, progress: Option<Progress>) -> Result<&mut Self>;
4242
fn verbose(&mut self, verbosity: Verbosity) -> &mut Self;
4343
fn disable_scan_suggest(&mut self) -> &mut Self;
44-
fn cross_labels(&mut self, target: &str, platform: &str) -> &mut Self;
44+
fn cross_labels(&mut self, target: &str, platform: &Vec<&str>) -> &mut Self;
4545
}
4646

4747
impl BuildCommandExt for Command {
@@ -74,15 +74,18 @@ impl BuildCommandExt for Command {
7474
self.env("DOCKER_SCAN_SUGGEST", "false")
7575
}
7676

77-
fn cross_labels(&mut self, target: &str, platform: &str) -> &mut Self {
77+
fn cross_labels(&mut self, target: &str, platforms: &Vec<&str>) -> &mut Self {
7878
self.args([
7979
"--label",
8080
&format!("{}.for-cross-target={target}", crate::CROSS_LABEL_DOMAIN,),
8181
]);
82-
self.args([
83-
"--label",
84-
&format!("{}.runs-with={platform}", crate::CROSS_LABEL_DOMAIN,),
85-
])
82+
for platform in platforms {
83+
self.args([
84+
"--label",
85+
&format!("{}.runs-with={platform}", crate::CROSS_LABEL_DOMAIN,),
86+
]);
87+
}
88+
self
8689
}
8790
}
8891

src/docker/custom.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ impl<'a> Dockerfile<'a> {
9797

9898
docker_build.progress(None)?;
9999
docker_build.verbose(msg_info.verbosity);
100-
docker_build.cross_labels(options.target.triple(), self.runs_with().target.triple());
100+
docker_build.cross_labels(
101+
options.target.triple(),
102+
&vec![self.runs_with().target.triple()],
103+
);
101104

102105
docker_build.args([
103106
"--label",

targets.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ dylib = true
5353
std = true
5454
run = true
5555
runners = "qemu-user qemu-system"
56+
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
5657

5758
[[target]]
5859
target = "arm-unknown-linux-gnueabi"
@@ -61,6 +62,7 @@ cpp = true
6162
dylib = true
6263
std = true
6364
run = true
65+
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
6466

6567
[[target]]
6668
target = "arm-unknown-linux-gnueabihf"
@@ -69,6 +71,7 @@ cpp = true
6971
dylib = true
7072
std = true
7173
run = true
74+
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
7275

7376
[[target]]
7477
target = "armv7-unknown-linux-gnueabi"
@@ -78,6 +81,7 @@ dylib = true
7881
std = true
7982
run = true
8083
runners = "qemu-user"
84+
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
8185

8286
[[target]]
8387
target = "armv7-unknown-linux-gnueabihf"

xtask/src/build_docker_image.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,30 +176,35 @@ pub fn build_docker_image(
176176
};
177177

178178
let mut results = vec![];
179-
for (platform, (target, dockerfile)) in targets
180-
.iter()
181-
.flat_map(|t| platforms.iter().map(move |p| (p, t)))
182-
{
179+
for (target, dockerfile) in targets.iter() {
183180
if gha && targets.len() > 1 {
184181
gha_print("::group::Build {target}");
185182
} else {
186-
msg_info.note(format_args!("Build {target} for {}", platform.target))?;
183+
msg_info.note(format_args!(
184+
"Build {target} for {}",
185+
platforms
186+
.iter()
187+
.map(|p| p.target.to_string())
188+
.collect::<Vec<_>>()
189+
.join(" and ")
190+
))?;
187191
}
188192
let mut docker_build = engine.command();
189193
docker_build.invoke_build_command();
190194
let has_buildkit = docker::Engine::has_buildkit();
191195
docker_build.current_dir(&docker_root);
192196

193-
let docker_platform = platform.docker_platform();
197+
let docker_platforms: Vec<String> = platforms.iter().map(|p| p.docker_platform()).collect();
194198
let mut dockerfile = dockerfile.clone();
195-
docker_build.args(["--platform", &docker_platform]);
196199
let uppercase_triple = target.name.to_ascii_uppercase().replace('-', "_");
197200
docker_build.args([
198201
"--build-arg",
199202
&format!("CROSS_TARGET_TRIPLE={}", uppercase_triple),
200203
]);
201204
// add our platform, and determine if we need to use a native docker image
202-
if has_native_image(docker_platform.as_str(), target, msg_info)? {
205+
if docker_platforms.len() == 1
206+
&& has_native_image(docker_platforms[0].as_str(), target, msg_info)?
207+
{
203208
let dockerfile_name = match target.sub.as_deref() {
204209
Some(sub) => format!("Dockerfile.native.{sub}"),
205210
None => "Dockerfile.native".to_owned(),
@@ -220,6 +225,7 @@ pub fn build_docker_image(
220225
} else if no_output {
221226
msg_info.fatal("cannot specify `--no-output` with engine that does not support the `--output` flag", 1);
222227
} else if has_buildkit {
228+
// TODO: docker daemon doesn't support loading multi-arch, so it will fail when platforms.len() > 1
223229
docker_build.arg("--load");
224230
}
225231

@@ -287,7 +293,10 @@ pub fn build_docker_image(
287293
docker_build.args(["--label", label]);
288294
}
289295

290-
docker_build.cross_labels(&target.name, platform.target.triple());
296+
docker_build.cross_labels(
297+
&target.name,
298+
&platforms.iter().map(|p| p.target.triple()).collect(),
299+
);
291300
docker_build.args(["--file", &dockerfile]);
292301

293302
docker_build.progress(progress)?;

xtask/src/ci/target_matrix.rs

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub struct TargetMatrix {
1717
pub merge_group: Option<String>,
1818
#[clap(subcommand)]
1919
pub subcommand: Option<TargetMatrixSub>,
20+
#[clap(long)]
21+
pub for_build: bool,
2022
}
2123

2224
#[derive(Parser, Debug)]
@@ -91,6 +93,7 @@ impl TargetMatrix {
9193
weekly: false,
9294
merge_group: Some(_) | None,
9395
subcommand: None,
96+
for_build: false | true,
9497
}
9598
) || is_default_try
9699
{
@@ -99,26 +102,54 @@ impl TargetMatrix {
99102

100103
app.filter(&mut matrix);
101104

102-
let matrix = matrix
103-
.iter()
104-
.map(|target| TargetMatrixElement {
105-
pretty: target.to_image_target().alt(),
106-
platforms: target.platforms(),
107-
target: &target.target,
108-
sub: target.sub.as_deref(),
109-
os: &target.os,
110-
run: target.run.map(|b| b as u8),
111-
deploy: target.deploy.map(|b| b as u8),
112-
build_std: target.build_std.map(|b| b as u8),
113-
cpp: target.cpp.map(|b| b as u8),
114-
dylib: target.dylib.map(|b| b as u8),
115-
runners: target.runners.as_deref(),
116-
std: target.std.map(|b| b as u8),
117-
verbose: app.verbose,
118-
})
119-
.collect::<Vec<_>>();
105+
let json = if self.for_build {
106+
let matrix = matrix
107+
.iter()
108+
.flat_map(|target| {
109+
target
110+
.platforms()
111+
.iter()
112+
.map(move |platform| (target, platform))
113+
})
114+
.map(|(target, platform)| TargetMatrixElementForBuild {
115+
pretty: target.to_image_target().alt(),
116+
platform: platform.to_string(),
117+
target: &target.target,
118+
sub: target.sub.as_deref(),
119+
os: &target.os,
120+
run: target.run.map(|b| b as u8),
121+
deploy: target.deploy.map(|b| b as u8),
122+
build_std: target.build_std.map(|b| b as u8),
123+
cpp: target.cpp.map(|b| b as u8),
124+
dylib: target.dylib.map(|b| b as u8),
125+
runners: target.runners.as_deref(),
126+
std: target.std.map(|b| b as u8),
127+
verbose: app.verbose,
128+
})
129+
.collect::<Vec<_>>();
130+
serde_json::to_string(&matrix)?
131+
} else {
132+
let matrix = matrix
133+
.iter()
134+
.map(|target| TargetMatrixElement {
135+
pretty: target.to_image_target().alt(),
136+
platforms: target.platforms(),
137+
target: &target.target,
138+
sub: target.sub.as_deref(),
139+
os: &target.os,
140+
run: target.run.map(|b| b as u8),
141+
deploy: target.deploy.map(|b| b as u8),
142+
build_std: target.build_std.map(|b| b as u8),
143+
cpp: target.cpp.map(|b| b as u8),
144+
dylib: target.dylib.map(|b| b as u8),
145+
runners: target.runners.as_deref(),
146+
std: target.std.map(|b| b as u8),
147+
verbose: app.verbose,
148+
})
149+
.collect::<Vec<_>>();
150+
serde_json::to_string(&matrix)?
151+
};
120152

121-
let json = serde_json::to_string(&matrix)?;
122153
gha_output("matrix", &json)?;
123154
let tests = serde_json::to_string(&app.tests()?)?;
124155
gha_output("tests", &tests)?;
@@ -265,6 +296,31 @@ struct TargetMatrixElement<'a> {
265296
std: Option<u8>,
266297
verbose: bool,
267298
}
299+
#[derive(Serialize)]
300+
#[serde(rename_all = "kebab-case")]
301+
struct TargetMatrixElementForBuild<'a> {
302+
pretty: String,
303+
platform: String,
304+
target: &'a str,
305+
#[serde(skip_serializing_if = "Option::is_none")]
306+
sub: Option<&'a str>,
307+
os: &'a str,
308+
#[serde(skip_serializing_if = "Option::is_none")]
309+
run: Option<u8>,
310+
#[serde(skip_serializing_if = "Option::is_none")]
311+
deploy: Option<u8>,
312+
#[serde(skip_serializing_if = "Option::is_none")]
313+
build_std: Option<u8>,
314+
#[serde(skip_serializing_if = "Option::is_none")]
315+
cpp: Option<u8>,
316+
#[serde(skip_serializing_if = "Option::is_none")]
317+
dylib: Option<u8>,
318+
#[serde(skip_serializing_if = "Option::is_none")]
319+
runners: Option<&'a str>,
320+
#[serde(skip_serializing_if = "Option::is_none")]
321+
std: Option<u8>,
322+
verbose: bool,
323+
}
268324

269325
#[derive(Parser, Debug, PartialEq, Eq)]
270326
#[clap(no_binary_name = true)]

0 commit comments

Comments
 (0)