Skip to content

Commit

Permalink
update ci tools to publish arm64 docker images
Browse files Browse the repository at this point in the history
  • Loading branch information
garasubo committed Mar 4, 2025
1 parent 49338b1 commit 99fb11f
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 37 deletions.
103 changes: 101 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,27 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

generate-matrix-for-build:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate-matrix.outputs.matrix }}
tests: ${{ steps.generate-matrix.outputs.tests }}
steps:
- uses: actions/checkout@v3
with:
ref: ${{ inputs.checkout-ref }}
- uses: ./.github/actions/setup-rust

- name: Generate matrix
id: generate-matrix
run: cargo xtask ci-job target-matrix ${{ github.event_name == 'merge_group' && format('--merge-group {0}', github.ref) || '' }} ${{ inputs.matrix-args || '' }} --for-build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build:
name: target (${{ matrix.pretty }},${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [shellcheck, test, generate-matrix, check]
needs: [shellcheck, test, generate-matrix-for-build, check]
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 != ''
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.pretty }}
Expand Down Expand Up @@ -186,7 +203,7 @@ jobs:
id: build-docker-image
if: steps.prepare-meta.outputs.has-image
timeout-minutes: 120
run: cargo xtask build-docker-image -v "${TARGET}${SUB:+.$SUB}" ${{ matrix.verbose && '-v' || '' }}
run: cargo xtask build-docker-image -v "${TARGET}${SUB:+.$SUB}" ${{ matrix.verbose && '-v' || '' }} --platform ${{ matrix.platforms }}
env:
TARGET: ${{ matrix.target }}
SUB: ${{ matrix.sub }}
Expand Down Expand Up @@ -254,6 +271,88 @@ jobs:
LATEST: ${{ needs.check.outputs.is-latest || 'false' }}
shell: bash

push_image:
name: target (${{ matrix.pretty }},${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [shellcheck, test, generate-matrix, check, build]
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 != ''
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.pretty }}
cancel-in-progress: false
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
outputs:
has-image: ${{ steps.prepare-meta.outputs.has-image }}
images: ${{ steps.build-docker-image.outputs.images && fromJSON(steps.build-docker-image.outputs.images) }}
coverage-artifact: ${{ steps.cov.outputs.artifact-name }}
steps:
- uses: actions/checkout@v3
with:
ref: ${{ inputs.checkout-ref }}

- uses: ./.github/actions/setup-rust

- name: Set up Docker Buildx
if: runner.os == 'Linux'
uses: docker/setup-buildx-action@v2

- name: Build xtask
run: cargo build -p xtask

- name: Prepare Meta
id: prepare-meta
timeout-minutes: 60
run: cargo xtask ci-job prepare-meta "${TARGET}${SUB:+.$SUB}"
env:
TARGET: ${{ matrix.target }}
SUB: ${{ matrix.sub }}
PLATFORMS: ${{ join(matrix.platforms, ',') }}
shell: bash

- name: LLVM instrument coverage
id: cov
uses: ./.github/actions/cargo-llvm-cov
if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant != 'zig'
with:
name: cross-${{matrix.pretty}}

- name: Install cross
if: matrix.deploy
run: cargo install --path . --force --debug

- name: Docker Meta
if: steps.prepare-meta.outputs.has-image
id: docker-meta
uses: docker/metadata-action@v4
with:
images: |
name=${{ steps.prepare-meta.outputs.image }}
labels: |
${{ fromJSON(steps.prepare-meta.outputs.labels) }}
- name: Login to GitHub Container Registry
if: steps.prepare-meta.outputs.has-image
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push image to GitHub Container Registry
if: >
(github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') &&
steps.prepare-meta.outputs.has-image && (
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) ||
startsWith(github.ref, 'refs/tags/v')
)
run: cargo xtask build-docker-image -v --push "${TARGET}${SUB:+.$SUB}"
env:
TARGET: ${{ matrix.target }}
SUB: ${{ matrix.sub }}
LABELS: ${{ steps.docker-meta.outputs.labels }}
LATEST: ${{ needs.check.outputs.is-latest || 'false' }}
shell: bash

# we should always have an artifact from a previous build.
remote:
needs: [test, check, generate-matrix]
Expand Down
15 changes: 9 additions & 6 deletions src/docker/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub trait BuildCommandExt {
fn progress(&mut self, progress: Option<Progress>) -> Result<&mut Self>;
fn verbose(&mut self, verbosity: Verbosity) -> &mut Self;
fn disable_scan_suggest(&mut self) -> &mut Self;
fn cross_labels(&mut self, target: &str, platform: &str) -> &mut Self;
fn cross_labels(&mut self, target: &str, platform: &Vec<&str>) -> &mut Self;
}

