Repository: cross-rs/cross (rust)

Issue:
Images with cargo and rustc built-in?
Great docker images are provided. When used with cross tool it works great. However, cross can not be invoked in any of the CI systems running builds inside a container. I have found what inheriting the images from "rust" base image instead or ubuntu/debian addresses the issue and the images can be used with cross and in CI too.

Is it possible to modify the images so they are inherited from "rust" image, or alternatively, where rustup/cargo/rustc are installed as a last step? If you would like to keep the current images smallish, is it possible to provide an additional set of the same images where rustup/cargo are installed too?

If these images were provided, I believe rust developers would consolidate and use these, instead of working around the issue creating private not-well-maintained derivatives from your images.

I am keen to submit pull request if you are ok with any of the above options.

Makes sense?

Pull request description:
Add dockerfile for cross.
This uses the official Docker docker-in-docker image for the latest stable, and installs the latest stable Rust version internally. It uses an entrypoint to ensure cargo is on the path, and that cross is installed from the latest git (locked), which when tagged will work well with our releases. It exports the environment variable `CROSS_CONTAINER_IN_CONTAINER` so everything should work as expected.

Closes #468.
Replaces #667.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f535b5f..715269a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -207,6 +207,7 @@ jobs:
             - { target: thumbv7em-none-eabi,              os: ubuntu-latest,                    std: 1 }
             - { target: thumbv7em-none-eabihf,            os: ubuntu-latest,                    std: 1 }
             - { target: thumbv7m-none-eabi,               os: ubuntu-latest,                    std: 1 }
+            - { target: cross,                            os: ubuntu-latest }
 
   build:
     name: target (${{ matrix.pretty }},${{ matrix.os }})
@@ -283,7 +284,7 @@ jobs:
           IMAGE: ${{ steps.build-docker-image.outputs.image }}
         shell: bash
       - name: Test Image
-        if: steps.prepare-meta.outputs.has-image
+        if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant == 'default'
         run: ./ci/test.sh
         env:
           TARGET: ${{ matrix.target }}
@@ -300,6 +301,13 @@ jobs:
           target: ${{ matrix.target }}
           image: ${{ steps.build-docker-image.outputs.image }}
 
+      - name: Test Cross Image
+        if: steps.prepare-meta.outputs.has-image && steps.prepare-meta.outputs.test-variant == 'cross'
+        run: ./ci/test-cross-image.sh
+        env:
+          TARGET: 'aarch64-unknown-linux-gnu'
+        shell: bash
+
       - name: Login to GitHub Container Registry
         if: steps.prepare-meta.outputs.has-image
         uses: docker/login-action@v1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6db56e7..ba3e713 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - #900 - add the option to skip copying build artifacts back to host when using remote cross via `CROSS_REMOTE_SKIP_BUILD_ARTIFACTS`.
 - #891 - support custom user namespace overrides by setting the `CROSS_CONTAINER_USER_NAMESPACE` environment variable. 
 - #890 - support rootless docker via the `CROSS_ROOTLESS_CONTAINER_ENGINE` environment variable.
+- #878 - added an image `ghcr.io/cross-rs/cross` containing cross.
 
 ### Changed
 
diff --git a/docker/Dockerfile.cross b/docker/Dockerfile.cross
new file mode 100644
index 0000000..bb0fd55
--- /dev/null
+++ b/docker/Dockerfile.cross
@@ -0,0 +1,22 @@
+FROM ubuntu:20.04 as rust
+ARG DEBIAN_FRONTEND=noninteractive
+COPY docker/lib.sh docker/cross.sh /
+COPY ./ /project
+RUN /cross.sh /project
+
+# we build our images in 2 steps, to ensure we have a compact
+# image, since we want to add our current subdirectory
+FROM ubuntu:20.04
+COPY --from=rust /root/.cargo /root/.cargo
+COPY --from=rust /root/.rustup /root/.rustup
+
+# need some basic devtools, and requirements for docker
+RUN apt-get update && apt-get install --assume-yes --no-install-recommends \
+    ca-certificates \
+    curl \
+    git
+
+RUN curl -fsSL https://get.docker.com | sh
+
+ENV CROSS_CONTAINER_IN_CONTAINER=1 \
+    PATH=/root/.cargo/bin:$PATH
diff --git a/docker/cross.sh b/docker/cross.sh
new file mode 100755
index 0000000..aef03ad
--- /dev/null
+++ b/docker/cross.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC1090,SC1091
+
+set -x
+set -euo pipefail
+
+. lib.sh
+
+main() {
+    local project_dir="${1}"
+
+    install_packages ca-certificates curl gcc libc6-dev
+
+    cd "${project_dir}"
+    curl --proto "=https" --tlsv1.2 --retry 3 -sSfL https://sh.rustup.rs | sh -s -- -y
+    source "${HOME}"/.cargo/env
+    cargo install --path . --locked
+
+    purge_packages
+
+    rm -rf "${0}"
+}
+
+main "${@}"
diff --git a/xtask/src/build_docker_image.rs b/xtask/src/build_docker_image.rs
index eacaf17..d682892 100644
--- a/xtask/src/build_docker_image.rs
+++ b/xtask/src/build_docker_image.rs
@@ -144,7 +144,8 @@ pub fn build_docker_image(
         }
     }
     let gha = std::env::var("GITHUB_ACTIONS").is_ok();
