From 3af45bd40255b80134fc1806b6eac58b45491f44 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 24 Apr 2020 15:48:17 -0700 Subject: [PATCH 1/5] Add initial scripts for image maintainers to use to auto-generate appropriate GitHub Actions --- scripts/github-actions/example-ci.yml | 49 +++++++ scripts/github-actions/generate.sh | 183 ++++++++++++++++++++++++++ scripts/github-actions/munge-i386.sh | 23 ++++ 3 files changed, 255 insertions(+) create mode 100644 scripts/github-actions/example-ci.yml create mode 100755 scripts/github-actions/generate.sh create mode 100755 scripts/github-actions/munge-i386.sh diff --git a/scripts/github-actions/example-ci.yml b/scripts/github-actions/example-ci.yml new file mode 100644 index 00000000..d9218b21 --- /dev/null +++ b/scripts/github-actions/example-ci.yml @@ -0,0 +1,49 @@ +name: GitHub CI + +on: + pull_request: + push: + schedule: + - cron: 0 0 * * 0 + +defaults: + run: + shell: 'bash -Eeuo pipefail -x {0}' + +jobs: + + generate-jobs: + name: Generate Jobs + runs-on: ubuntu-latest + outputs: + strategy: ${{ steps.generate-jobs.outputs.strategy }} + steps: + - uses: actions/checkout@v1 + - id: generate-jobs + name: Generate Jobs + run: | + git clone --depth 1 https://github.com/docker-library/bashbrew.git -b master ~/bashbrew + strategy="$(~/bashbrew/scripts/github-actions/generate.sh)" + jq . <<<"$strategy" # sanity check / debugging aid + echo "::set-output name=strategy::$strategy" + + test: + needs: generate-jobs + strategy: ${{ fromJson(needs.generate-jobs.outputs.strategy) }} + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - name: Prepare Environment + run: ${{ matrix.runs.prepare }} + - name: Pull Dependencies + run: ${{ matrix.runs.pull }} + - name: Build ${{ matrix.name }} + run: ${{ matrix.runs.build }} + - name: History ${{ matrix.name }} + run: ${{ matrix.runs.history }} + - name: Test ${{ matrix.name }} + run: ${{ matrix.runs.test }} + - name: '"docker images"' + run: ${{ matrix.runs.images }} + diff --git a/scripts/github-actions/generate.sh b/scripts/github-actions/generate.sh new file mode 100755 index 00000000..24414387 --- /dev/null +++ b/scripts/github-actions/generate.sh @@ -0,0 +1,183 @@ +#!/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)" + echo >&2 'Building bashbrew ...' + "$dir/bashbrew.sh" --version > /dev/null + export PATH="$dir/bin:$PATH" + bashbrew --version >&2 +fi + +mkdir "$tmp/library" +export BASHBREW_LIBRARY="$tmp/library" + +eval "${GENERATE_STACKBREW_LIBRARY:-./generate-stackbrew-library.sh}" > "$BASHBREW_LIBRARY/$image" + +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 ..." + meta="$( + bashbrew cat --format ' + {{- $e := .TagEntry -}} + {{- "{" -}} + "name": {{- json ($e.Tags | first) -}}, + "tags": {{- json ($.Tags "" false $e) -}}, + "directory": {{- json $e.Directory -}}, + "file": {{- json $e.File -}}, + "constraints": {{- json $e.Constraints -}}, + "froms": {{- json ($.DockerFroms $e) -}} + {{- "}" -}} + ' "$tag" | jq -c ' + { + name: .name, + os: ( + if (.constraints | contains(["windowsservercore-1809"])) or (.constraints | contains(["nanoserver-1809"])) then + "windows-2019" + elif .constraints | contains(["windowsservercore-ltsc2016"]) then + "windows-2016" + 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: ( + [ + "docker build" + ] + + ( + .tags + | map( + "--tag " + (. | @sh) + ) + ) + + if .file != "Dockerfile" then + [ "--file", (.file | @sh) ] + else + [] + end + + [ + (.directory | @sh) + ] + | join(" ") + ), + history: ("docker history " + (.tags[0] | @sh)), + test: ("~/oi/test/run.sh " + (.tags[0] | @sh)), + }, + } + ' + )" + + parent="$(bashbrew parents "$tag" | tail -1)" # if there ever exists an image with TWO parents in the same repo, this will break :) + if [ -n "$parent" ]; then + parent="$(bashbrew list --uniq "$parent")" # 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 + jq -c ' + .meta += { + froms: ( + [ .meta.entries[].froms[] ] + - [ .meta.entries[].tags[] ] + ), + 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", + "# create a dummy empty image/layer so we can --filter since= later to get a meanginful 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 -", + ( + if .os | startswith("windows-") | not then + ( + "# PGP Happy Eyeballs", + "git clone --depth 1 https://github.com/tianon/pgp-happy-eyeballs.git ~/phe", + "~/phe/hack-my-builds.sh", + "rm -rf ~/phe" + ) + else + empty + end + ) + ] | join("\n")), + pull: ([ .meta.froms[] | "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 diff --git a/scripts/github-actions/munge-i386.sh b/scripts/github-actions/munge-i386.sh new file mode 100755 index 00000000..2adf8798 --- /dev/null +++ b/scripts/github-actions/munge-i386.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +jq --arg dpkgSmokeTest '[ "$(dpkg --print-architecture)" = "amd64" ]' ' + .matrix.include += [ + .matrix.include[] + | select(.name | test(" (.+)") | not) # ignore any existing munged builds + | select(.os | startswith("windows-") | not) + | .name += " (i386)" + | .runs.pull = ([ + "# pull i386 variants of base images for multi-architecture testing", + $dpkgSmokeTest, + ( + .meta.froms[] + | ("i386/" + . | @sh) as $i386 + | ( + "docker pull " + $i386, + "docker tag " + $i386 + " " + @sh + ) + ) + ] | join("\n")) + ] +' "$@" From dd71778b05aff1165e69b3e8a07b6ac0a9667654 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 27 Apr 2020 15:41:37 -0700 Subject: [PATCH 2/5] Account for BASHBREW_NAMESPACE to support non-library use cases too --- scripts/github-actions/generate.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/github-actions/generate.sh b/scripts/github-actions/generate.sh index 24414387..11ee3409 100755 --- a/scripts/github-actions/generate.sh +++ b/scripts/github-actions/generate.sh @@ -31,18 +31,19 @@ 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 -}} {{- "{" -}} "name": {{- json ($e.Tags | first) -}}, - "tags": {{- json ($.Tags "" false $e) -}}, + "tags": {{- json ($.Tags namespace false $e) -}}, "directory": {{- json $e.Directory -}}, "file": {{- json $e.File -}}, "constraints": {{- json $e.Constraints -}}, "froms": {{- json ($.DockerFroms $e) -}} {{- "}" -}} - ' "$tag" | jq -c ' + ' "$bashbrewImage" | jq -c ' { name: .name, os: ( @@ -86,9 +87,10 @@ for tag in $tags; do ' )" - parent="$(bashbrew parents "$tag" | tail -1)" # if there ever exists an image with TWO parents in the same repo, this will break :) + parent="$(bashbrew parents "$bashbrewImage" | tail -1)" # if there ever exists an image with TWO parents in the same repo, this will break :) if [ -n "$parent" ]; then - parent="$(bashbrew list --uniq "$parent")" # normalize + parentBashbrewImage="${parent##*/}" # account for BASHBREW_NAMESPACE being set + parent="$(bashbrew list --uniq "$parentBashbrewImage")" # normalize parentMeta="${metas["$parent"]}" parentMeta="$(jq -c --argjson meta "$meta" ' . + { From 8bc7e993ea64d59807dc55eb9f0fe9c8fab3efc0 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 27 Apr 2020 15:53:38 -0700 Subject: [PATCH 3/5] Remove extraneous whitespace --- scripts/github-actions/example-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/github-actions/example-ci.yml b/scripts/github-actions/example-ci.yml index d9218b21..155ef316 100644 --- a/scripts/github-actions/example-ci.yml +++ b/scripts/github-actions/example-ci.yml @@ -46,4 +46,3 @@ jobs: run: ${{ matrix.runs.test }} - name: '"docker images"' run: ${{ matrix.runs.images }} - From 53b96a62f11247b84375880f904f1d454021f55b Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 27 Apr 2020 17:12:44 -0700 Subject: [PATCH 4/5] Adjust Bashbrew building logic to be less noisy if called multiple times (quietly adjust PATH if bashbrew is already built) --- scripts/github-actions/generate.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/github-actions/generate.sh b/scripts/github-actions/generate.sh index 11ee3409..b6c10dcd 100755 --- a/scripts/github-actions/generate.sh +++ b/scripts/github-actions/generate.sh @@ -12,10 +12,13 @@ if ! command -v bashbrew &> /dev/null; then dir="$(readlink -f "$BASH_SOURCE")" dir="$(dirname "$dir")" dir="$(cd "$dir/../.." && pwd -P)" - echo >&2 'Building bashbrew ...' - "$dir/bashbrew.sh" --version > /dev/null + 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 >&2 + bashbrew --version > /dev/null fi mkdir "$tmp/library" From b22f02ed1bd48bffe86131ca2e2b7e783dade598 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 27 Apr 2020 17:16:13 -0700 Subject: [PATCH 5/5] Skip "docker pull" on scratch --- scripts/github-actions/generate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/github-actions/generate.sh b/scripts/github-actions/generate.sh index b6c10dcd..9ee824b9 100755 --- a/scripts/github-actions/generate.sh +++ b/scripts/github-actions/generate.sh @@ -166,7 +166,7 @@ strategy="$( end ) ] | join("\n")), - pull: ([ .meta.froms[] | "docker pull " + @sh ] | join("\n")), + pull: ([ .meta.froms[] | select(. != "scratch") | "docker pull " + @sh ] | join("\n")), # build # history # test