impl BuildCommandExt for Command {
Expand Down Expand Up @@ -74,15 +74,18 @@ impl BuildCommandExt for Command {
self.env("DOCKER_SCAN_SUGGEST", "false")
}

fn cross_labels(&mut self, target: &str, platform: &str) -> &mut Self {
fn cross_labels(&mut self, target: &str, platforms: &Vec<&str>) -> &mut Self {
self.args([
"--label",
&format!("{}.for-cross-target={target}", crate::CROSS_LABEL_DOMAIN,),
]);
self.args([
"--label",
&format!("{}.runs-with={platform}", crate::CROSS_LABEL_DOMAIN,),
])
for platform in platforms {
self.args([
"--label",
&format!("{}.runs-with={platform}", crate::CROSS_LABEL_DOMAIN,),
]);
}
self
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/docker/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ impl<'a> Dockerfile<'a> {

docker_build.progress(None)?;
docker_build.verbose(msg_info.verbosity);
docker_build.cross_labels(options.target.triple(), self.runs_with().target.triple());
docker_build.cross_labels(
options.target.triple(),
&vec![self.runs_with().target.triple()],
);

docker_build.args([
"--label",
Expand Down
4 changes: 4 additions & 0 deletions targets.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ dylib = true
std = true
run = true
runners = "qemu-user qemu-system"
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]

[[target]]
target = "arm-unknown-linux-gnueabi"
Expand All @@ -61,6 +62,7 @@ cpp = true
dylib = true
std = true
run = true
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]

[[target]]
target = "arm-unknown-linux-gnueabihf"
Expand All @@ -69,6 +71,7 @@ cpp = true
dylib = true
std = true
run = true
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]

[[target]]
target = "armv7-unknown-linux-gnueabi"
Expand All @@ -78,6 +81,7 @@ dylib = true
std = true
run = true
runners = "qemu-user"
platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]

[[target]]
target = "armv7-unknown-linux-gnueabihf"
Expand Down
27 changes: 18 additions & 9 deletions xtask/src/build_docker_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,30 +176,35 @@ pub fn build_docker_image(
};

let mut results = vec![];
for (platform, (target, dockerfile)) in targets
.iter()
.flat_map(|t| platforms.iter().map(move |p| (p, t)))
{
for (target, dockerfile) in targets.iter() {
if gha && targets.len() > 1 {
gha_print("::group::Build {target}");
} else {
msg_info.note(format_args!("Build {target} for {}", platform.target))?;
msg_info.note(format_args!(
"Build {target} for {}",
platforms
.iter()
.map(|p| p.target.to_string())
.collect::<Vec<_>>()
.join(" and ")
))?;
}
let mut docker_build = engine.command();
docker_build.invoke_build_command();
let has_buildkit = docker::Engine::has_buildkit();
docker_build.current_dir(&docker_root);

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

Expand Down Expand Up @@ -287,7 +293,10 @@ pub fn build_docker_image(
docker_build.args(["--label", label]);
}

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

docker_build.progress(progress)?;
Expand Down
94 changes: 75 additions & 19 deletions xtask/src/ci/target_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub struct TargetMatrix {
pub merge_group: Option<String>,
#[clap(subcommand)]
pub subcommand: Option<TargetMatrixSub>,
#[clap(long)]
pub for_build: bool,
}

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -91,6 +93,7 @@ impl TargetMatrix {
weekly: false,
merge_group: Some(_) | None,
subcommand: None,
for_build: false | true,
}
) || is_default_try
{
Expand All @@ -99,26 +102,54 @@ impl TargetMatrix {

app.filter(&mut matrix);

let matrix = matrix
.iter()
.map(|target| TargetMatrixElement {
pretty: target.to_image_target().alt(),
platforms: target.platforms(),
target: &target.target,
sub: target.sub.as_deref(),
os: &target.os,
run: target.run.map(|b| b as u8),
deploy: target.deploy.map(|b| b as u8),
build_std: target.build_std.map(|b| b as u8),
cpp: target.cpp.map(|b| b as u8),
dylib: target.dylib.map(|b| b as u8),
runners: target.runners.as_deref(),
std: target.std.map(|b| b as u8),
verbose: app.verbose,
})
.collect::<Vec<_>>();
let json = if self.for_build {
let matrix = matrix
.iter()
.flat_map(|target| {
target
.platforms()
.iter()
.map(move |platform| (target, platform))
})
.map(|(target, platform)| TargetMatrixElementForBuild {
pretty: target.to_image_target().alt(),
platform: platform.to_string(),
target: &target.target,
sub: target.sub.as_deref(),
os: &target.os,
run: target.run.map(|b| b as u8),
deploy: target.deploy.map(|b| b as u8),
build_std: target.build_std.map(|b| b as u8),
cpp: target.cpp.map(|b| b as u8),
dylib: target.dylib.map(|b| b as u8),
runners: target.runners.as_deref(),
std: target.std.map(|b| b as u8),
verbose: app.verbose,
})
.collect::<Vec<_>>();
serde_json::to_string(&matrix)?
} else {
let matrix = matrix
.iter()
.map(|target| TargetMatrixElement {
pretty: target.to_image_target().alt(),
platforms: target.platforms(),
target: &target.target,
sub: target.sub.as_deref(),
os: &target.os,
run: target.run.map(|b| b as u8),
deploy: target.deploy.map(|b| b as u8),
build_std: target.build_std.map(|b| b as u8),
cpp: target.cpp.map(|b| b as u8),
dylib: target.dylib.map(|b| b as u8),
runners: target.runners.as_deref(),
std: target.std.map(|b| b as u8),
verbose: app.verbose,
})
.collect::<Vec<_>>();
serde_json::to_string(&matrix)?
};

let json = serde_json::to_string(&matrix)?;
gha_output("matrix", &json)?;
let tests = serde_json::to_string(&app.tests()?)?;
gha_output("tests", &tests)?;
Expand Down Expand Up @@ -265,6 +296,31 @@ struct TargetMatrixElement<'a> {
std: Option<u8>,
verbose: bool,
}
#[derive(Serialize)]
#[serde(rename_all = "kebab-case")]
struct TargetMatrixElementForBuild<'a> {
pretty: String,
platform: String,
target: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
sub: Option<&'a str>,
os: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
run: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
deploy: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
build_std: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
cpp: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
dylib: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
runners: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
std: Option<u8>,
verbose: bool,
}

#[derive(Parser, Debug, PartialEq, Eq)]
#[clap(no_binary_name = true)]
Expand Down

0 comments on commit 99fb11f

Please sign in to comment.