diff --git a/.test/builds.json b/.test/builds.json index 3efe1ec..3159621 100644 --- a/.test/builds.json +++ b/.test/builds.json @@ -2716,5 +2716,61 @@ } } } + }, + "93476ae64659d71f4ee7fac781d6d1890df8926682e2fa6bd647a246b33ad9bf": { + "buildId": "93476ae64659d71f4ee7fac781d6d1890df8926682e2fa6bd647a246b33ad9bf", + "build": { + "img": "oisupport/staging-amd64:93476ae64659d71f4ee7fac781d6d1890df8926682e2fa6bd647a246b33ad9bf", + "resolved": null, + "sourceId": "a7e4b56dd1e5dde4c9d988092cb8639987559f13c5c92210664a4ffb880f222b", + "arch": "amd64", + "parents": {}, + "resolvedParents": {} + }, + "source": { + "sourceId": "a7e4b56dd1e5dde4c9d988092cb8639987559f13c5c92210664a4ffb880f222b", + "reproducibleGitChecksum": "0ea17ff707666270e1fcb3edd76545906c68714d658ccde88c22d1606b2d7e4f", + "allTags": [ + "ubuntu:22.04", + "ubuntu:jammy-20240111", + "ubuntu:jammy", + "ubuntu:latest" + ], + "entry": { + "GitRepo": "https://git.launchpad.net/cloud-images/+oci/ubuntu-base", + "GitFetch": "refs/tags/dist-jammy-amd64-20240111-e6e3490a", + "GitCommit": "e6e3490ad3f524ccaa072edafe525f8ca8ac5490", + "Directory": "oci", + "File": "index.json", + "Builder": "oci-import", + "SOURCE_DATE_EPOCH": 1704931200 + }, + "arches": { + "amd64": { + "tags": [ + "ubuntu:22.04", + "ubuntu:jammy-20240111", + "ubuntu:jammy", + "ubuntu:latest" + ], + "archTags": [], + "froms": [ + "scratch" + ], + "lastStageFrom": "scratch", + "platformString": "linux/amd64", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "parents": { + "scratch": { + "sourceId": null, + "pin": null + } + } + } + } + } } } diff --git a/.test/example-commands.sh b/.test/example-commands.sh index ce9de70..8494a4b 100644 --- a/.test/example-commands.sh +++ b/.test/example-commands.sh @@ -77,3 +77,54 @@ SOURCE_DATE_EPOCH=1700741054 \ # docker push 'oisupport/staging-windows-amd64:9b405cfa5b88ba65121aabdb95ae90fd2e1fee7582174de82ae861613ae3072e' # + +# ubuntu:22.04 [amd64] +# + +# +# +export BASHBREW_CACHE="${BASHBREW_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/bashbrew}" +gitCache="$BASHBREW_CACHE/git" +git init --bare "$gitCache" +_git() { git -C "$gitCache" "$@"; } +_git config gc.auto 0 +_commit() { _git rev-parse 'e6e3490ad3f524ccaa072edafe525f8ca8ac5490^{commit}'; } +if ! _commit &> /dev/null; then _git fetch 'https://git.launchpad.net/cloud-images/+oci/ubuntu-base' 'e6e3490ad3f524ccaa072edafe525f8ca8ac5490:' || _git fetch 'refs/tags/dist-jammy-amd64-20240111-e6e3490a:'; fi +_commit +mkdir temp +_git archive --format=tar 'e6e3490ad3f524ccaa072edafe525f8ca8ac5490:oci/' | tar -xvC temp +jq -s ' + if length != 1 then + error("unexpected '\''oci-layout'\'' document count: " + length) + else .[0] end + | if .imageLayoutVersion != "1.0.0" then + error("unsupported imageLayoutVersion: " + .imageLayoutVersion) + else . end +' temp/oci-layout > /dev/null +jq -s ' + if length != 1 then + error("unexpected '\''index.json'\'' document count: " + length) + else .[0] end + | if .schemaVersion != 2 then + error("unsupported schemaVersion: " + .schemaVersion) + else . end + | if .manifests | length != 1 then + error("expected only one manifests entry, not " + (.manifests | length)) + else . end + | .manifests[0] |= ( + if .mediaType != "application/vnd.oci.image.manifest.v1+json" then + error("unsupported descriptor mediaType: " + .mediaType) + else . end + | if .size < 0 then + error("invalid descriptor size: " + .size) + else . end + | del(.annotations, .urls) + | .annotations = {"org.opencontainers.image.source":"https://git.launchpad.net/cloud-images/+oci/ubuntu-base","org.opencontainers.image.revision":"e6e3490ad3f524ccaa072edafe525f8ca8ac5490","org.opencontainers.image.created":"2024-01-11T00:00:00Z","org.opencontainers.image.version":"22.04","org.opencontainers.image.url":"https://hub.docker.com/_/ubuntu","org.opencontainers.image.base.name":"scratch"} + ) +' temp/index.json > temp/index.json.new +mv temp/index.json.new temp/index.json +# +# +crane push --index temp 'oisupport/staging-amd64:93476ae64659d71f4ee7fac781d6d1890df8926682e2fa6bd647a246b33ad9bf' +rm -rf temp +# diff --git a/.test/library/ubuntu b/.test/library/ubuntu new file mode 100644 index 0000000..abf596f --- /dev/null +++ b/.test/library/ubuntu @@ -0,0 +1,15 @@ +# https://github.com/docker-library/official-images/blob/fe9c059402181390eac083cbdd7229b5d123236e/library/ubuntu but intentionally slimmed down (just "latest" on one architecture, no email addresses) + +Maintainers: Tomáš Virtus (@woky), Cristóvão Cordeiro (@cjdcordeiro) +GitRepo: https://git.launchpad.net/cloud-images/+oci/ubuntu-base +GitCommit: fa42be9027eccb928a1f0f43d95ffd9a45d36737 +Builder: oci-import +File: index.json + +# 20240111 (jammy) +Tags: 22.04, jammy-20240111, jammy, latest +Architectures: amd64 +Directory: oci +# https://git.launchpad.net/cloud-images/+oci/ubuntu-base/tree/?h=dist-jammy-amd64-20240111-e6e3490a +amd64-GitFetch: refs/tags/dist-jammy-amd64-20240111-e6e3490a +amd64-GitCommit: e6e3490ad3f524ccaa072edafe525f8ca8ac5490 diff --git a/.test/sources.json b/.test/sources.json index d211087..0b846d4 100644 --- a/.test/sources.json +++ b/.test/sources.json @@ -757,5 +757,50 @@ } } } + }, + "a7e4b56dd1e5dde4c9d988092cb8639987559f13c5c92210664a4ffb880f222b": { + "sourceId": "a7e4b56dd1e5dde4c9d988092cb8639987559f13c5c92210664a4ffb880f222b", + "reproducibleGitChecksum": "0ea17ff707666270e1fcb3edd76545906c68714d658ccde88c22d1606b2d7e4f", + "allTags": [ + "ubuntu:22.04", + "ubuntu:jammy-20240111", + "ubuntu:jammy", + "ubuntu:latest" + ], + "entry": { + "GitRepo": "https://git.launchpad.net/cloud-images/+oci/ubuntu-base", + "GitFetch": "refs/tags/dist-jammy-amd64-20240111-e6e3490a", + "GitCommit": "e6e3490ad3f524ccaa072edafe525f8ca8ac5490", + "Directory": "oci", + "File": "index.json", + "Builder": "oci-import", + "SOURCE_DATE_EPOCH": 1704931200 + }, + "arches": { + "amd64": { + "tags": [ + "ubuntu:22.04", + "ubuntu:jammy-20240111", + "ubuntu:jammy", + "ubuntu:latest" + ], + "archTags": [], + "froms": [ + "scratch" + ], + "lastStageFrom": "scratch", + "platformString": "linux/amd64", + "platform": { + "architecture": "amd64", + "os": "linux" + }, + "parents": { + "scratch": { + "sourceId": null, + "pin": null + } + } + } + } } } diff --git a/.test/test.sh b/.test/test.sh index 3d5cff5..0a64f9d 100755 --- a/.test/test.sh +++ b/.test/test.sh @@ -8,7 +8,7 @@ dir="$(dirname "$BASH_SOURCE")" dir="$(readlink -ve "$dir")" export BASHBREW_LIBRARY="$dir/library" -set -- docker:cli docker:dind docker:windowsservercore notary # a little bit of Windows, a little bit of Linux, a little bit of multi-stage +set -- docker:cli docker:dind docker:windowsservercore notary ubuntu:latest # a little bit of Windows, a little bit of Linux, a little bit of multi-stage, a little bit of oci-import # (see "library/" and ".external-pins/" for where these come from / are hard-coded for consistent testing purposes) # NOTE: we are explicitly *not* pinning "golang:1.19-alpine3.16" so that this also tests unpinned parent behavior (that image is deprecated so should stay unchanging) diff --git a/meta.jq b/meta.jq index 71b1bb9..550c581 100644 --- a/meta.jq +++ b/meta.jq @@ -265,10 +265,76 @@ def build_command: ] | join("\n") elif $builder == "oci-import" then [ - "git init temp", # TODO figure out a good, safe place to temporary "git init"?? - @sh "git -C temp fetch \(.source.entry.GitRepo) \(.source.entry.GitCommit): || git -C temp fetch \(.source.entry.GitRepo) \(.source.entry.GitFetch):", - @sh "git -C temp checkout -q \(.source.entry.GitCommit)", - # TODO something clever, especially to deal with "index.json" vs not-"index.json" (possibly using "jq" to either synthesize/normalize to what we actually need it to be for "crane push temp/dir \(.build.img)") + # initialize "~/.cache/bashbrew/git" + #"gitCache=\"$(bashbrew cat --format '{{ gitCache }}' <(echo 'Maintainers: empty hack (@example)'))\"", + # https://github.com/docker-library/bashbrew/blob/5152c0df682515cbe7ac62b68bcea4278856429f/cmd/bashbrew/git.go#L52-L80 + "export BASHBREW_CACHE=\"${BASHBREW_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/bashbrew}\"", + "gitCache=\"$BASHBREW_CACHE/git\"", + "git init --bare \"$gitCache\"", + "_git() { git -C \"$gitCache\" \"$@\"; }", + "_git config gc.auto 0", + # "bashbrew fetch" but in Bash (because we have bashbrew, but not the library file -- we could synthesize a library file instead, but six of one half a dozen of another) + @sh "_commit() { _git rev-parse \(.source.entry.GitCommit + "^{commit}"); }", + @sh "if ! _commit &> /dev/null; then _git fetch \(.source.entry.GitRepo) \(.source.entry.GitCommit + ":") || _git fetch \(.source.entry.GitFetch + ":"); fi", + "_commit", + + # TODO figure out a good, safe place to store our temporary build/push directory (maybe this is fine? we do it for buildx build too) + "mkdir temp", + # https://github.com/docker-library/bashbrew/blob/5152c0df682515cbe7ac62b68bcea4278856429f/cmd/bashbrew/git.go#L140-L147 (TODO "bashbrew context" ?) + @sh "_git archive --format=tar \(.source.entry.GitCommit + ":" + (.source.entry.Directory | if . == "." then "" else . + "/" end)) | tar -xvC temp", + + # validate oci-layout file (https://github.com/docker-library/bashbrew/blob/4e0ea8d8aba49d54daf22bd8415fabba65dc83ee/cmd/bashbrew/oci-builder.go#L104-L112) + @sh "jq -s \(" + if length != 1 then + error(\"unexpected 'oci-layout' document count: \" + length) + else .[0] end + | if .imageLayoutVersion != \"1.0.0\" then + error(\"unsupported imageLayoutVersion: \" + .imageLayoutVersion) + else . end + " | unindent_and_decomment_jq(3)) temp/oci-layout > /dev/null", + + # https://github.com/docker-library/bashbrew/blob/4e0ea8d8aba49d54daf22bd8415fabba65dc83ee/cmd/bashbrew/oci-builder.go#L116 + if .source.entry.File != "index.json" then + @sh "jq -s \("{ schemaVersion: 2, manifests: . }") \("./" + .source.entry.File) > temp/index.json" + else empty end, + + @sh "jq -s \(" + if length != 1 then + error(\"unexpected 'index.json' document count: \" + length) + else .[0] end + + # https://github.com/docker-library/bashbrew/blob/4e0ea8d8aba49d54daf22bd8415fabba65dc83ee/cmd/bashbrew/oci-builder.go#L117-L127 + | if .schemaVersion != 2 then + error(\"unsupported schemaVersion: \" + .schemaVersion) + else . end + # TODO check .mediaType ? (technically optional, but does not have to be *and* shouldn't be); https://github.com/moby/buildkit/issues/4595 + | if .manifests | length != 1 then + error(\"expected only one manifests entry, not \" + (.manifests | length)) + else . end + + | .manifests[0] |= ( + # https://github.com/docker-library/bashbrew/blob/4e0ea8d8aba49d54daf22bd8415fabba65dc83ee/cmd/bashbrew/oci-builder.go#L135-L144 + if .mediaType != \"application/vnd.oci.image.manifest.v1+json\" then + error(\"unsupported descriptor mediaType: \" + .mediaType) + else . end + # TODO validate .digest somehow (`crane validate`? see below) - would also be good to validate all descriptors recursively (not sure if `crane push` does that) + | if .size < 0 then + error(\"invalid descriptor size: \" + .size) + else . end + + # purge maintainer-provided URLs / annotations (https://github.com/docker-library/bashbrew/blob/4e0ea8d8aba49d54daf22bd8415fabba65dc83ee/cmd/bashbrew/oci-builder.go#L146-L147) + | del(.annotations, .urls) + + # inject our annotations + | .annotations = \(build_annotations(.source.entry.GitRepo) | @json) + ) + " | unindent_and_decomment_jq(3)) temp/index.json > temp/index.json.new", + "mv temp/index.json.new temp/index.json", + + # TODO consider / check what "crane validate" does and if it would be appropriate here + + # TODO generate SBOM? ... somehow + empty ] | join("\n") else @@ -289,7 +355,11 @@ def push_command: empty ] | join("\n") elif $builder == "oci-import" then - "TODO" + [ + @sh "crane push --index temp \(.build.img)", + "rm -rf temp", + empty + ] | join("\n") else error("unknown/unimplemented Builder: \($builder)") end diff --git a/sources.sh b/sources.sh index c29431c..0c3cc9e 100755 --- a/sources.sh +++ b/sources.sh @@ -65,7 +65,12 @@ bashbrew cat --build-order --format ' "tags": {{ $.Tags namespace false . | json }}, "archTags": {{ if $archNs -}} {{ $.Tags $archNs false . | json }} {{- else -}} [] {{- end }}, "froms": {{ $.ArchDockerFroms $a . | json }}, - "lastStageFrom": {{ $.ArchLastStageFrom $a . | json }}, + "lastStageFrom": {{ if eq $builder "oci-import" -}} + {{- /* TODO remove this special case: https://github.com/docker-library/bashbrew/pull/92 */ -}} + "scratch" + {{- else -}} + {{ $.ArchLastStageFrom $a . | json }} + {{- end }}, "platformString": {{ (ociPlatform $a).String | json }}, "platform": {{ ociPlatform $a | json }}, "parents": { }