add support for ARM (#485)

* build and run on arm64

* multiarch docker build

* add ARM64 warning to README.md
This commit is contained in:
Mark Crossen 2024-03-02 20:09:41 -07:00 committed by GitHub
parent bd76f1c541
commit 98d3e475d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 95 additions and 28 deletions

View File

@ -12,9 +12,11 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: build - name: build
env: env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: | run: |
./build.py ./build.py --multiarch

View File

@ -11,10 +11,12 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: build and push - name: build and push
if: ${{ env.DOCKER_USERNAME != '' && env.DOCKER_PASSWORD != '' }} if: ${{ env.DOCKER_USERNAME != '' && env.DOCKER_PASSWORD != '' }}
env: env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: | run: |
./build.py --push-tags ./build.py --push-tags --multiarch

View File

@ -1,5 +1,8 @@
# Factorio [![Docker Version](https://img.shields.io/docker/v/factoriotools/factorio?sort=semver)](https://hub.docker.com/r/factoriotools/factorio/) [![Docker Pulls](https://img.shields.io/docker/pulls/factoriotools/factorio.svg?maxAge=600)](https://hub.docker.com/r/factoriotools/factorio/) [![Docker Stars](https://img.shields.io/docker/stars/factoriotools/factorio.svg?maxAge=600)](https://hub.docker.com/r/factoriotools/factorio/) # Factorio [![Docker Version](https://img.shields.io/docker/v/factoriotools/factorio?sort=semver)](https://hub.docker.com/r/factoriotools/factorio/) [![Docker Pulls](https://img.shields.io/docker/pulls/factoriotools/factorio.svg?maxAge=600)](https://hub.docker.com/r/factoriotools/factorio/) [![Docker Stars](https://img.shields.io/docker/stars/factoriotools/factorio.svg?maxAge=600)](https://hub.docker.com/r/factoriotools/factorio/)
> [!NOTE]
> Support for ARM is experimental. Expect crashes and lag if you try to run this on a raspberry pi.
[中文](./README_zh_CN.md) [中文](./README_zh_CN.md)
<!-- start autogeneration tags --> <!-- start autogeneration tags -->

View File

@ -7,14 +7,40 @@ import shutil
import sys import sys
import tempfile import tempfile
def build_dockerfile(sha256, version, tags):
build_dir = tempfile.mktemp()
shutil.copytree("docker", build_dir)
build_command = ["docker", "build", "--build-arg", f"VERSION={version}", PLATFORMS = [
"--build-arg", f"SHA256={sha256}", "."] "linux/arm64",
for tag in tags: "linux/amd64",
build_command.extend(["-t", f"factoriotools/factorio:{tag}"]) ]
def create_builder(build_dir, builder_name, platform):
check_exists_command = ["docker", "buildx", "inspect", builder_name]
if subprocess.run(check_exists_command, stderr=subprocess.DEVNULL).returncode != 0:
create_command = ["docker", "buildx", "create", "--platform", platform, "--name", builder_name]
try:
subprocess.run(create_command, cwd=build_dir, check=True)
except subprocess.CalledProcessError:
print("Creating builder failed")
exit(1)
def build_and_push_multiarch(build_dir, build_args, push):
builder_name = "factoriotools-multiarch"
platform=",".join(PLATFORMS)
create_builder(build_dir, builder_name, platform)
build_command = ["docker", "buildx", "build", "--platform", platform, "--builder", builder_name] + build_args
if push:
build_command.append("--push")
try:
subprocess.run(build_command, cwd=build_dir, check=True)
except subprocess.CalledProcessError:
print("Build and push of image failed")
exit(1)
def build_singlearch(build_dir, build_args):
build_command = ["docker", "build"] + build_args
try: try:
subprocess.run(build_command, cwd=build_dir, check=True) subprocess.run(build_command, cwd=build_dir, check=True)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
@ -22,6 +48,30 @@ def build_dockerfile(sha256, version, tags):
exit(1) exit(1)
def push_singlearch(tags):
for tag in tags:
try:
subprocess.run(["docker", "push", f"factoriotools/factorio:{tag}"],
check=True)
except subprocess.CalledProcessError:
print("Docker push failed")
exit(1)
def build_and_push(sha256, version, tags, push, multiarch):
build_dir = tempfile.mktemp()
shutil.copytree("docker", build_dir)
build_args = ["--build-arg", f"VERSION={version}", "--build-arg", f"SHA256={sha256}", "."]
for tag in tags:
build_args.extend(["-t", f"factoriotools/factorio:{tag}"])
if multiarch:
build_and_push_multiarch(build_dir, build_args, push)
else:
build_singlearch(build_dir, build_args)
if push:
push_singlearch(tags)
def login(): def login():
try: try:
username = os.environ["DOCKER_USERNAME"] username = os.environ["DOCKER_USERNAME"]
@ -35,7 +85,7 @@ def login():
exit(1) exit(1)
def main(push_tags=False): def main(push_tags=False, multiarch=False):
with open(os.path.join(os.path.dirname(__file__), "buildinfo.json")) as file_handle: with open(os.path.join(os.path.dirname(__file__), "buildinfo.json")) as file_handle:
builddata = json.load(file_handle) builddata = json.load(file_handle)
@ -45,18 +95,15 @@ def main(push_tags=False):
for version, buildinfo in builddata.items(): for version, buildinfo in builddata.items():
sha256 = buildinfo["sha256"] sha256 = buildinfo["sha256"]
tags = buildinfo["tags"] tags = buildinfo["tags"]
build_dockerfile(sha256, version, tags) build_and_push(sha256, version, tags, push_tags, multiarch)
if not push_tags:
continue
for tag in tags:
try:
subprocess.run(["docker", "push", f"factoriotools/factorio:{tag}"],
check=True)
except subprocess.CalledProcessError:
print("Docker push failed")
exit(1)
if __name__ == '__main__': if __name__ == '__main__':
push_tags = len(sys.argv) > 1 and sys.argv[1] == "--push-tags" push_tags = False
main(push_tags) multiarch = False
for arg in sys.argv[1:]:
if arg == "--push-tags":
push_tags = True
elif arg == "--multiarch":
multiarch = True
main(push_tags, multiarch)

View File

@ -6,6 +6,7 @@ ARG USER=factorio
ARG GROUP=factorio ARG GROUP=factorio
ARG PUID=845 ARG PUID=845
ARG PGID=845 ARG PGID=845
ARG BOX64_VERSION=v0.2.4
# version checksum of the archive to download # version checksum of the archive to download
ARG VERSION ARG VERSION
@ -40,13 +41,21 @@ RUN set -ox pipefail \
&& mkdir -p /opt /factorio \ && mkdir -p /opt /factorio \
&& apt-get -q update \ && apt-get -q update \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils --no-install-recommends \ && DEBIAN_FRONTEND=noninteractive apt-get -qy install ca-certificates curl jq pwgen xz-utils --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& curl -sSL "https://www.factorio.com/get-download/$VERSION/headless/linux64" -o "$archive" --retry $CURL_RETRIES\ && curl -sSL "https://www.factorio.com/get-download/$VERSION/headless/linux64" -o "$archive" --retry $CURL_RETRIES\
&& echo "$SHA256 $archive" | sha256sum -c \ && echo "$SHA256 $archive" | sha256sum -c \
|| (sha256sum "$archive" && file "$archive" && exit 1) \ || (sha256sum "$archive" && file "$archive" && exit 1) \
&& tar xf "$archive" --directory /opt \ && tar xf "$archive" --directory /opt \
&& chmod ugo=rwx /opt/factorio \ && chmod ugo=rwx /opt/factorio \
&& rm "$archive" \ && rm "$archive" \
&& if [[ "$(uname -m)" == "aarch64" ]]; then \
echo "installing ARM compatability layer" \
&& DEBIAN_FRONTEND=noninteractive apt-get -qy install unzip --no-install-recommends \
&& curl -LO https://github.com/ptitSeb/box64/releases/download/${BOX64_VERSION}/box64-GENERIC_ARM-RelWithDebInfo.zip \
&& unzip box64-GENERIC_ARM-RelWithDebInfo.zip -d /bin \
&& rm -f box64-GENERIC_ARM-RelWithDebInfo.zip \
&& chmod +x /bin/box64; \
fi \
&& rm -rf /var/lib/apt/lists/* \
&& ln -s "$SCENARIOS" /opt/factorio/scenarios \ && ln -s "$SCENARIOS" /opt/factorio/scenarios \
&& ln -s "$SAVES" /opt/factorio/saves \ && ln -s "$SAVES" /opt/factorio/saves \
&& mkdir -p /opt/factorio/config/ \ && mkdir -p /opt/factorio/config/ \

View File

@ -43,6 +43,7 @@ if [[ ${UPDATE_MODS_ON_START:-} == "true" ]]; then
./docker-update-mods.sh ./docker-update-mods.sh
fi fi
EXEC=""
if [[ $(id -u) = 0 ]]; then if [[ $(id -u) = 0 ]]; then
# Update the User and Group ID based on the PUID/PGID variables # Update the User and Group ID based on the PUID/PGID variables
usermod -o -u "$PUID" factorio usermod -o -u "$PUID" factorio
@ -50,9 +51,12 @@ if [[ $(id -u) = 0 ]]; then
# Take ownership of factorio data if running as root # Take ownership of factorio data if running as root
chown -R factorio:factorio "$FACTORIO_VOL" chown -R factorio:factorio "$FACTORIO_VOL"
# Drop to the factorio user # Drop to the factorio user
SU_EXEC="runuser -u factorio -g factorio --" EXEC="runuser -u factorio -g factorio --"
else fi
SU_EXEC="" if [[ -f /bin/box64 ]]; then
# Use an emulator to run on ARM hosts
# this only gets installed when the target docker platform is linux/arm64
EXEC="$EXEC /bin/box64"
fi fi
sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini
@ -71,7 +75,7 @@ if [[ $GENERATE_NEW_SAVE == true ]]; then
if [[ -f "$SAVES/$SAVE_NAME.zip" ]]; then if [[ -f "$SAVES/$SAVE_NAME.zip" ]]; then
echo "Map $SAVES/$SAVE_NAME.zip already exists, skipping map generation" echo "Map $SAVES/$SAVE_NAME.zip already exists, skipping map generation"
else else
$SU_EXEC /opt/factorio/bin/x64/factorio \ $EXEC /opt/factorio/bin/x64/factorio \
--create "$SAVES/$SAVE_NAME.zip" \ --create "$SAVES/$SAVE_NAME.zip" \
--map-gen-settings "$CONFIG/map-gen-settings.json" \ --map-gen-settings "$CONFIG/map-gen-settings.json" \
--map-settings "$CONFIG/map-settings.json" --map-settings "$CONFIG/map-settings.json"
@ -105,4 +109,4 @@ else
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
exec $SU_EXEC /opt/factorio/bin/x64/factorio "${FLAGS[@]}" "$@" exec $EXEC /opt/factorio/bin/x64/factorio "${FLAGS[@]}" "$@"