Skip to content

Set up cross for limited but substantial local testing of s390x and Android #1895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 38 commits into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4687e6d
Start toward testing s390x via cross
EliahKagan Mar 4, 2025
a7db588
Add `system`-scope config var in container
EliahKagan Mar 4, 2025
3a296ae
Add `jq` to container; simplify container build command
EliahKagan Mar 4, 2025
2a155b6
Put experiment details (except dockerfile) in a script
EliahKagan Mar 4, 2025
121eb3f
Use image:tag naming style for image; let more env through
EliahKagan Mar 5, 2025
1b25974
Start toward testing armv7-linux-androideabi
EliahKagan Mar 8, 2025
07022ee
feat: Show unexpected stderr in `umask` panic message
EliahKagan Mar 9, 2025
8d596b4
Add `env` testtools subcommand to show the environment
EliahKagan Mar 9, 2025
ab2b26c
Recognize `NO_PRELOAD_CXX` to not preload Android C++ library
EliahKagan Mar 9, 2025
b26c2a2
Only patch `/android-runner` if it exists
EliahKagan Mar 9, 2025
35627b5
Change `interpolate_user` tests to cover Android
EliahKagan Mar 9, 2025
c19bf1d
Test `gix_testtools::umask()` on Android targets
EliahKagan Mar 9, 2025
d3a9f5f
Move `cross` testing commands into `justfile`
EliahKagan Mar 9, 2025
8d7154d
Ensure `cross` test images have `patch`
EliahKagan Mar 9, 2025
239eee4
Keep the `cross` test config from affecting other `cross` usage
EliahKagan Mar 9, 2025
2fb7db8
Remove potentially misleading "(limited)" for Android
EliahKagan Mar 9, 2025
809fb2f
Move `env` subcommand to `internal-tools`
EliahKagan Mar 10, 2025
c9b0abe
Have `it env` use quoted/debug form when newlines are present
EliahKagan Mar 10, 2025
a6afbfb
Let backtrace-related env vars into the `cross` container
EliahKagan Mar 17, 2025
eeb5279
Start on `test-cross` CI job definition
EliahKagan Mar 17, 2025
cfeb06d
Divide up the `test-cross` CI steps better
EliahKagan Mar 17, 2025
7d66f9e
Thanks clippy
EliahKagan Mar 18, 2025
c40ead9
Configure binfmt_misc for QEMU on CI
EliahKagan Mar 18, 2025
db8697b
Start on trying to get an s390x `git` in the s390x cross container
EliahKagan Mar 9, 2025
154b1a6
Try to enable ports APT sources unconditionally
EliahKagan Mar 10, 2025
ec63b31
Show that the problem still happens with `add-apt-repository`
EliahKagan Mar 10, 2025
a3fed77
Convert the problem to failure to install perl:s390x
EliahKagan Mar 10, 2025
f0773ee
Install host-arch `perl` deps, force target arch `git` to use them
EliahKagan Mar 10, 2025
9be7e99
Manually set up PPA + other fixups
EliahKagan Mar 10, 2025
6e7122f
Make sure we actually have `dpkg-architecture`
EliahKagan Mar 11, 2025
1df3894
Let `apt-get download` drop privileges for the download
EliahKagan Mar 11, 2025
6e3e068
Test max on s390x (and potentially elsewhere supported)
EliahKagan Mar 11, 2025
a203bf4
Start toward moving the dense `RUN` to a commented script
EliahKagan Mar 11, 2025
c3ea44b
Finish moving the dense `RUN` to a commented script
EliahKagan Mar 12, 2025
33e825a
Only install `git` strangely when there is reason to do so
EliahKagan Mar 19, 2025
e88e635
Make it easier to pass arguments to test executable
EliahKagan Mar 19, 2025
803d054
Adjust `cross` CI jobs for changed `just` recipe syntax
EliahKagan Mar 19, 2025
73dcf57
Remove `test-cross` CI job definition
EliahKagan Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions etc/docker/Dockerfile.test-cross
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ARG TARGET
FROM ghcr.io/cross-rs/${TARGET}:latest

