Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ runs:
sudo apt-get update
if [ "${{ inputs.arch }}" = "amd64" ]; then
sudo docker system prune -a -f
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends qemu-user-static
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends qemu-user-binfmt
elif [ "${{ inputs.arch }}" = "arm64" ]; then
sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends podman uidmap slirp4netns dbus-user-session
id="$(id -u)"
Expand Down
13 changes: 0 additions & 13 deletions build
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ container_run_opts=(
container_cmd=()

use_kms=0
resolve_cname=0
allow_frankenstein=0
apparmor_profile=

Expand Down Expand Up @@ -56,10 +55,6 @@ while [ $# -gt 0 ]; do
printf '%s\n' "$container_image" >&3
exit 0
;;
--resolve-cname)
resolve_cname=1
shift
;;
--target)
target_dir="$2"
shift 2
Expand Down Expand Up @@ -96,14 +91,6 @@ timestamp="$(./get_timestamp)"
default_version="$(./get_version)"


if [ "$resolve_cname" = 1 ]; then
arch="$("$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" dpkg --print-architecture)"
cname="$("$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" /builder/parse_features --feature-dir /builder/features --default-arch "$arch" --default-version "$default_version" --cname "$1")"
short_commit="$(head -c 8 <<< "$commit")"
echo "$cname-$short_commit" >&3
exit 0
fi

