Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v4.0.0] - TBD
### Changed
- Updated Java from 17 to 21 (Eclipse Temurin `21.0.11+10`)
- Updated Kotlin compiler for danger-kotlin from `2.2.21` to `2.4.0`

### Fixed
- Git commands no longer fail with "detected dubious ownership" (exit 128) in repositories owned by a different UID than the container user, e.g. projects bind-mounted from the host. This broke Gradle builds using the Ackee plugin, which derives version code via `git rev-list HEAD --count`. Fixed by setting `safe.directory = *` in the system gitconfig, which is safe in a single-user container.

### Security
- npm `ignore-scripts` is now set via `ENV` (`npm_config_ignore_scripts`) instead of `npm config set`, so the supply-chain protection also applies to the `nonroot` Danger install. Previously the setting was written to root's `~/.npmrc` and silently bypassed after the `USER nonroot` switch, leaving the one third-party install unprotected.

## [v3.0.2] - 2026-03-12
### Fixed
- Fixed missing `fontconfig` dependency for EasyLauncher plugin
Expand Down
54 changes: 30 additions & 24 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@
# == Versions ==

ARG ANDROID_CMDLINE_TOOLS_VERSION="14742923"
ARG ANDROID_BUILD_TOOLS_VERSION="36.1.0"
ARG ANDROID_PLATFORM_VERSION="36"

ARG DANGER_JS_VERSION="12.3.4"
ARG DANGER_KOTLIN_VERSION="1.3.4"
ARG DANGER_KOTLIN_CHECKSUM="sha256:232b11680cdfe50c64a6cef1d96d3cd09a857422da1e2dd0464f80c8ddb1afac"

# If you need to update major Java version, you will need to adjust the version in the download link as well
ARG JAVA_VERSION="17.0.18_8"
ARG JAVA_CHECKSUM="sha256:0c94cbb54325c40dcf026143eb621562017db5525727f2d9131a11250f72c450"
# JAVA_VERSION selects both the Temurin base image tag (`<version>-jdk`) and the JAVA_HOME dir name.
ARG JAVA_VERSION="21.0.11_10"

# Needed for danger-kotlin
ARG KOTLINC_VERSION="2.2.21"
ARG KOTLINC_CHECKSUM="sha256:a623871f1cd9c938946948b70ef9170879f0758043885bbd30c32f024e511714"
ARG KOTLINC_VERSION="2.4.0"
ARG KOTLINC_CHECKSUM="sha256:ba1b9e6eb6ddc3275079224f2e9ea4a2b02eef7d59ce2d38404f04b22613c20a"

# == Others ==

Expand Down Expand Up @@ -68,27 +65,24 @@ RUN apt update && apt install -y --no-install-recommends \
unzip

# Disables npm preinstall, postintall and other scripts that might run when any npm package is installed,
# which is usually exploited by supply chain attacks like shai-hulud
RUN npm config set ignore-scripts true
# which is usually exploited by supply chain attacks like shai-hulud. Set via ENV rather than
# `npm config set` so it applies to every user (incl. the nonroot install in danger-installation),
# instead of being written to a single user's ~/.npmrc that a USER switch would bypass.
ENV npm_config_ignore_scripts=true



# == java-installation ==

FROM build AS java-installation

ARG JAVA_CHECKSUM
ARG JAVA_VERSION
# The JDK is copied from Adoptium's official Temurin image instead of downloading and unpacking the
# tarball ourselves. `ADD --unpack` corrupts the large (~142MB) Java 21 module image when the build
# runs under QEMU emulation (e.g. building linux/amd64 on Apple Silicon): lib/modules ends up with
# zero-filled pages and fails at runtime with "ClassFormatError: Incompatible magic value 0".
# COPY --from is a native BuildKit op that copies the pre-extracted, known-good bytes.
FROM eclipse-temurin:${JAVA_VERSION}-jdk AS temurin-jdk