ARG TARGET
COPY customize.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/customize.sh && \
/usr/local/bin/customize.sh "$TARGET"
133 changes: 133 additions & 0 deletions etc/docker/test-cross-context/customize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/bin/bash
set -euxC

target="$1"
test -n "$target"

# Arrange for the indirect `tzdata` dependency to be installed and configured
# without prompting for the time zone. (Passing `-y` is not enough.)
export DEBIAN_FRONTEND=noninteractive TZ=UTC

# Install tools for setting up APT repositores. Install `apt-utils` before the
# others, so the installation of `gnupg` can use it for debconf.
apt-get update
apt-get install --no-install-recommends -y apt-utils
apt-get install --no-install-recommends -y apt-transport-https dpkg-dev gnupg
type dpkg-architecture # Make sure we really have this.

# Decide what architecture to use for `git`, shared libraries `git` links to,
# and shared libraries gitoxide links to when building `max`. Instead of this
# custom logic, we could use `$CROSS_DEB_ARCH`, which `cross` tries to provide
# (https://github.com/cross-rs/cross/blob/v0.2.5/src/lib.rs#L268), and which is
# available for roughly the same architectures where this logic gets a nonempty
# value. But using `$CROSS_DEB_ARCH` may make it harder to build and test the
# image manually. In particular, if it is not passed, we would conclude that we
# should install the versions of those packages with the host's architecture.
apt_suffix=
if target_arch="$(dpkg-architecture --host-type "$target" --query DEB_HOST_ARCH)"
then
dpkg --add-architecture "$target_arch"
apt_suffix=":$target_arch"
printf 'INFO: Using target architecture for `git` and libs in container.\n'
printf 'INFO: This architecture is %s.\n' "$target_arch"
else
apt_suffix=''
printf 'WARNING: Using HOST architecture for `git` and libs in container.\n'
fi

# Get release codename. Like `lsb_release -sc`. (`lsb_release` may be absent.)
release="$(sed -n 's/^VERSION_CODENAME=//p' /etc/os-release)"

# Add the git-core PPA manually. (Faster than installing `add-apt-repository`.)
echo "deb https://ppa.launchpadcontent.net/git-core/ppa/ubuntu $release main" \
>/etc/apt/sources.list.d/git-core-ubuntu-ppa.list
apt-key adv --keyserver keyserver.ubuntu.com \
--recv-keys F911AB184317630C59970973E363C90F8F1B6217
apt-get update

# Remove the old `git` and associated packages.
apt-get purge --autoremove -y git

# Git dependencies. These are for the desired architecture, except `git-man` is
# the same package for all architectures, and we can't always install `perl` or
# `liberror-perl` for the desired architecture (at least in s390x).
# TODO(maint): Resolve these dynamically to support future `cross` base images.
git_deps=(
git-man
"libc6$apt_suffix"
"libcurl3-gnutls$apt_suffix"
"libexpat1$apt_suffix"
liberror-perl
"libpcre2-8-0$apt_suffix"
"zlib1g$apt_suffix"
perl
)

# Other dependencies for running the gitoxide test suite and fixture scripts,
# and for building and testing gitoxide for feature sets beyond `max-pure`.
gix_test_deps=(
ca-certificates
cmake
"curl$apt_suffix"
jq
"libc-dev$apt_suffix"
"libssl-dev$apt_suffix"
patch
pkgconf
)

if test -n "$apt_suffix"; then
# Install everything we need except `git` (and what we already have). We
# can't necessarily install `git` this way, because it insists on `perl`
# and `liberror-perl` dependencies of the same architecture as it. These
# may not be possible to install in a mixed environment, where most
# packages are a different architecture, and where `perl` is a dependency
# of other important packages. So we will install everything else first
# (then manually add `git`).
apt-get install --no-install-recommends -y \
"${git_deps[@]}" "${gix_test_deps[@]}" file