make_opts=(
REPO="$repo"
COMMIT="$commit"
Expand Down
58 changes: 31 additions & 27 deletions builder/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ MAKEFLAGS += --no-builtin-rules

lastword = $(word $(words $1),$1)
prelastword = $(word $(words $1),_ $1)
cname_version = $(call lastword,$(subst -, ,$1))
cname_arch = $(call prelastword,$(subst -, ,$1))
extract_version = $(call lastword,$(subst -, ,$1))
extract_arch = $(call prelastword,$(subst -, ,$1))

define require_var =
ifndef $1
Expand Down Expand Up @@ -43,32 +43,34 @@ clean:
.build/%.sentinel:
true

.build/bootstrap-%-$(SHORT_COMMIT).tar: $$(shell ./make_repo_sentinel $$(REPO) $$(call cname_version,$$*))
.build/bootstrap-%-$(SHORT_COMMIT).tar: $$(shell ./make_repo_sentinel $$(REPO) $$(call extract_version,$$*))
target '$@'
info 'bootstrapping $*-$(SHORT_COMMIT)'
arch='$(call cname_arch,$*)'
version='$(call cname_version,$*)'
arch='$(call extract_arch,$*)'
version='$(call extract_version,$*)'
./bootstrap "$$arch" "$$version" '$(REPO)' keyring.gpg '$@'

.build/%-$(SHORT_COMMIT).tar: .build/bootstrap-$$(call cname_arch,$$*)-$$(call cname_version,$$*)-$(SHORT_COMMIT).tar $(shell ./make_directory_sentinel features) $(shell ./make_directory_sentinel cert)
.build/%-$(SHORT_COMMIT).tar: .build/bootstrap-$$(call extract_arch,$$*)-$$(call extract_version,$$*)-$(SHORT_COMMIT).tar $(shell ./make_directory_sentinel features) $(shell ./make_directory_sentinel cert)
target '$@' '$<'
info 'configuring rootfs $*-$(SHORT_COMMIT)'
features="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$*' features)"
features_platforms="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$*' platforms)"
features_elements="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$*' elements)"
features_flags="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$*' flags)"
cname_base="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$*' cname_base)"
BUILDER_CNAME='$*'
BUILDER_CNAME_BASE="$$cname_base"
BUILDER_VERSION='$(call cname_version,$*)'
BUILDER_ARCH='$(call cname_arch,$*)'
features="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$*' features)"
features_platforms="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$*' platforms)"
features_elements="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$*' elements)"
features_flags="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$*' flags)"
cname="$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$*' cname)"
BUILDER_CNAME="$$cname"
BUILDER_FLAVOR="$$cname-$(call extract_arch,$*)"
BUILDER_VERSIONED_FLAVOR='$*'
BUILDER_ARTIFACT_BASE='$*-$(SHORT_COMMIT)'
BUILDER_VERSION='$(call extract_version,$*)'
BUILDER_ARCH='$(call extract_arch,$*)'
BUILDER_TIMESTAMP='$(TIMESTAMP)'
BUILDER_COMMIT='$(COMMIT)'
BUILDER_FEATURES="$$features"
BUILDER_FEATURES_PLATFORMS="$$features_platforms"
BUILDER_FEATURES_ELEMENTS="$$features_elements"
BUILDER_FEATURES_FLAGS="$$features_flags"
export BUILDER_CNAME BUILDER_CNAME_BASE BUILDER_VERSION BUILDER_ARCH BUILDER_TIMESTAMP BUILDER_COMMIT BUILDER_FEATURES BUILDER_FEATURES_PLATFORMS BUILDER_FEATURES_ELEMENTS BUILDER_FEATURES_FLAGS
export BUILDER_CNAME BUILDER_FLAVOR BUILDER_VERSIONED_FLAVOR BUILDER_ARTIFACT_BASE BUILDER_VERSION BUILDER_ARCH BUILDER_TIMESTAMP BUILDER_COMMIT BUILDER_FEATURES BUILDER_FEATURES_PLATFORMS BUILDER_FEATURES_ELEMENTS BUILDER_FEATURES_FLAGS
./configure '$(word 1,$^)' '$@'

define artifact_template =
Expand All @@ -77,22 +79,24 @@ define artifact_template =
input='$$(word 2,$$^)'
target '$$@' "$$$$input"
info 'building $1 image $$*'
features="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$$*' features)"
features_platforms="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$$*' platforms)"
features_elements="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$$*' elements)"
features_flags="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$$*' flags)"
cname_base="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --cname '$$*' cname_base)"
BUILDER_CNAME='$$*'
BUILDER_CNAME_BASE="$$$$cname_base"
BUILDER_VERSION='$$(call cname_version,$$*)'
BUILDER_ARCH='$$(call cname_arch,$$*)'
features="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$$*' features)"
features_platforms="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$$*' platforms)"
features_elements="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$$*' elements)"
features_flags="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$$*' flags)"
cname="$$$$(./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --target '$$*' cname)"
BUILDER_CNAME="$$$$cname"
BUILDER_FLAVOR="$$$$cname-$$(call extract_arch,$$*)"
BUILDER_VERSIONED_FLAVOR='$$*'
BUILDER_ARTIFACT_BASE='$$*-$(SHORT_COMMIT)'
BUILDER_VERSION='$$(call extract_version,$$*)'
BUILDER_ARCH='$$(call extract_arch,$$*)'
BUILDER_TIMESTAMP='$$(TIMESTAMP)'
BUILDER_COMMIT='$$(COMMIT)'
BUILDER_FEATURES="$$$$features"
BUILDER_FEATURES_PLATFORMS="$$$$features_platforms"
BUILDER_FEATURES_ELEMENTS="$$$$features_elements"
BUILDER_FEATURES_FLAGS="$$$$features_flags"
export BUILDER_CNAME BUILDER_CNAME_BASE BUILDER_VERSION BUILDER_ARCH BUILDER_TIMESTAMP BUILDER_COMMIT BUILDER_FEATURES BUILDER_FEATURES_PLATFORMS BUILDER_FEATURES_ELEMENTS BUILDER_FEATURES_FLAGS
export BUILDER_CNAME BUILDER_FLAVOR BUILDER_VERSIONED_FLAVOR BUILDER_ARTIFACT_BASE BUILDER_VERSION BUILDER_ARCH BUILDER_TIMESTAMP BUILDER_COMMIT BUILDER_FEATURES BUILDER_FEATURES_PLATFORMS BUILDER_FEATURES_ELEMENTS BUILDER_FEATURES_FLAGS
"./$$$$script" "$$$$input" '$$@'
endef

Expand All @@ -106,7 +110,7 @@ $(foreach artifact_rule,$(shell ./make_get_artifact_rules),$(eval $(call artifac
echo "$$(basename "$$f").log" | tee -a '$@'
done

%: .build/$$(shell ./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --default-arch '$$(DEFAULT_ARCH)' --default-version '$$(DEFAULT_VERSION)' --cname '$$*')-$(SHORT_COMMIT).artifacts
%: .build/$$(shell ./parse_features $(PARSE_FEATURES_ARGS) --feature-dir features --default-arch '$$(DEFAULT_ARCH)' --default-version '$$(DEFAULT_VERSION)' --target '$$*')-$(SHORT_COMMIT).artifacts
ln -f -s -r '$<' '.build/$*'

# prevents match anything rule from applying to Makefile and image/convert scripts
Expand Down
3 changes: 3 additions & 0 deletions builder/image.sbom
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ SYFT_CACHE_DIR="$chroot_dir"/syft-cache syft --quiet "$1" --config syft.yaml --o
--arg cname "$BUILDER_CNAME" \
--arg arch "$BUILDER_ARCH" \
--arg version "$BUILDER_VERSION" \
--arg commit "$BUILDER_COMMIT" \
--arg features "$BUILDER_FEATURES" \
--arg builder_timestamp "$BUILDER_TIMESTAMP" \
--arg cpe "" \
Expand All @@ -25,6 +26,8 @@ SYFT_CACHE_DIR="$chroot_dir"/syft-cache syft --quiet "$1" --config syft.yaml --o
"properties": [
{"name": "cname", "value": $cname},
{"name": "arch", "value": $arch},
{"name": "version", "value": $version},
{"name": "commit", "value": $commit},
{"name": "features", "value": $features},
{"name": "build timestamp", "value": $builder_timestamp}
]
Expand Down
4 changes: 2 additions & 2 deletions builder/make_get_image_dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ exec 1>&2
# get longest chain of extensions, but not extensions starting with a number to prevent parsing minor version as extension
extension="$(grep -E -o '(\.[a-z][a-zA-Z0-9\-_]*)*$' <<< "$1")"
artifact_base="${1%"-$COMMIT$extension"}"
cname="$(basename "$artifact_base")"
versioned_flavor="$(basename "$artifact_base")"

[ "$extension" != ".raw" ] || extension=

Expand All @@ -26,7 +26,7 @@ if [ -f "convert$extension" ]; then
input="$artifact_base-$COMMIT.raw"
fi

IFS=',' read -r -a features < <(./parse_features --allow-frankenstein --feature-dir features --cname "$cname" features)
IFS=',' read -r -a features < <(./parse_features --allow-frankenstein --feature-dir features --target "$versioned_flavor" features)

for feature in "${features[@]}"; do
if [ -s "features/$feature/image$extension" ]; then
Expand Down
12 changes: 6 additions & 6 deletions builder/make_list_build_artifacts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
set -euo pipefail
shopt -s nullglob

cname="$1"
versioned_flavor="$1"

IFS=',' read -r -a features < <(./parse_features --allow-frankenstein --feature-dir features --cname "$cname" features)
IFS=',' read -r -a features < <(./parse_features --allow-frankenstein --feature-dir features --target "$versioned_flavor" features)

artifacts=(".build/$cname-$COMMIT.tar" ".build/$cname-$COMMIT.release" ".build/$cname-$COMMIT.manifest" ".build/$cname-$COMMIT.sbom" ".build/$cname-$COMMIT.requirements")
artifacts=(".build/$versioned_flavor-$COMMIT.tar" ".build/$versioned_flavor-$COMMIT.release" ".build/$versioned_flavor-$COMMIT.manifest" ".build/$versioned_flavor-$COMMIT.sbom" ".build/$versioned_flavor-$COMMIT.requirements")

for feature in "${features[@]}"; do
for i in "features/$feature/"{image,convert}.*; do
# get target artifact file extension, usually this is the image/convert script extension
# except if the script extension is of the form filename.extA~extB in which case the artifact extension is .extA only
extension="$(grep -E -o '(\.[a-z][a-zA-Z0-9\-_~]*)*$' <<< "$i")"
artifacts+=(".build/$cname-$COMMIT${extension%~*}")
artifacts+=(".build/$versioned_flavor-$COMMIT${extension%~*}")
done
done

if [ "${#artifacts[@]}" = 5 ] && [ -n "$(./parse_features --allow-frankenstein --feature-dir "features" --cname "$cname" platforms)" ]; then
artifacts+=(".build/$cname-$COMMIT.raw")
if [ "${#artifacts[@]}" = 5 ] && [ -n "$(./parse_features --allow-frankenstein --feature-dir "features" --target "$versioned_flavor" platforms)" ]; then
artifacts+=(".build/$versioned_flavor-$COMMIT.raw")
fi

echo "${artifacts[@]}"
50 changes: 29 additions & 21 deletions builder/parse_features
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def main():
parser.add_argument("--feature-dir", default="features")
parser.add_argument("--features", type=lambda arg: set([f for f in arg.split(",") if f]))
parser.add_argument("--ignore", type=lambda arg: set([f for f in arg.split(",") if f]), default=set())
parser.add_argument("--cname")
parser.add_argument("--target")
parser.add_argument("--arch")
parser.add_argument("--version")
parser.add_argument("--default-arch")
Expand All @@ -24,7 +24,8 @@ def main():

args_type_allowed = [
"cname",
"cname_base",
"flavor",
"versioned_flavor",
"features",
"platforms",
"flags",
Expand All @@ -34,22 +35,26 @@ def main():
"graph"
]

parser.add_argument("type", nargs="?", choices=args_type_allowed, default="cname")
parser.add_argument("type", nargs="?", choices=args_type_allowed, default="versioned_flavor")
args = parser.parse_args()

assert bool(args.features) ^ bool(args.cname), "please provide either `--features` or `--cname` argument"
assert bool(args.features) ^ bool(args.target), "please provide either `--features` or `--target` argument"

arch = None
version = None

if args.cname:
search = re.search("^([a-z][a-zA-Z0-9_-]*?)(-(amd64|arm64)(-([a-z0-9.]+))?)?$", args.cname)
assert search, f"not a valid cname {args.cname}"
if args.target:
# the target is the raw string a dev passes to the builder, at any level
# of specificity (cname, flavor, or versioned flavor) and not necessarily
# in canonical order; the canonical cname/flavor/versioned flavor are
# derived from it below
search = re.search("^([a-z][a-zA-Z0-9_-]*?)(-(amd64|arm64)(-([a-z0-9.]+))?)?$", args.target)
assert search, f"not a valid target {args.target}"
matches = search.groups()
input_cname_base = matches[0]
input_cname = matches[0]
arch = matches[2]
version = matches[4]
input_features = reverse_cname_base(input_cname_base)
input_features = reverse_cname(input_cname)
else:
input_features = args.features

Expand All @@ -59,10 +64,10 @@ def main():
if args.version:
version = args.version

if not arch and (args.type == "cname" or args.type == "arch"):
if not arch and (args.type == "flavor" or args.type == "versioned_flavor" or args.type == "arch"):
assert args.default_arch, "architecture not specified and no default architecture set"
arch = args.default_arch
if not version and (args.type == "cname" or args.type == "version"):
if not version and (args.type == "versioned_flavor" or args.type == "version"):
assert args.default_version, "version not specified and no default version set"
version = args.default_version

Expand Down Expand Up @@ -98,13 +103,16 @@ def main():
sorted_features = sort_nodes(graph)
minimal_feature_set = get_minimal_feature_set(graph)
sorted_minimal_features = sort_set(minimal_feature_set, sorted_features)
cname_base = get_cname_base(sorted_minimal_features)
cname = f"{cname_base}-{arch}-{version}"
cname = get_cname(sorted_minimal_features)
flavor = f"{cname}-{arch}"
versioned_flavor = f"{cname}-{arch}-{version}"

if args.type == "cname_base":
print(cname_base)
elif args.type == "cname":
if args.type == "cname":
print(cname)
elif args.type == "flavor":
print(flavor)
elif args.type == "versioned_flavor":
print(versioned_flavor)
elif args.type == "features":
print(",".join(features))
elif args.type == "platforms":
Expand All @@ -114,17 +122,17 @@ def main():
elif args.type == "flags":
print(",".join(features_by_type["flag"]))
elif args.type == "graph":
print(graph_as_mermaid_markup(cname_base, graph))
print(graph_as_mermaid_markup(cname, graph))

def graph_as_mermaid_markup(cname_base, graph):
def graph_as_mermaid_markup(cname, graph):
"""
Generates a mermaid.js representation of the graph.
This is helpful to identify dependencies between features.

Syntax docs:
https://mermaid.js.org/syntax/flowchart.html?id=flowcharts-basic-syntax
"""
markup = f"---\ntitle: Dependency Graph for Feature {cname_base}\n---\ngraph TD;\n"
markup = f"---\ntitle: Dependency Graph for Feature {cname}\n---\ngraph TD;\n"
for u,v in networkx.edges(graph):
markup += f" {u}-->{v};\n"
return markup
Expand All @@ -139,11 +147,11 @@ def popen_read(command):
f.close()
return output

def reverse_cname_base(cname):
def reverse_cname(cname):
cname = cname.replace("_", "-_")
return set(cname.split("-"))

def get_cname_base(sorted_features):
def get_cname(sorted_features):
return reduce(lambda a, b : a + ("-" if not b.startswith("_") else "") + b, sorted_features)

def get_minimal_feature_set(graph):
Expand Down
5 changes: 0 additions & 5 deletions docs/reference/builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,9 @@ builder container. This means standard `make` flags such as `-j` are valid here.
| `--privileged` | _(none)_ | Off | Runs the builder container with `--privileged` and passes `--second-stage` to the container entrypoint. Required for two-stage builds that need elevated privileges inside the container. |
| `--kms` | _(none)_ | Off | Forwards AWS credential environment variables (`AWS_DEFAULT_REGION`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) into the build. Required when Secure Boot key operations during the build use AWS Key Management Service. |
| `--allow-frankenstein` | _(none)_ | Off | Disables the [single-platform enforcement](/reference/adr/0020-enforce-single-platform-by-default-in-builder) by passing `ALLOW_FRANKENSTEIN=1` to the build. Allows composing a flavor from multiple platform features. See [ADR 0020](/reference/adr/0020-enforce-single-platform-by-default-in-builder) for the rationale. |
| `--resolve-cname` | _(none)_ | Off | Resolves the given flavor argument to the artifact base name (`{cname}-{arch}-{version}-{short_commit}`), prints it to stdout, and exits. Requires exactly one flavor argument. Useful for generating reproducible artifact names in scripts. |
| `--print-container-image` | _(none)_ | Off | Prints the pinned builder container image reference to stdout and exits immediately. Useful for scripts that need to pull or inspect the builder image independently of running a build. |
| `--apparmor-profile` | `<profile>` | Auto-detected | Applies the named AppArmor profile to the builder container, replacing the default `apparmor=unconfined`. If not set and Docker is used on a host with `kernel.apparmor_restrict_unprivileged_userns = 1`, the script detects the restriction and offers to create a `builder` profile at `/etc/apparmor.d/builder` automatically. |

:::warning
The `--resolve-cname` flag will be renamed `--resolve-artifact-basename` in [builder#156](https://github.com/gardenlinux/builder/issues/156).
:::

### `make` flags

Any argument not consumed by the option parser above is forwarded to `make` inside the builder
Expand Down
Loading
Loading