Skip to content

Commit 9c899c5

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

File tree

6 files changed

+191
-37
lines changed

6 files changed

+191
-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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ 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(options.target.triple(), &vec![self.runs_with().target.triple()]);
101101

102102
docker_build.args([
103103
"--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: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,30 +176,26 @@ 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!("Build {target} for {}", platforms.iter().map(|p| p.target.to_string()).collect::<Vec<_>>().join(" and ")))?;
187184
}
188185
let mut docker_build = engine.command();
189186
docker_build.invoke_build_command();
190187
let has_buildkit = docker::Engine::has_buildkit();
191188
docker_build.current_dir(&docker_root);
192189

193-
let docker_platform = platform.docker_platform();
190+
let docker_platforms: Vec<String> = platforms.iter().map(|p| p.docker_platform()).collect();
194191
let mut dockerfile = dockerfile.clone();
195-
docker_build.args(["--platform", &docker_platform]);
196192
let uppercase_triple = target.name.to_ascii_uppercase().replace('-', "_");
197193
docker_build.args([
198194
"--build-arg",
199195
&format!("CROSS_TARGET_TRIPLE={}", uppercase_triple),
200196
]);
201197
// 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)? {
198+
if docker_platforms.len() == 1 && has_native_image(docker_platforms[0].as_str(), target, msg_info)? {
203199
let dockerfile_name = match target.sub.as_deref() {
204200
Some(sub) => format!("Dockerfile.native.{sub}"),
205201
None => "Dockerfile.native".to_owned(),
@@ -220,6 +216,7 @@ pub fn build_docker_image(
220216
} else if no_output {
221217
msg_info.fatal("cannot specify `--no-output` with engine that does not support the `--output` flag", 1);
222218
} else if has_buildkit {
219+
// TODO: docker daemon doesn't support loading multi-arch, so it will fail when platforms.len() > 1
223220
docker_build.arg("--load");
224221
}
225222

@@ -287,7 +284,7 @@ pub fn build_docker_image(
287284
docker_build.args(["--label", label]);
288285
}
289286

290-
docker_build.cross_labels(&target.name, platform.target.triple());
287+
docker_build.cross_labels(&target.name, &platforms.iter().map(|p| p.target.triple()).collect());
291288
docker_build.args(["--file", &dockerfile]);
292289

293290
docker_build.progress(progress)?;

xtask/src/ci/target_matrix.rs

Lines changed: 70 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,49 @@ 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| target.platforms().iter().map(move |platform| (target, platform)))
109+
.map(|(target, platform)| TargetMatrixElementForBuild {
110+
pretty: target.to_image_target().alt(),
111+
platform: platform.to_string(),
112+
target: &target.target,
113+
sub: target.sub.as_deref(),
114+
os: &target.os,
115+
run: target.run.map(|b| b as u8),
116+
deploy: target.deploy.map(|b| b as u8),
117+
build_std: target.build_std.map(|b| b as u8),
118+
cpp: target.cpp.map(|b| b as u8),
119+
dylib: target.dylib.map(|b| b as u8),
120+
runners: target.runners.as_deref(),
121+
std: target.std.map(|b| b as u8),
122+
verbose: app.verbose,
123+
})
124+
.collect::<Vec<_>>();
125+
serde_json::to_string(&matrix)?
126+
} else {
127+
let matrix = matrix
128+
.iter()
129+
.map(|target| TargetMatrixElement {
130+
pretty: target.to_image_target().alt(),
131+
platforms: target.platforms(),
132+
target: &target.target,
133+
sub: target.sub.as_deref(),
134+
os: &target.os,
135+
run: target.run.map(|b| b as u8),
136+
deploy: target.deploy.map(|b| b as u8),
137+
build_std: target.build_std.map(|b| b as u8),
138+
cpp: target.cpp.map(|b| b as u8),
139+
dylib: target.dylib.map(|b| b as u8),
140+
runners: target.runners.as_deref(),
141+
std: target.std.map(|b| b as u8),
142+
verbose: app.verbose,
143+
})
144+
.collect::<Vec<_>>();
145+
serde_json::to_string(&matrix)?
146+
};
120147

121-
let json = serde_json::to_string(&matrix)?;
122148
gha_output("matrix", &json)?;
123149
let tests = serde_json::to_string(&app.tests()?)?;
124150
gha_output("tests", &tests)?;
@@ -265,6 +291,31 @@ struct TargetMatrixElement<'a> {
265291
std: Option<u8>,
266292
verbose: bool,
267293
}
294+
#[derive(Serialize)]
295+
#[serde(rename_all = "kebab-case")]
296+
struct TargetMatrixElementForBuild<'a> {
297+
pretty: String,
298+
platform: String,
299+
target: &'a str,
300+
#[serde(skip_serializing_if = "Option::is_none")]
301+
sub: Option<&'a str>,
302+
os: &'a str,
303+
#[serde(skip_serializing_if = "Option::is_none")]
304+
run: Option<u8>,
305+
#[serde(skip_serializing_if = "Option::is_none")]
306+
deploy: Option<u8>,
307+
#[serde(skip_serializing_if = "Option::is_none")]
308+
build_std: Option<u8>,
309+
#[serde(skip_serializing_if = "Option::is_none")]
310+
cpp: Option<u8>,
311+
#[serde(skip_serializing_if = "Option::is_none")]
312+
dylib: Option<u8>,
313+
#[serde(skip_serializing_if = "Option::is_none")]
314+
runners: Option<&'a str>,
315+
#[serde(skip_serializing_if = "Option::is_none")]
316+
std: Option<u8>,
317+
verbose: bool,
318+
}
268319

269320
#[derive(Parser, Debug, PartialEq, Eq)]
270321
#[clap(no_binary_name = true)]

0 commit comments

Comments
 (0)