ARG JAVA_TEMP_DIR="java"
# Replace _ with + in version for download link
ENV JAVA_VERSION_PLUS="${JAVA_VERSION/_/+}"
ADD --checksum="$JAVA_CHECKSUM" --unpack=true \
"https://github.com/adoptium/temurin17-binaries/releases/download/jdk-$JAVA_VERSION_PLUS/OpenJDK17U-jdk_x64_linux_hotspot_$JAVA_VERSION.tar.gz" \
"$JAVA_TEMP_DIR"
RUN mkdir -p "$JAVA_HOME" && \
# Temp dir contains unpacked JDK folder, we want to move its contents to JAVA_HOME
mv "$JAVA_TEMP_DIR"/*/* "$JAVA_HOME"
FROM build AS java-installation
COPY --from=temurin-jdk /opt/java/openjdk "$JAVA_HOME"



Expand All @@ -97,9 +91,7 @@ RUN mkdir -p "$JAVA_HOME" && \
# Installs Android SDK. Requires Java to be already installed.
FROM java-installation AS android-sdk-installation

ARG ANDROID_BUILD_TOOLS_VERSION
ARG ANDROID_CMDLINE_TOOLS_VERSION
ARG ANDROID_PLATFORM_VERSION
ARG CMDLINE_TOOLS_DIR
ARG CMDLINE_TOOLS_VERSION_DIR

Expand Down Expand Up @@ -134,8 +126,14 @@ ARG KOTLINC_BASE_PATH
ARG KOTLINC_CHECKSUM
ARG KOTLINC_VERSION

# Recent hardened Debian base images removed /usr/local, so we need to create it ourselves
RUN mkdir -p "$DANGER_BASE_PATH"
# chown of directories where danger will be installed, so nonroot npm process can write there
RUN chown -R nonroot:nonroot "$DANGER_BASE_PATH"
# Recent hardened Debian base images no longer pin npm's global prefix to /usr/local, so npm
# defaults to /usr (installs to /usr/lib/node_modules, not writable by nonroot). Pin it back to
# $DANGER_BASE_PATH so `npm install -g` and `npm root -g` target the chowned, expected location.
ENV npm_config_prefix="$DANGER_BASE_PATH"
# Change to nonroot user for npm install to reduce attack surface if compromised
USER nonroot

Expand Down Expand Up @@ -232,6 +230,14 @@ ARG GIT_LFS_PATH="/usr/bin/git-lfs"
COPY --from=git-lfs-installation "$GIT_LFS_PATH" "$GIT_LFS_PATH"
RUN git lfs install

# Repositories are often owned by a different UID than the runtime user, e.g. projects bind-mounted
# from the host (Docker Desktop passes the host UID through) or CI checkouts made under another
# user. Git >= 2.35.2 then refuses any repo operation with "detected dubious ownership" (exit 128),
# which breaks Gradle plugins that read git state, like the Ackee plugin deriving version code from
# `git rev-list HEAD --count`. The ownership check protects multi-user machines, which this
# single-user container is not, so trust all directories.
RUN git config --system safe.directory '*'

# Remove binaries that might allow privilege escalation
RUN rm -f /bin/su
RUN rm -f /usr/bin/apt /usr/bin/apt-get /usr/bin/apt-cache
Expand Down
2 changes: 1 addition & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Docker image providing a complete environment for running Android builds on GitL

## Contents

- Java 17 (OpenJDK from Eclipse Temurin)
- Java 21 (OpenJDK from Eclipse Temurin)
- Android SDK (cmdline-tools, platform tools, build-tools)
- danger-kotlin + danger-js + Kotlin compiler
- Node.js (system package, required by danger-js)
Expand Down
18 changes: 18 additions & 0 deletions image-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ check_command_exists() {
fi
}

check_git_foreign_owned_repo() {
local repo
repo=$(mktemp -d)
git init -q "$repo"
# GIT_TEST_ASSUME_DIFFERENT_OWNER makes git treat the repo as owned by another user, the same
# situation as a project bind-mounted from the host or checked out by CI under a different UID.
# Without safe.directory=* in the system gitconfig, git refuses to work in such repos with
# "detected dubious ownership" (exit 128), breaking e.g. Gradle plugins that read git state.
if GIT_TEST_ASSUME_DIFFERENT_OWNER=1 git -C "$repo" rev-parse --git-dir > /dev/null; then
echo "git works in repos owned by another user ✅"
else
echo "git fails in repos owned by another user (safe.directory not set?) ❌" >&2
exit 1
fi
rm -rf "$repo"
}

check_dir() {
local dir=$1
if [[ -d "$dir" ]]; then
Expand Down Expand Up @@ -76,6 +93,7 @@ check_command_missing "sudo"
echo
echo "Required custom SW check"
check_command_exists "git"
check_git_foreign_owned_repo
check_command_exists "git-lfs"
check_command_exists "java"

Expand Down
Loading