mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-21 19:35:36 -05:00
Containerfile for base forge actions
Build autoconf and automake and add autoregen.py from https://sourceware.org/git/builder.git Add forge action to build container images. ChangeLog: * .forgejo/workflows/build-containers.yaml: New file. contrib/ChangeLog: * ci-containers/README: New file. * ci-containers/autoregen/Containerfile: New file. * ci-containers/autoregen/autoregen.py: New file. * ci-containers/build-image.sh: New file. Signed-off-by: Pietro Monteiro <pietro@sociotechnical.xyz>
This commit is contained in:
55
.forgejo/workflows/build-containers.yaml
Normal file
55
.forgejo/workflows/build-containers.yaml
Normal file
@@ -0,0 +1,55 @@
|
||||
on:
|
||||
# push:
|
||||
# branches:
|
||||
# - trunk
|
||||
# # run on changes to any file for ci containers or to this file
|
||||
# paths:
|
||||
# - .forgejo/workflows/build-containers.yaml
|
||||
# - 'contrib/ci-containers/**/*'
|
||||
# similar for pull requests
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- .forgejo/workflows/build-containers.yaml
|
||||
- 'contrib/ci-containers/**/*'
|
||||
|
||||
jobs:
|
||||
containers:
|
||||
runs-on: sourceware-runner
|
||||
container:
|
||||
image: fedora:latest
|
||||
env:
|
||||
# the default overlayfs doesn't work when running on docker, which uses overlayfs
|
||||
STORAGE_DRIVER: vfs
|
||||
# we can't run containers in docker, so use a chroot to build the image
|
||||
BUILDAH_ISOLATION: chroot
|
||||
steps:
|
||||
- name: install dependencies
|
||||
run: |
|
||||
dnf -y --setopt=install_weak_deps=False install buildah git nodejs
|
||||
|
||||
# Checkout sources
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: build containers
|
||||
run: |
|
||||
echo "Building containers from contrib/ci-containers"
|
||||
for DIR in ./contrib/ci-containers/*
|
||||
do
|
||||
! [ -d "$DIR" ] && continue
|
||||
CONTAINER="$(basename "$DIR")"
|
||||
if [ "$FORGEJO_EVENT_NAME" = pull_request ]; then
|
||||
# branch name in lowercase, replace non-alphanumerics with '-', and remove leading and trailling '-'
|
||||
TAG="$(echo "$FORGEJO_HEAD_REF" | sed -e 's/\(.*\)/\L\1/' -e 's/[^[:alnum:]-]/-/g' -e 's/^-\+//;s/-\+$//')"
|
||||
else
|
||||
# branch name
|
||||
TAG="$FORGEJO_REF_NAME"
|
||||
fi
|
||||
echo "Building $CONTAINER with tag $TAG"
|
||||
./contrib/ci-containers/build-image.sh -d "$DIR" -t "$TAG" -- --network=host
|
||||
echo "Built $CONTAINER:$TAG should push it somewhere"
|
||||
buildah images --json "$CONTAINER:$TAG"
|
||||
echo "Removing container image from localhost"
|
||||
buildah rmi "$CONTAINER:$TAG"
|
||||
buildah rmi --prune
|
||||
done
|
||||
47
contrib/ci-containers/README
Normal file
47
contrib/ci-containers/README
Normal file
@@ -0,0 +1,47 @@
|
||||
# CI Containers
|
||||
|
||||
Each subdirectory under `contrib/ci-containers/` holds a hermetic description of
|
||||
a container image that powers jobs on the [Sourceware
|
||||
Forge](https://forge.sourceware.org). The directory itself is used as the build
|
||||
context, so any assets referenced by the `Containerfile` must be present
|
||||
in the subdirectory.
|
||||
|
||||
Keeping the description self-contained guarantees reproducible builds.
|
||||
|
||||
## Building Images
|
||||
|
||||
Images are built with [buildah](https://buildah.io) via the helper script
|
||||
`build-image.sh`. A typical invocation looks like:
|
||||
|
||||
```bash
|
||||
./contrib/ci-containers/build-image.sh \
|
||||
-d ./contrib/ci-containers/foo \
|
||||
-t v1.0 \
|
||||
-- --layers --no-cache
|
||||
```
|
||||
|
||||
* `-d` - Path to the directory containing the `Containerfile`.
|
||||
* `-t` - Tag to apply to the resulting image.
|
||||
* The trailing `--` passes additional flags directly to `buildah` (here we
|
||||
request layered output and disable the cache).
|
||||
|
||||
The full image tag will be the basename of the directory, in this case `foo`,
|
||||
and the value passed to the `-t/--tag` argument. Our hypothetical image will be
|
||||
tagged locally as `foo:v1.0`.
|
||||
|
||||
### Verify the build
|
||||
|
||||
```bash
|
||||
buildah images --json foo:v1.0
|
||||
```
|
||||
|
||||
The command returns a JSON object with the image's ID, size, and other metadata.
|
||||
|
||||
### Test the image locally
|
||||
|
||||
```bash
|
||||
podman run --rm -it foo:v1.0 /bin/bash
|
||||
```
|
||||
|
||||
By running the image interactively you can confirm that the environment behaves
|
||||
as expected.
|
||||
83
contrib/ci-containers/autoregen/Containerfile
Normal file
83
contrib/ci-containers/autoregen/Containerfile
Normal file
@@ -0,0 +1,83 @@
|
||||
FROM debian:stable-slim
|
||||
|
||||
ARG AUTOCONF_VERSION=2.69
|
||||
ARG AUTOMAKE_VERSION=1.15.1
|
||||
|
||||
# Run time deps
|
||||
RUN set -eux; \
|
||||
apt-get update; \
|
||||
apt-get upgrade -y; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
autogen \
|
||||
ca-certificates \
|
||||
git \
|
||||
m4 \
|
||||
nodejs \
|
||||
perl \
|
||||
python3 \
|
||||
python3-git \
|
||||
python3-termcolor \
|
||||
python3-unidiff \
|
||||
wget; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Get and install the autoregen.py script
|
||||
COPY --chmod=755 autoregen.py /usr/local/bin/autoregen.py
|
||||
|
||||
# Build and install autoconf and automake
|
||||
# Automake depends on autoconf, which is built and installed first
|
||||
RUN set -eux; \
|
||||
\
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
gzip \
|
||||
m4 \
|
||||
tar \
|
||||
wget \
|
||||
; \
|
||||
rm -r /var/lib/apt/lists/*; \
|
||||
\
|
||||
builddir="$(mktemp -d)"; \
|
||||
\
|
||||
cd "${builddir}"; \
|
||||
AUTOCONF_VERSION_SHORT="$(echo $AUTOCONF_VERSION | awk -F. '{ print $1 "." $2 }')"; \
|
||||
wget -q "https://ftp.gnu.org/gnu/autoconf/autoconf-${AUTOCONF_VERSION}.tar.gz"; \
|
||||
tar xf "autoconf-${AUTOCONF_VERSION}.tar.gz"; \
|
||||
cd "autoconf-${AUTOCONF_VERSION}"; \
|
||||
./configure --program-suffix="-${AUTOCONF_VERSION}"; \
|
||||
make; \
|
||||
make install; \
|
||||
cd .. ;\
|
||||
rm -rf autoconf*; \
|
||||
cd /usr/local/bin; \
|
||||
for f in autoconf autoheader autom4te autoreconf autoscan autoupdate ifnames; do \
|
||||
ln -sv "$f-$AUTOCONF_VERSION" "$f"; \
|
||||
[ ! "$AUTOCONF_VERSION" = "$AUTOCONF_VERSION_SHORT" ] && \
|
||||
ln -sv "$f-$AUTOCONF_VERSION" "$f-$AUTOCONF_VERSION_SHORT"; \
|
||||
done; \
|
||||
\
|
||||
cd "${builddir}"; \
|
||||
AUTOMAKE_VERSION_SHORT="$(echo $AUTOMAKE_VERSION | awk -F. '{ print $1 "." $2 }')"; \
|
||||
wget -q "https://ftp.gnu.org/gnu/automake/automake-${AUTOMAKE_VERSION}.tar.gz"; \
|
||||
tar xf "automake-${AUTOMAKE_VERSION}.tar.gz"; \
|
||||
cd "automake-${AUTOMAKE_VERSION}"; \
|
||||
./configure --program-suffix="-${AUTOMAKE_VERSION}"; \
|
||||
make; \
|
||||
make install; \
|
||||
cd ..; \
|
||||
rm -rf automake*; \
|
||||
cd /usr/local/bin; \
|
||||
for f in aclocal automake; do \
|
||||
ln -sv "$f-$AUTOMAKE_VERSION" "$f"; \
|
||||
[ ! "$AUTOMAKE_VERSION" = "$AUTOMAKE_VERSION_SHORT" ] && \
|
||||
ln -sv "$f-$AUTOMAKE_VERSION" "$f-$AUTOMAKE_VERSION_SHORT"; \
|
||||
done; \
|
||||
\
|
||||
rm -rf "${builddir}"; \
|
||||
\
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false
|
||||
146
contrib/ci-containers/autoregen/autoregen.py
Executable file
146
contrib/ci-containers/autoregen/autoregen.py
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# This script helps to regenerate files managed by autotools and
|
||||
# autogen in binutils-gdb and gcc repositories.
|
||||
|
||||
# It can be used by buildbots to check that the current repository
|
||||
# contents has been updated correctly, and by developers to update
|
||||
# such files as expected.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# On Gentoo, vanilla unpatched autotools are packaged separately.
|
||||
# We place the vanilla names first as we want to prefer those if both exist.
|
||||
AUTOCONF_NAMES = ["autoconf-vanilla-2.69", "autoconf-2.69", "autoconf"]
|
||||
AUTOMAKE_NAMES = ["automake-vanilla-1.15", "automake-1.15.1", "automake"]
|
||||
ACLOCAL_NAMES = ["aclocal-vanilla-1.15", "aclocal-1.15.1", "aclocal"]
|
||||
AUTOHEADER_NAMES = ["autoheader-vanilla-2.69", "autoheader-2.69", "autoheader"]
|
||||
AUTORECONF_NAMES = ["autoreconf-vanilla-2.69", "autoreconf-2.69", "autoreconf"]
|
||||
|
||||
# Pick the first for each list that exists on this system.
|
||||
AUTOCONF_BIN = next(name for name in AUTOCONF_NAMES if shutil.which(name))
|
||||
AUTOMAKE_BIN = next(name for name in AUTOMAKE_NAMES if shutil.which(name))
|
||||
ACLOCAL_BIN = next(name for name in ACLOCAL_NAMES if shutil.which(name))
|
||||
AUTOHEADER_BIN = next(name for name in AUTOHEADER_NAMES if shutil.which(name))
|
||||
AUTORECONF_BIN = next(name for name in AUTORECONF_NAMES if shutil.which(name))
|
||||
|
||||
AUTOGEN_BIN = "autogen"
|
||||
|
||||
# autoconf-wrapper and automake-wrapper from Gentoo look at this environment variable.
|
||||
# It's harmless to set it on other systems though.
|
||||
EXTRA_ENV = {
|
||||
"WANT_AUTOCONF": AUTOCONF_BIN.split("-", 1)[1] if "-" in AUTOCONF_BIN else "",
|
||||
"WANT_AUTOMAKE": AUTOMAKE_BIN.split("-", 1)[1] if "-" in AUTOMAKE_BIN else "",
|
||||
"AUTOCONF": AUTOCONF_BIN,
|
||||
"ACLOCAL": ACLOCAL_BIN,
|
||||
"AUTOMAKE": AUTOMAKE_BIN,
|
||||
"AUTOGEN": AUTOGEN_BIN,
|
||||
}
|
||||
ENV = os.environ.copy()
|
||||
ENV.update(EXTRA_ENV)
|
||||
|
||||
|
||||
# Directories we should skip entirely because they're vendored or have different
|
||||
# autotools versions.
|
||||
SKIP_DIRS = [
|
||||
# readline and minizip are maintained with different autotools versions
|
||||
"readline",
|
||||
"minizip",
|
||||
]
|
||||
|
||||
MANUAL_CONF_DIRS = [
|
||||
".",
|
||||
# autoreconf does not update aclocal.m4
|
||||
"fixincludes",
|
||||
]
|
||||
|
||||
# Run the shell command CMD.
|
||||
#
|
||||
# Print the command on stdout prior to running it.
|
||||
def run_shell(cmd: str):
|
||||
print(f"+ {cmd}", flush=True)
|
||||
res = subprocess.run(
|
||||
f"{cmd}",
|
||||
shell=True,
|
||||
encoding="utf8",
|
||||
env=ENV,
|
||||
)
|
||||
res.check_returncode()
|
||||
|
||||
|
||||
def regenerate_with_autoreconf():
|
||||
run_shell(f"{AUTORECONF_BIN} -f")
|
||||
|
||||
def regenerate_with_autogen():
|
||||
run_shell(f"{AUTOGEN_BIN} Makefile.def")
|
||||
|
||||
def regenerate_manually():
|
||||
configure_lines = open("configure.ac").read().splitlines()
|
||||
if folder.stem == "fixincludes" or any(
|
||||
True for line in configure_lines if line.startswith("AC_CONFIG_MACRO_DIR")
|
||||
):
|
||||
include_arg = ""
|
||||
include_arg2 = ""
|
||||
if (folder / ".." / "config").is_dir():
|
||||
include_arg = "-I../config"
|
||||
|
||||
if folder.stem == "fixincludes":
|
||||
include_arg = "-I.."
|
||||
include_arg2 = "-I../config"
|
||||
|
||||
# aclocal does not support the -f short option for force
|
||||
run_shell(f"{ACLOCAL_BIN} --force {include_arg} {include_arg2}")
|
||||
|
||||
if (folder / "config.in").is_file() or any(
|
||||
True for line in configure_lines if line.startswith("AC_CONFIG_HEADERS")
|
||||
):
|
||||
run_shell(f"{AUTOHEADER_BIN} -f")
|
||||
|
||||
# apparently automake is somehow unstable -> skip it for gotools
|
||||
if any(
|
||||
True for line in configure_lines if line.startswith("AM_INIT_AUTOMAKE")
|
||||
) and not str(folder).endswith("gotools"):
|
||||
run_shell(f"{AUTOMAKE_BIN} -f")
|
||||
|
||||
run_shell(f"{AUTOCONF_BIN} -f")
|
||||
|
||||
|
||||
run_shell(f"{AUTOCONF_BIN} --version")
|
||||
run_shell(f"{AUTOMAKE_BIN} --version")
|
||||
run_shell(f"{ACLOCAL_BIN} --version")
|
||||
run_shell(f"{AUTOHEADER_BIN} --version")
|
||||
|
||||
print(f"Extra environment: {EXTRA_ENV}", flush=True)
|
||||
|
||||
config_folders: list[Path] = []
|
||||
autogen_folders: list[Path] = []
|
||||
repo_root = Path.cwd()
|
||||
|
||||
for root, _, files in os.walk("."):
|
||||
for file in files:
|
||||
if file == "configure.ac":
|
||||
config_folders.append(Path(root).resolve())
|
||||
if file == "Makefile.tpl":
|
||||
autogen_folders.append(Path(root).resolve())
|
||||
|
||||
for folder in sorted(autogen_folders):
|
||||
print(f"Entering directory {folder}", flush=True)
|
||||
os.chdir(folder)
|
||||
regenerate_with_autogen()
|
||||
|
||||
for folder in sorted(config_folders):
|
||||
if folder.stem in SKIP_DIRS:
|
||||
print(f"Skipping directory {folder}", flush=True)
|
||||
continue
|
||||
|
||||
print(f"Entering directory {folder}", flush=True)
|
||||
os.chdir(folder)
|
||||
|
||||
if str(folder.relative_to(repo_root)) in MANUAL_CONF_DIRS:
|
||||
regenerate_manually()
|
||||
else:
|
||||
regenerate_with_autoreconf()
|
||||
104
contrib/ci-containers/build-image.sh
Executable file
104
contrib/ci-containers/build-image.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build a container using buildah
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: build-image.sh -d <directory> -t <tag> [-e timestamp] [-- buildah-args...]
|
||||
|
||||
Options:
|
||||
-d, --dir <path> Directory with the Containerfile (required).
|
||||
-t, --tag <tag> Tag to apply to the built image (required).
|
||||
-e, --epoch <ts> Set the "created" timestamp for the built image to this number of seconds since the epoch (optional).
|
||||
Default is to use the timestamp of the current commit.
|
||||
Needs buildah 1.41 or newer.
|
||||
-h, --help Show this help message and exit.
|
||||
|
||||
All arguments after a double-dash (--) are forwarded unchanged to 'buildah'.
|
||||
|
||||
Example:
|
||||
./build-image.sh -d src -t v1.0 -- --layers --no-cache
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
DIR=""
|
||||
TAG=""
|
||||
EXTRA_ARGS=()
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
-d|--dir)
|
||||
if [[ -n "${2-}" ]]; then
|
||||
DIR="$2"
|
||||
shift 2
|
||||
else
|
||||
echo "error: --dir requires a value" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-t|--tag)
|
||||
if [[ -n "${2-}" ]]; then
|
||||
TAG="$2"
|
||||
shift 2
|
||||
else
|
||||
echo "error: --tag requires a value" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-e|--epoch)
|
||||
if [[ -n "${2-}" ]]; then
|
||||
SOURCE_DATE_EPOCH="$2"
|
||||
shift 2
|
||||
else
|
||||
echo "error: --source-date-epoch requires a value" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
EXTRA_ARGS+=("$@")
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown option '$1'" >&2
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$DIR" ]]; then
|
||||
echo "error: directory (-d/--dir) is required" >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ -z "$TAG" ]]; then
|
||||
echo "error: Tag (-t/--tag) is required." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! -e "${DIR}/Containerfile" ]]; then
|
||||
echo "error: '${DIR}/Containerfile' does not exist." >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
CONTAINER="$(basename "$DIR")"
|
||||
IMAGE_TAG="${CONTAINER}:${TAG}"
|
||||
|
||||
if [[ -z "${SOURCE_DATE_EPOCH-}" ]]; then
|
||||
SCRIPT_DIR="$(dirname "$0")"
|
||||
SOURCE_DATE_EPOCH="$(cd "${SCRIPT_DIR}" && git log -1 --pretty=%ct)"
|
||||
fi
|
||||
export SOURCE_DATE_EPOCH
|
||||
|
||||
|
||||
buildah build \
|
||||
-f "${DIR}/Containerfile" \
|
||||
-t "$IMAGE_TAG" \
|
||||
"${EXTRA_ARGS[@]}" \
|
||||
"$DIR"
|
||||
Reference in New Issue
Block a user