Skip to content

Commit 3af45bd

Browse files
committed
Add initial scripts for image maintainers to use to auto-generate appropriate GitHub Actions
1 parent eb76b2e commit 3af45bd

File tree

3 files changed

+255
-0
lines changed

3 files changed

+255
-0
lines changed

scripts/github-actions/example-ci.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: GitHub CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
schedule:
7+
- cron: 0 0 * * 0
8+
9+
defaults:
10+
run:
11+
shell: 'bash -Eeuo pipefail -x {0}'
12+
13+
jobs:
14+
15+
generate-jobs:
16+
name: Generate Jobs
17+
runs-on: ubuntu-latest
18+
outputs:
19+
strategy: ${{ steps.generate-jobs.outputs.strategy }}
20+
steps:
21+
- uses: actions/checkout@v1
22+
- id: generate-jobs
23+
name: Generate Jobs
24+
run: |
25+
git clone --depth 1 https://github.com/docker-library/bashbrew.git -b master ~/bashbrew
26+
strategy="$(~/bashbrew/scripts/github-actions/generate.sh)"
27+
jq . <<<"$strategy" # sanity check / debugging aid
28+
echo "::set-output name=strategy::$strategy"
29+
30+
test:
31+
needs: generate-jobs
32+
strategy: ${{ fromJson(needs.generate-jobs.outputs.strategy) }}
33+
name: ${{ matrix.name }}
34+
runs-on: ${{ matrix.os }}
35+
steps:
36+
- uses: actions/checkout@v1
37+
- name: Prepare Environment
38+
run: ${{ matrix.runs.prepare }}
39+
- name: Pull Dependencies
40+
run: ${{ matrix.runs.pull }}
41+
- name: Build ${{ matrix.name }}
42+
run: ${{ matrix.runs.build }}
43+
- name: History ${{ matrix.name }}
44+
run: ${{ matrix.runs.history }}
45+
- name: Test ${{ matrix.name }}
46+
run: ${{ matrix.runs.test }}
47+
- name: '"docker images"'
48+
run: ${{ matrix.runs.images }}
49+