# Add `git` by manually downloading it and installing it with `dpkg`,
# forcing installation to proceed even if its `perl` and `liberror-perl`
# dependencies, as declared by `git`, are absent. (We have already
# installed them, but in a possibly different architecture. `git` can still
# use them, because its use is to run scripts, rather than to link to a
# shared library they provide.) It is preferred to let `apt-get download`
# drop privileges to the `_apt` user during download, so we download it
# inside `/tmp`. But we create a subdirectory so it is safe to make
# assumptions about what files globs can expand to, even if `/tmp` is
# mounted to an outside share temp dir on a multi-user system.
mkdir /tmp/dl # Don't use `-p`; if it exists already, we cannot trust it.
chown _apt /tmp/dl # Use owner, as the container may have no `_apt` group.
(cd /tmp/dl && apt-get download "git$apt_suffix")
dpkg --ignore-depends="perl$apt_suffix,liberror-perl$apt_suffix" \
-i /tmp/dl/git[-_]*.deb
rm -r /tmp/dl
else
# Install everything we need, including `git`.
apt-get install --no-install-recommends -y git "${gix_test_deps[@]}" file
fi

# Show information about the newly installed `git` (and ensure it can run).
git version --build-options
git="$(command -v git)"
file -- "$git"

# Clean up files related to package management that we won't need anymore.
apt-get clean
rm -rf /var/lib/apt/lists/*

# If this image has a runner script `cross` uses for Android, patch the script
# to add the ability to suppress its customization of `LD_PRELOAD`. The runner
# script sets `LD_PRELOAD` to the path of `libc++_shared.so` in the Android NDK
# (https://github.com/cross-rs/cross/blob/v0.2.5/docker/android-runner#L34).
# But this causes a problem for us. When a host-architecture program is run,
# `ld.so` shows a message about the "wrong ELF class". Such programs can still
# run, but when we rely on their specific output to stderr, fixtures and tests
# fail. The change we make here lets us set `NO_PRELOAD_CXX=1` to avoid that.
runner=/android-runner
patch='s/^[[:blank:]]*export LD_PRELOAD=/test "${NO_PRELOAD_CXX:-0}" != 0 || &/'
if test -f "$runner"; then sed -i.orig "$patch" -- "$runner"; fi

# Ensure a nonempty Git `system` scope (for the `installation_config` tests).
git config --system gitoxide.imaginary.arbitraryVariable arbitraryValue
26 changes: 26 additions & 0 deletions etc/docker/test-cross.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# `cross` configuration for running tests. Treated like `Cross.toml` if enabled
# with `CROSS_CONFIG=etc/docker/test-cross.toml`. This avoids affecting other
# `cross` usage, e.g. in `release.yml`. See `cross-test` recipes in `justfile`.

[build.env]
passthrough = [
"CI",
"GITHUB_ACTIONS",
"GIX_CREDENTIALS_HELPER_STDERR",
"GIX_EXTERNAL_COMMAND_STDERR",
"GIX_OBJECT_CACHE_MEMORY",
"GIX_PACK_CACHE_MEMORY",
"GIX_TEST_CREATE_ARCHIVES_EVEN_ON_CI",
"GIX_TEST_EXPECT_REDUCED_TRUST",
"GIX_TEST_IGNORE_ARCHIVES",
"GIX_VERSION",
"NO_PRELOAD_CXX",
"RUST_BACKTRACE",
"RUST_LIB_BACKTRACE",
]

[target.armv7-linux-androideabi]
image = "cross-rs-gitoxide:armv7-linux-androideabi"

[target.s390x-unknown-linux-gnu]
image = "cross-rs-gitoxide:s390x-unknown-linux-gnu"
6 changes: 3 additions & 3 deletions gix-config-value/tests/value/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ mod interpolate {
Ok(())
}

#[cfg(windows)]
#[cfg(any(target_os = "windows", target_os = "android"))]
#[test]
fn tilde_with_given_user_is_unsupported_on_windows() {
fn tilde_with_given_user_is_unsupported_on_windows_and_android() {
assert!(matches!(
interpolate_without_context("~baz/foo/bar"),
Err(gix_config_value::path::interpolate::Error::UserInterpolationUnsupported)
));
}

#[cfg(not(windows))]
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[test]
fn tilde_with_given_user() -> crate::Result {
let home = std::env::current_dir()?;
Expand Down
19 changes: 19 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,25 @@ journey-tests-async: dbg
cargo build -p gix-testtools
dbg="$({{ j }} dbg)" && tests/journey.sh "$dbg/ein" "$dbg/gix" "$dbg/jtt" async

# Build a customized `cross` container image for testing
cross-image target:
docker build --build-arg "TARGET={{ target }}" \
-t "cross-rs-gitoxide:{{ target }}" \
-f etc/docker/Dockerfile.test-cross etc/docker/test-cross-context

# Test another platform with `cross`
cross-test target options test-options: (cross-image target)
CROSS_CONFIG=etc/docker/test-cross.toml NO_PRELOAD_CXX=1 \
cross test --workspace --no-fail-fast --target {{ target }} \
{{ options }} -- --skip realpath::fuzzed_timeout {{ test-options }}

# Test s390x with `cross`
cross-test-s390x: (cross-test 's390x-unknown-linux-gnu' '' '')

# Test Android with `cross` (max-pure)
cross-test-android: (cross-test 'armv7-linux-androideabi'
'--no-default-features --features max-pure' '')

# Run `cargo diet` on all crates to see that they are still in bounds
check-size:
etc/check-package-size.sh
Expand Down
16 changes: 16 additions & 0 deletions tests/it/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ pub enum Subcommands {
/// current repository. Its main use is checking that fixture scripts are have correct modes.
#[clap(visible_alias = "cm")]
CheckMode {},
/// Print environment variables as `NAME=value` lines.
///
/// It is useful to be able to observe environment variables that are set when running code
/// with tools such as `cargo` or `cross`. Commands like `cargo run -p internal-tools -- env`
/// include environment changes from `cargo` itself. With `cross`, changes are more extensive,
/// due to effects of `build.env.passthrough`, container customization, and preexisting special
/// cases in wrapper scripts shipped in default `cross` containers (such as to `LD_PRELOAD`).
///
/// Since one use for checking environment variables is to investigate the effects of
/// environments that contain variable names or values that are not valid Unicode, this avoids
/// requiring that environment variables all be Unicode. Any name or value that is not Unicode
/// is shown in its Rust debug representation. This is always quoted. To decrease ambiguity,
/// any name or value containing a literal double quote or newline is also shown in its debug
/// representation. Names and values without such content are shown literally and not quoted.
#[clap(visible_alias = "e")]
Env {},
}

#[derive(Clone)]
Expand Down
14 changes: 14 additions & 0 deletions tests/it/src/commands/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub(super) mod function {
pub fn env() -> anyhow::Result<()> {
for (name, value) in std::env::vars_os() {
println!("{}={}", repr(&name), repr(&value));
}
Ok(())
}

fn repr(text: &std::ffi::OsStr) -> String {
text.to_str()
.filter(|s| !s.chars().any(|c| c == '"' || c == '\n'))
.map_or_else(|| format!("{text:?}"), ToOwned::to_owned)
}
}
3 changes: 3 additions & 0 deletions tests/it/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ pub use git_to_sh::function::git_to_sh;

pub mod check_mode;
pub use check_mode::function::check_mode;

pub mod env;
pub use env::function::env;
1 change: 1 addition & 0 deletions tests/it/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn main() -> anyhow::Result<()> {
patterns,
} => commands::copy_royal(dry_run, &worktree_root, destination_dir, patterns),
Subcommands::CheckMode {} => commands::check_mode(),
Subcommands::Env {} => commands::env(),
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/tools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ pub fn umask() -> u32 {
.output()
.expect("can execute `sh -c umask`");
assert!(output.status.success(), "`sh -c umask` failed");
assert!(output.stderr.is_empty(), "`sh -c umask` unexpected message");
assert_eq!(output.stderr.as_bstr(), "", "`sh -c umask` unexpected message");
let text = output.stdout.to_str().expect("valid Unicode").trim();
u32::from_str_radix(text, 8).expect("parses as octal number")
}
Expand Down
5 changes: 4 additions & 1 deletion tests/tools/tests/umask.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#[test]
#[cfg(unix)]
#[cfg_attr(not(target_os = "linux"), ignore = "The test itself uses /proc")]
#[cfg_attr(
not(any(target_os = "linux", target_os = "android")),
ignore = "The test itself uses /proc"
)]
fn umask() {
use std::fs::File;
use std::io::{BufRead, BufReader};
Expand Down
Loading