-    let docker_root = metadata.workspace_root.join("docker");
+    let root = metadata.workspace_root;
+    let docker_root = root.join("docker");
     let cross_toolchains_root = docker_root.join("cross-toolchains").join("docker");
     let targets = targets
         .into_iter()
@@ -237,7 +238,11 @@ pub fn build_docker_image(
             docker_build.args(&["--build-arg", arg]);
         }
 
-        docker_build.arg(".");
+        if target.needs_workspace_root_context() {
+            docker_build.arg(&root);
+        } else {
+            docker_build.arg(".");
+        }
 
         if !dry_run && (force || !push || gha) {
             let result = docker_build.run(msg_info, false);
diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs
index d32b2c0..125fc87 100644
--- a/xtask/src/ci.rs
+++ b/xtask/src/ci.rs
@@ -40,10 +40,11 @@ pub fn ci(args: CiJob, metadata: CargoMetadata) -> cross::Result<()> {
             // Set labels
             let mut labels = vec![];
 
-            labels.push(format!(
-                "org.opencontainers.image.title=cross (for {})",
-                target.triplet
-            ));
+            let image_title = match target.triplet.as_ref() {
+                "cross" => target.triplet.to_string(),
+                _ => format!("cross (for {})", target.triplet),
+            };
+            labels.push(format!("org.opencontainers.image.title={image_title}"));
             labels.push(format!(
                 "org.opencontainers.image.licenses={}",
                 cross_meta.license.as_deref().unwrap_or_default()
@@ -69,6 +70,11 @@ pub fn ci(args: CiJob, metadata: CargoMetadata) -> cross::Result<()> {
             if target.has_ci_image() {
                 gha_output("has-image", "true")
             }
+            if target.is_default_test_image() {
+                gha_output("test-variant", "default")
+            } else {
+                gha_output("test-variant", &target.triplet)
+            }
         }
         CiJob::Check { ref_type, ref_name } => {
             let version = semver::Version::parse(&cross_meta.version)?;
diff --git a/xtask/src/util.rs b/xtask/src/util.rs
index 8eafeb3..312f1aa 100644
--- a/xtask/src/util.rs
+++ b/xtask/src/util.rs
@@ -129,6 +129,16 @@ impl ImageTarget {
             .iter()
             .any(|m| m.builds_image() && m.target == self.triplet && m.sub == self.sub)
     }
+
+    /// Determine if this target uses the default test script
+    pub fn is_default_test_image(&self) -> bool {
+        self.triplet != "cross"
+    }
+
+    /// Determine if this target needs to interact with the project root.
+    pub fn needs_workspace_root_context(&self) -> bool {
+        self.triplet == "cross"
+    }
 }
 
 impl std::str::FromStr for ImageTarget {


Test patch:
diff --git a/ci/test-cross-image.sh b/ci/test-cross-image.sh
new file mode 100755
index 0000000..d94f7b8
--- /dev/null
+++ b/ci/test-cross-image.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# shellcheck disable=SC2086
+
+set -x
+set -eo pipefail
+
+if [[ -z "${TARGET}" ]]; then
+    export TARGET="aarch64-unknown-linux-gnu"
+fi
+if [[ -z "${CROSS_TARGET_CROSS_IMAGE}" ]]; then
+    CROSS_TARGET_CROSS_IMAGE="ghcr.io/cross-rs/cross:main"
+fi
+
+main() {
+    docker run --rm -e TARGET \
+        -v /var/run/docker.sock:/var/run/docker.sock \
+        "${CROSS_TARGET_CROSS_IMAGE}" sh -c '
+#!/usr/bin/env sh
+td="$(mktemp -d)"
+git clone --depth 1 https://github.com/cross-rs/rust-cpp-hello-word "${td}"
+cd "${td}"
+cross run --target "${TARGET}"
+'
+}
+
+main "${@}"