scripts/github-actions/generate.sh

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/usr/bin/env bash
2+
set -Eeuo pipefail
3+
4+
image="${GITHUB_REPOSITORY##*/}" # "python", "golang", etc
5+
6+
[ -n "${GENERATE_STACKBREW_LIBRARY:-}" ] || [ -x ./generate-stackbrew-library.sh ] # sanity check
7+
8+
tmp="$(mktemp -d)"
9+
trap "$(printf 'rm -rf %q' "$tmp")" EXIT
10+
11+
if ! command -v bashbrew &> /dev/null; then
12+
dir="$(readlink -f "$BASH_SOURCE")"
13+
dir="$(dirname "$dir")"
14+
dir="$(cd "$dir/../.." && pwd -P)"
15+
echo >&2 'Building bashbrew ...'
16+
"$dir/bashbrew.sh" --version > /dev/null
17+
export PATH="$dir/bin:$PATH"
18+
bashbrew --version >&2
19+
fi
20+
21+
mkdir "$tmp/library"
22+
export BASHBREW_LIBRARY="$tmp/library"
23+
24+
eval "${GENERATE_STACKBREW_LIBRARY:-./generate-stackbrew-library.sh}" > "$BASHBREW_LIBRARY/$image"
25+
26+
tags="$(bashbrew list --build-order --uniq "$image")"
27+
28+
# 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
29+
30+
order=()
31+
declare -A metas=()
32+
for tag in $tags; do
33+
echo >&2 "Processing $tag ..."
34+
meta="$(
35+
bashbrew cat --format '
36+
{{- $e := .TagEntry -}}
37+
{{- "{" -}}
38+
"name": {{- json ($e.Tags | first) -}},
39+
"tags": {{- json ($.Tags "" false $e) -}},
40+
"directory": {{- json $e.Directory -}},
41+
"file": {{- json $e.File -}},
42+
"constraints": {{- json $e.Constraints -}},
43+
"froms": {{- json ($.DockerFroms $e) -}}
44+
{{- "}" -}}
45+
' "$tag" | jq -c '
46+
{
47+
name: .name,
48+
os: (
49+
if (.constraints | contains(["windowsservercore-1809"])) or (.constraints | contains(["nanoserver-1809"])) then
50+
"windows-2019"
51+
elif .constraints | contains(["windowsservercore-ltsc2016"]) then
52+
"windows-2016"
53+
elif .constraints == [] or .constraints == ["!aufs"] then
54+
"ubuntu-latest"
55+
else
56+
# use an intentionally invalid value so that GitHub chokes and we notice something is wrong
57+
"invalid-or-unknown"
58+
end
59+
),
60+
meta: { entries: [ . ] },
61+
runs: {
62+
build: (
63+
[
64+
"docker build"
65+
]
66+
+ (
67+
.tags
68+
| map(
69+
"--tag " + (. | @sh)
70+
)
71+
)
72+
+ if .file != "Dockerfile" then
73+
[ "--file", (.file | @sh) ]
74+
else
75+
[]
76+
end
77+
+ [
78+
(.directory | @sh)
79+
]
80+
| join(" ")
81+
),
82+
history: ("docker history " + (.tags[0] | @sh)),
83+
test: ("~/oi/test/run.sh " + (.tags[0] | @sh)),
84+
},
85+
}
86+
'
87+
)"
88+
89+
parent="$(bashbrew parents "$tag" | tail -1)" # if there ever exists an image with TWO parents in the same repo, this will break :)
90+
if [ -n "$parent" ]; then
91+
parent="$(bashbrew list --uniq "$parent")" # normalize
92+
parentMeta="${metas["$parent"]}"
93+
parentMeta="$(jq -c --argjson meta "$meta" '
94+
. + {
95+
name: (.name + ", " + $meta.name),
96+
os: (if .os == $meta.os then .os else "invalid-os-chain--" + .os + "+" + $meta.os end),
97+
meta: { entries: (.meta.entries + $meta.meta.entries) },
98+
runs: (
99+
.runs
100+
| to_entries
101+
| map(
102+
.value += "\n" + $meta.runs[.key]
103+
)
104+
| from_entries
105+
),
106+
}
107+
' <<<"$parentMeta")"
108+
metas["$parent"]="$parentMeta"
109+
else
110+
metas["$tag"]="$meta"
111+
order+=( "$tag" )
112+
fi
113+
done
114+
115+
strategy="$(
116+
for tag in "${order[@]}"; do
117+
jq -c '
118+
.meta += {
119+
froms: (
120+
[ .meta.entries[].froms[] ]
121+
- [ .meta.entries[].tags[] ]
122+
),
123+
dockerfiles: [
124+
.meta.entries[]
125+
| .directory + "/" + .file
126+
],
127+
}
128+
| .runs += {
129+
prepare: ([
130+
(
131+
if .os | startswith("windows-") then
132+
"# enable symlinks on Windows (https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks)",
133+
"git config --global core.symlinks true",
134+
"# ... make sure they are *real* symlinks (https://github.com/git-for-windows/git/pull/156)",
135+
"export MSYS=winsymlinks:nativestrict",
136+
"# make sure line endings get checked out as-is",
137+
"git config --global core.autocrlf false"
138+
else
139+
empty
140+
end
141+
),
142+
"git clone --depth 1 https://github.com/docker-library/official-images.git -b master ~/oi",
143+
"# create a dummy empty image/layer so we can --filter since= later to get a meanginful image list",
144+
"{ echo FROM " + (
145+
if (.os | startswith("windows-")) then
146+
"mcr.microsoft.com/windows/servercore:ltsc" + (.os | ltrimstr("windows-"))
147+
else
148+
"busybox:latest"
149+
end
150+
) + "; echo RUN :; } | docker build --no-cache --tag image-list-marker -",
151+
(
152+
if .os | startswith("windows-") | not then
153+
(
154+
"# PGP Happy Eyeballs",
155+
"git clone --depth 1 https://github.com/tianon/pgp-happy-eyeballs.git ~/phe",
156+
"~/phe/hack-my-builds.sh",
157+
"rm -rf ~/phe"
158+
)
159+
else
160+
empty
161+
end
162+
)
163+
] | join("\n")),
164+
pull: ([ .meta.froms[] | "docker pull " + @sh ] | join("\n")),
165+
# build
166+
# history
167+
# test
168+
images: "docker image ls --filter since=image-list-marker",
169+
}
170+
' <<<"${metas["$tag"]}"
171+
done | jq -cs '
172+
{
173+
"fail-fast": false,
174+
matrix: { include: . },
175+
}
176+
'
177+
)"
178+
179+
if [ -t 1 ]; then
180+
jq <<<"$strategy"
181+
else
182+
cat <<<"$strategy"
183+
fi

scripts/github-actions/munge-i386.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
set -Eeuo pipefail
3+
4+
jq --arg dpkgSmokeTest '[ "$(dpkg --print-architecture)" = "amd64" ]' '
5+
.matrix.include += [
6+
.matrix.include[]
7+
| select(.name | test(" (.+)") | not) # ignore any existing munged builds
8+
| select(.os | startswith("windows-") | not)
9+
| .name += " (i386)"
10+
| .runs.pull = ([
11+
"# pull i386 variants of base images for multi-architecture testing",
12+
$dpkgSmokeTest,
13+
(
14+
.meta.froms[]
15+
| ("i386/" + . | @sh) as $i386
16+
| (
17+
"docker pull " + $i386,
18+
"docker tag " + $i386 + " " + @sh
19+
)
20+
)
21+
] | join("\n"))
22+
]
23+
' "$@"

0 commit comments

Comments
 (0)