245 lines
8.5 KiB
Bash
Executable File
245 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
image="${GITHUB_REPOSITORY##*/}" # "python", "golang", etc
|
|
|
|
[ -n "${GENERATE_STACKBREW_LIBRARY:-}" ] || [ -x ./generate-stackbrew-library.sh ] # sanity check
|
|
|
|
tmp="$(mktemp -d)"
|
|
trap "$(printf 'rm -rf %q' "$tmp")" EXIT
|
|
|
|
if ! command -v bashbrew &> /dev/null; then
|
|
dir="$(readlink -f "$BASH_SOURCE")"
|
|
dir="$(dirname "$dir")"
|
|
dir="$(cd "$dir/../.." && pwd -P)"
|
|
if [ ! -x "$dir/bin/bashbrew" ]; then
|
|
echo >&2 'Building bashbrew ...'
|
|
"$dir/bashbrew.sh" --version > /dev/null
|
|
"$dir/bin/bashbrew" --version >&2
|
|
fi
|
|
export PATH="$dir/bin:$PATH"
|
|
bashbrew --version > /dev/null
|
|
fi
|
|
|
|
mkdir "$tmp/library" # not exporting this as BASHBREW_LIBRARY yet so that "generate-stackbrew-library.sh" gets the externally-set value of BASHBREW_LIBRARY (or unset value) so it can use that to change behavior (see https://github.com/docker-library/buildpack-deps/commit/cc2dc88e04e82cb4c4c2091205d888a5d5b386f3 for an example)
|
|
|
|
eval "${GENERATE_STACKBREW_LIBRARY:-./generate-stackbrew-library.sh}" > "$tmp/library/$image"
|
|
|
|
export BASHBREW_LIBRARY="$tmp/library"
|
|
|
|
# if we don't appear to be able to fetch the listed commits, they might live in a PR branch, so we should force them into the Bashbrew cache directly to allow it to do what it needs
|
|
if ! bashbrew fetch "$image" &> /dev/null; then
|
|
gitCache="$(bashbrew cat --format '{{ gitCache }}' "$image")"
|
|
git -C "$gitCache" fetch --quiet --update-shallow "$PWD" HEAD > /dev/null
|
|
bashbrew fetch "$image" > /dev/null
|
|
fi
|
|
|
|
tags="$(bashbrew list --build-order --uniq "$image")"
|
|
|
|
# see https://github.com/docker-library/python/commit/6b513483afccbfe23520b1f788978913e025120a for the ideal of what this would be (minimal YAML in all 30+ repos, shared shell script that outputs fully dynamic steps list), if GitHub Actions were to support a fully dynamic steps list
|
|
|
|
order=()
|
|
declare -A metas=()
|
|
for tag in $tags; do
|
|
echo >&2 "Processing $tag ..."
|
|
bashbrewImage="${tag##*/}" # account for BASHBREW_NAMESPACE being set
|
|
meta="$(
|
|
bashbrew cat --format '
|
|
{{- $e := .TagEntry -}}
|
|
{{- $arch := $e.HasArchitecture arch | ternary arch ($e.Architectures | first) -}}
|
|
{{- "{" -}}
|
|
"name": {{- json ($e.Tags | first) -}},
|
|
"tags": {{- json ($.Tags namespace false $e) -}},
|
|
"directory": {{- json ($e.ArchDirectory $arch) -}},
|
|
"file": {{- json ($e.ArchFile $arch) -}},
|
|
"builder": {{- json ($e.ArchBuilder $arch) -}},
|
|
"constraints": {{- json $e.Constraints -}},
|
|
"froms": {{- json ($.ArchDockerFroms $arch $e) -}},
|
|
"platform": {{- json (ociPlatform $arch).String -}}
|
|
{{- "}" -}}
|
|
' "$bashbrewImage" | jq -c '
|
|
{
|
|
name: .name,
|
|
os: (
|
|
if (.constraints | contains(["windowsservercore-ltsc2022"])) or (.constraints | contains(["nanoserver-ltsc2022"])) then
|
|
"windows-2022"
|
|
elif (.constraints | contains(["windowsservercore-1809"])) or (.constraints | contains(["nanoserver-1809"])) then
|
|
"windows-2019"
|
|
elif .constraints == [] or .constraints == ["!aufs"] then
|
|
"ubuntu-latest"
|
|
else
|
|
# use an intentionally invalid value so that GitHub chokes and we notice something is wrong
|
|
"invalid-or-unknown"
|
|
end
|
|
),
|
|
meta: { entries: [ . ] },
|
|
runs: {
|
|
build: (
|
|
[
|
|
# https://github.com/docker-library/bashbrew/pull/43
|
|
if .builder == "classic" or .builder == "" then
|
|
"DOCKER_BUILDKIT=0 docker build"
|
|
elif .builder == "buildkit" then
|
|
"docker buildx build --progress plain --build-arg BUILDKIT_SYNTAX=\"$BASHBREW_BUILDKIT_SYNTAX\""
|
|
# TODO elif .builder == "oci-import" then ????
|
|
else
|
|
"echo >&2 " + ("error: unknown/unsupported builder: " + .builder | @sh) + "\nexit 1\n#"
|
|
end
|
|
]
|
|
+ [
|
|
# TODO error out on unsupported platforms, or just let the emulation go wild?
|
|
"--platform", .platform
|
|
]
|
|
+ (
|
|
.tags
|
|
| map(
|
|
"--tag " + (. | @sh)
|
|
)
|
|
)
|
|
+ if .file != "Dockerfile" then
|
|
[ "--file", ((.directory + "/" + .file) | @sh) ]
|
|
else
|
|
[]
|
|
end
|
|
+ [
|
|
(.directory | @sh)
|
|
]
|
|
| join(" ")
|
|
),
|
|
history: ("docker history " + (.tags[0] | @sh)),
|
|
test: (
|
|
[
|
|
"set -- " + (.tags[0] | @sh),
|
|
# https://github.com/docker-library/bashbrew/issues/46#issuecomment-1152567694 (allow local test config / tests)
|
|
"if [ -s ./.test/config.sh ]; then set -- --config ~/oi/test/config.sh --config ./.test/config.sh \"$@\"; fi",
|
|
"~/oi/test/run.sh \"$@\""
|
|
] | join("\n")
|
|
),
|
|
},
|
|
}
|
|
'
|
|
)"
|
|
|
|
if parent="$(bashbrew parents --depth=1 "$bashbrewImage" | grep "^${tag%%:*}:")" && [ -n "$parent" ]; then
|
|
if [ "$(wc -l <<<"$parent")" -ne 1 ]; then
|
|
echo >&2 "error: '$tag' has multiple parents in the same repository and this script can't handle that yet!"
|
|
echo >&2 "$parent"
|
|
exit 1
|
|
fi
|
|
parent="$(bashbrew parents "$bashbrewImage" | grep "^${tag%%:*}:" | tail -1)" # get the "ultimate" this-repo parent
|
|
parentBashbrewImage="${parent##*/}" # account for BASHBREW_NAMESPACE being set
|
|
parent="$(bashbrew list --uniq "$parentBashbrewImage")" # normalize
|
|
parentMeta="${metas["$parent"]}"
|
|
parentMeta="$(jq -c --argjson meta "$meta" '
|
|
. + {
|
|
name: (.name + ", " + $meta.name),
|
|
os: (if .os == $meta.os then .os else "invalid-os-chain--" + .os + "+" + $meta.os end),
|
|
meta: { entries: (.meta.entries + $meta.meta.entries) },
|
|
runs: (
|
|
.runs
|
|
| to_entries
|
|
| map(
|
|
.value += "\n" + $meta.runs[.key]
|
|
)
|
|
| from_entries
|
|
),
|
|
}
|
|
' <<<"$parentMeta")"
|
|
metas["$parent"]="$parentMeta"
|
|
else
|
|
metas["$tag"]="$meta"
|
|
order+=( "$tag" )
|
|
fi
|
|
done
|
|
|
|
strategy="$(
|
|
for tag in "${order[@]}"; do
|
|
# envObjectToGitHubEnvFileJQ converts from the output of ~/oi/.bin/bashbrew-buildkit-env-setup.sh into what GHA expects in $GITHUB_ENV
|
|
# (in a separate env to make embedding/quoting easier inside this sub-jq that generates JSON that embeds shell scripts)
|
|
envObjectToGitHubEnvFileJQ='
|
|
to_entries | map(
|
|
(.key | if test("[^a-zA-Z0-9_]+") then
|
|
error("invalid env key: \(.)")
|
|
else . end)
|
|
+ "="
|
|
+ (.value | if test("[\r\n]+") then
|
|
error("invalid env value: \(.)")
|
|
else . end)
|
|
) | join("\n")
|
|
' \
|
|
jq -c '
|
|
.meta += {
|
|
froms: (
|
|
[ .meta.entries[].froms[] ]
|
|
- [ .meta.entries[].tags[] ]
|
|
| unique
|
|
),
|
|
dockerfiles: [
|
|
.meta.entries[]
|
|
| .directory + "/" + .file
|
|
],
|
|
}
|
|
| .runs += {
|
|
prepare: ([
|
|
(
|
|
if .os | startswith("windows-") then
|
|
"# enable symlinks on Windows (https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks)",
|
|
"git config --global core.symlinks true",
|
|
"# ... make sure they are *real* symlinks (https://github.com/git-for-windows/git/pull/156)",
|
|
"export MSYS=winsymlinks:nativestrict",
|
|
"# make sure line endings get checked out as-is",
|
|
"git config --global core.autocrlf false"
|
|
else
|
|
empty
|
|
end
|
|
),
|
|
"git clone --depth 1 https://github.com/docker-library/official-images.git -b master ~/oi",
|
|
|
|
(
|
|
"# https://github.com/docker-library/bashbrew/pull/43",
|
|
if ([ .meta.entries[].builder ] | index("buildkit")) then
|
|
# https://github.com/docker-library/bashbrew/pull/70#issuecomment-1461033890 (we need to _not_ set BASHBREW_ARCH here)
|
|
"if [ -x ~/oi/.bin/bashbrew-buildkit-env-setup.sh ]; then",
|
|
"\t# https://github.com/docker-library/official-images/pull/14212",
|
|
"\tbuildkitEnvs=\"$(~/oi/.bin/bashbrew-buildkit-env-setup.sh)\"",
|
|
"\tjq <<<\"$buildkitEnvs\" -r \(env.envObjectToGitHubEnvFileJQ | @sh) | tee -a \"$GITHUB_ENV\"",
|
|
"else",
|
|
"\tBASHBREW_BUILDKIT_SYNTAX=\"$(< ~/oi/.bashbrew-buildkit-syntax)\"; export BASHBREW_BUILDKIT_SYNTAX",
|
|
"\tprintf \"BASHBREW_BUILDKIT_SYNTAX=%q\\n\" \"$BASHBREW_BUILDKIT_SYNTAX\" >> \"$GITHUB_ENV\"",
|
|
"fi",
|
|
empty
|
|
else
|
|
empty
|
|
end
|
|
),
|
|
|
|
"# create a dummy empty image/layer so we can --filter since= later to get a meaningful image list",
|
|
"{ echo FROM " + (
|
|
if .os | startswith("windows-") then
|
|
"mcr.microsoft.com/windows/servercore:ltsc" + (.os | ltrimstr("windows-"))
|
|
else
|
|
"busybox:latest"
|
|
end
|
|
) + "; echo RUN :; } | docker build --no-cache --tag image-list-marker -"
|
|
] | join("\n")),
|
|
pull: ([ .meta.froms[] | select(. != "scratch") | "docker pull " + @sh ] | join("\n")),
|
|
# build
|
|
# history
|
|
# test
|
|
images: "docker image ls --filter since=image-list-marker",
|
|
}
|
|
' <<<"${metas["$tag"]}"
|
|
done | jq -cs '
|
|
{
|
|
"fail-fast": false,
|
|
matrix: { include: . },
|
|
}
|
|
'
|
|
)"
|
|
|
|
if [ -t 1 ]; then
|
|
jq <<<"$strategy"
|
|
else
|
|
cat <<<"$strategy"
|
|
fi
|