diff --git a/.gitignore b/.gitignore
index 853c7f7..a107cdc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
!overlay.bu
__pycache__/
.pytest_cache/
+**/build/*.tar.gz
diff --git a/cookbooks/Makefile b/cookbooks/Makefile
index 5a23302..cd75a41 100644
--- a/cookbooks/Makefile
+++ b/cookbooks/Makefile
@@ -1,14 +1,14 @@
SUBDIRS := $(wildcard */Makefile)
SUBDIRS := $(dir $(SUBDIRS))
-.PHONY: all help butane clean dryrun fcos-vm clean-vm uninstall $(SUBDIRS)
+.PHONY: all help package clean dryrun fcos-vm clean-vm uninstall $(SUBDIRS)
export I_KNOW_WHAT_I_AM_DOING ?= no
all: help
help:
@echo "Available targets:"
- @echo " butane - Build Butane specifications suitable for Fedora CoreOS"
+ @echo " package - Package the quadlets and systemd units for distribution (Butane, Ignition, tarball, etc.)"
@echo " clean - Remove the quadlets persistent data and configuration"
@echo " dryrun - Perform a dry run of the podman systemd generator"
@echo " fcos-vm - Launch a Fedora CoreOS VM with the generated Butane spec"
@@ -16,11 +16,9 @@ help:
@echo " uninstall - Uninstall the generated resources"
@echo " pytest - Run integration tests on a clean Fedora CoreOS VM"
-
dryrun: $(SUBDIRS)
-butane: $(SUBDIRS)
+package: $(SUBDIRS)
clean: $(SUBDIRS)
- rm -f local.ign
fcos-vm: $(SUBDIRS)
clean-vm: $(SUBDIRS)
diff --git a/scripts/cleanup-pytest-vm.sh b/scripts/cleanup-pytest-vm.sh
new file mode 100755
index 0000000..c6e3115
--- /dev/null
+++ b/scripts/cleanup-pytest-vm.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -u
+
+for vm in $(virsh list --name); do
+ virsh destroy $vm
+ virsh undefine --nvram $vm
+ rm -rf "/var/lib/libvirt/images/$vm"
+done
+
+rm -rf /srv/pebble /srv/fcos-test-*
+podman stop -i pebble-acme-server
+podman rm -fi pebble-acme-server
+
+virsh net-dumpxml default | grep -oP '(?<=" --live --config
+virsh net-destroy default
+virsh net-start default
diff --git a/scripts/common.mk b/scripts/common.mk
index ae75c13..8516935 100644
--- a/scripts/common.mk
+++ b/scripts/common.mk
@@ -20,12 +20,13 @@ help:
@echo " clean - Remove the quadlets persistent data and configuration"
@echo " dryrun - Perform a dry run of the podman systemd generator"
@echo " tail-logs - Tail the logs of the quadlet units"
- @echo " butane - Build Butane specifications suitable for Fedora CoreOS"
+ @echo " package - Package the quadlets and systemd units for distribution (Butane, Ignition, tarball, etc.)"
@echo " fcos-vm - Launch a Fedora CoreOS VM with the generated Butane spec"
@echo " clean-vm - Clean up the Fedora CoreOS VM but keep its storage resources"
@echo " remove-vm - Remove all resources related to the Fedora CoreOS VM"
@echo " console - Connect to the Fedora CoreOS VM console"
@echo " pytest - Run integration tests on a clean Fedora CoreOS VM"
+ @echo " debug - Dump makefile variables for debugging purposes"
@echo
@echo "Useful commands:"
@echo
@@ -33,10 +34,11 @@ help:
@echo " 2. make uninstall clean install # Same but also remove persistent data and configuration"
@echo " 3. make uninstall install tail-logs # Replace quadlets and systemd units, then tail their logs"
@echo " 4. make I_KNOW_WHAT_I_AM_DOING=yes clean # Remove all persistent data and configuration without confirmation"
- @echo " 5. make butane # Build Butane specifications suitable for Fedora CoreOS"
+ @echo " 5. make package # Package the quadlets and systemd units for distribution (Butane, Ignition, tarball, etc.)"
@echo " 6. make fcos-vm console # Launch a fresh Fedora CoreOS VM (while retaining its persistent data) and connect to its console"
@echo " 7. make remove-vm # Remove all resources related to the Fedora CoreOS VM"
@echo " 8. make pytest # Run integration tests on a clean Fedora CoreOS VM"
+ @echo " 9. make debug | sort # Dump makefile variables for debugging purposes"
@echo
@echo "All-in-one commands:"
@echo
@@ -44,8 +46,13 @@ help:
@echo " 2. make fcos-vm console"
@echo
+# Absolute paths derived from the location of this Makefile
+SCRIPTS_DIR := $(shell realpath $(dir $(lastword $(MAKEFILE_LIST))))
+TOP_LEVEL_DIR := $(shell realpath $(SCRIPTS_DIR)/..)
+COOKBOOKS_DIR := $(shell realpath $(TOP_LEVEL_DIR)/cookbooks)
+
# Create a temporary directory as target chroot when we detect that we are building Butane specs or launching a Fedora CoreOS VM.
-ifneq ($(filter %.ign %.bu butane fcos-vm,$(MAKECMDGOALS)),)
+ifneq ($(filter %.ign %.bu package fcos-vm,$(MAKECMDGOALS)),)
ifeq ($(TARGET_CHROOT),)
export TARGET_CHROOT := $(shell mktemp -d /tmp/butane-chroot-XXXXXX)
endif
@@ -61,11 +68,6 @@ endif
# This is used to create subdirectories for configuration and state files.
PROJECT_NAME := $(shell basename "$${PWD}")
-# Absolute paths derived from the location of this Makefile
-SCRIPTS_DIR := $(shell realpath $(dir $(lastword $(MAKEFILE_LIST))))
-TOP_LEVEL_DIR := $(shell realpath $(SCRIPTS_DIR)/..)
-COOKBOOKS_DIR := $(shell realpath $(TOP_LEVEL_DIR)/cookbooks)
-
# Quadlets files and their corresponding systemd unit names
QUADLETS_FILES = $(wildcard *.container *.volume *.network *.pod *.build *.image)
QUADLET_UNIT_NAMES := $(patsubst %.container, %.service, $(wildcard *.container)) \
@@ -104,8 +106,8 @@ TARGET_EXAMPLES_SYSCTLD_FILES = $(patsubst sysctl.d/examples/%, $(TARGET_CHROOT)
TARGET_EXAMPLES_PROFILED_FILES = $(patsubst profile.d/examples/%, $(TARGET_CHROOT)/etc/profile.d/%, $(EXAMPLES_PROFILED_FILES))
# Example quadlet and systemd drop-ins files
-EXAMPLES_QUADLET_DROPINS_FILES := $(shell find examples -mindepth 1 -type f | grep -E '\.(container|volume|network|pod|build|image)\.d/' 2>/dev/null)
-EXAMPLES_SYSTEMD_DROPINS_FILES := $(shell find examples -mindepth 1 -type f | grep -E '\.(service|target|timer|mount)\.d/' 2>/dev/null)
+EXAMPLES_QUADLET_DROPINS_FILES := $(shell if [ -d examples ]; then find examples -mindepth 1 -type f | grep -E '\.(container|volume|network|pod|build|image)\.d/' 2>/dev/null; fi)
+EXAMPLES_SYSTEMD_DROPINS_FILES := $(shell if [ -d examples ]; then find examples -mindepth 1 -type f | grep -E '\.(service|target|timer|mount)\.d/' 2>/dev/null; fi)
TARGET_EXAMPLES_QUADLET_DROPINS_FILES = $(patsubst examples/%, $(TARGET_CHROOT)/etc/containers/systemd/%, $(EXAMPLES_QUADLET_DROPINS_FILES))
TARGET_EXAMPLES_SYSTEMD_DROPINS_FILES = $(patsubst examples/%, $(TARGET_CHROOT)/etc/systemd/system/%, $(EXAMPLES_SYSTEMD_DROPINS_FILES))
@@ -126,10 +128,10 @@ I_KNOW_WHAT_I_AM_DOING ?=
# List of all ignition files corresponding to the dependencies
# Here, we inject the "base" project as a dependency. It can therefore be assumed to always be embeddable in project's butane specs.
-DEPENDENCIES_IGNITION_FILES := $(shell for dep in $$(if [ "$(PROJECT_NAME)" != "base" ]; then echo base; fi) $(DEPENDENCIES); do echo $(COOKBOOKS_DIR)/$$dep/$$dep.ign; done)
+DEPENDENCIES_IGNITION_FILES := $(shell for dep in $$(if [ "$(PROJECT_NAME)" != "base" ]; then echo base; fi) $(DEPENDENCIES); do echo $(COOKBOOKS_DIR)/$$dep/build/$$dep.ign; done)
# Variation of the previous variable with the built-in examples.
-DEPENDENCIES_IGNITION_EXAMPLES_FILES := $(shell for dep in $$(if [ "$(PROJECT_NAME)" != "base" ]; then echo base; fi) $(DEPENDENCIES); do echo $(COOKBOOKS_DIR)/$$dep/$$dep.ign $(COOKBOOKS_DIR)/$$dep/$$dep-examples.ign; done)
+DEPENDENCIES_IGNITION_EXAMPLES_FILES := $(shell for dep in $$(if [ "$(PROJECT_NAME)" != "base" ]; then echo base; fi) $(DEPENDENCIES); do echo $(COOKBOOKS_DIR)/$$dep/build/$$dep.ign $(COOKBOOKS_DIR)/$$dep/build/$$dep-examples.ign; done)
# User and group IDs to own the project files and directories.
PROJECT_UID ?= 0
@@ -139,6 +141,15 @@ PROJECT_GID ?= 0
HOOKS := $(wildcard $(COOKBOOKS_DIR)/*/hooks.mk)
include $(HOOKS)
+# Dump makefile variables for debugging purposes
+debug:
+ $(foreach v, $(filter %_FILES SYSTEMD_% QUADLET% PROJECT_% DEPENDENCIES% %_DIR, $(.VARIABLES)), $(info $(v): $($(v))))
+ @echo "TARGET_CHROOT: $(TARGET_CHROOT)"
+ @echo "BUTANE_BLOCKLIST: $(BUTANE_BLOCKLIST)"
+ @echo "BUTANE_START_TS: $(BUTANE_START_TS)"
+ @echo "HOOKS: $(HOOKS)"
+ @echo "I_KNOW_WHAT_I_AM_DOING: $(I_KNOW_WHAT_I_AM_DOING)"
+
# Ensure that the Makefile is run as root.
pre-requisites::
@if [ "$$(id -u)" -ne 0 ]; then \
@@ -342,11 +353,18 @@ tail-logs: pre-requisites
run journalctl "$${journalctl_args[@]}"
pytest: pre-requisites
- $(MAKE) butane
+ $(MAKE) package
pytest tests/
+build:
+ mkdir -p build
+
# Build the Butane specifications, suitable for Fedora CoreOS, including those of the dependencies of this project.
-$(PROJECT_NAME).bu $(PROJECT_NAME)-examples.bu &:
+build/$(PROJECT_NAME).tar.gz build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-examples.bu: export PROJECT_NAME := $(PROJECT_NAME)
+build/$(PROJECT_NAME).tar.gz build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-examples.bu: export TARGET_CHROOT := $(TARGET_CHROOT)
+build/$(PROJECT_NAME).tar.gz build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-examples.bu: export BUTANE_BLOCKLIST := $(BUTANE_BLOCKLIST)
+build/$(PROJECT_NAME).tar.gz build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-examples.bu: export SYSTEMD_MAIN_UNIT_NAMES := $(SYSTEMD_MAIN_UNIT_NAMES)
+build/$(PROJECT_NAME).tar.gz build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-examples.bu &:
@if [ -z "$(TARGET_CHROOT)" ]; then \
echo "TARGET_CHROOT is not set!"; exit 1; \
fi; \
@@ -357,55 +375,59 @@ $(PROJECT_NAME).bu $(PROJECT_NAME)-examples.bu &:
echo "BUTANE_START_TS is not set!"; exit 1; \
fi
@run() { echo $$*; "$$@"; }; \
+ export ALL_DEPS="$(shell $(MAKE) -s list-dependencies 2>/dev/null)"; \
set -Eeuo pipefail; \
- if [ $(PROJECT_NAME).bu -ot "$(BUTANE_START_TS)" ] || [ $(PROJECT_NAME)-examples.bu -ot "$(BUTANE_START_TS)" ]; then \
+ if [ build/$(PROJECT_NAME).bu -ot "$(BUTANE_START_TS)" ] || [ build/$(PROJECT_NAME)-examples.bu -ot "$(BUTANE_START_TS)" ]; then \
for dep in base $(DEPENDENCIES); do \
if [[ "$$dep" == "$(PROJECT_NAME)" ]]; then \
# Avoid building the current project as its own dependency. \
continue; \
fi ; \
- if [ $(BUTANE_START_TS) -ot "$(COOKBOOKS_DIR)/$$dep/$$dep.ign" ] && [ $(BUTANE_START_TS) -ot "$(COOKBOOKS_DIR)/$$dep/$$dep-examples.ign" ]; then \
+ if [ $(BUTANE_START_TS) -ot "$(COOKBOOKS_DIR)/$$dep/build/$$dep.ign" ] && [ $(BUTANE_START_TS) -ot "$(COOKBOOKS_DIR)/$$dep/build/$$dep-examples.ign" ]; then \
# Dependency is up-to-date. \
continue; \
fi ; \
- run $(MAKE) -C $(COOKBOOKS_DIR)/$$dep $$dep.ign $$dep-examples.ign ; \
+ run $(MAKE) -C $(COOKBOOKS_DIR)/$$dep build/$$dep.ign build/$$dep-examples.ign ; \
done; \
run make install-config; \
YQ_FILES="$$(if [ -f "overlay.bu" ]; then echo "- overlay.bu"; else echo "-"; fi)"; \
- echo "generate-butane-spec.sh $(TARGET_CHROOT) > $(PROJECT_NAME).bu"; \
- $(SCRIPTS_DIR)/generate-butane-spec.sh $(TARGET_CHROOT) $(BUTANE_BLOCKLIST) $(SYSTEMD_MAIN_UNIT_NAMES) $(SYSTEMD_TIMER_NAMES) | yq eval-all '. as $$item ireduce ({}; . *+ $$item)' $$YQ_FILES > $(PROJECT_NAME).bu; \
+ echo "generate-butane-spec.sh $(TARGET_CHROOT) > build/$(PROJECT_NAME).bu"; \
+ $(SCRIPTS_DIR)/generate-butane-spec.sh $(TARGET_CHROOT) $(BUTANE_BLOCKLIST) $(SYSTEMD_MAIN_UNIT_NAMES) $(SYSTEMD_TIMER_NAMES) | yq eval-all '. as $$item ireduce ({}; . *+ $$item)' $$YQ_FILES > build/$(PROJECT_NAME).bu; \
+ $(SCRIPTS_DIR)/generate-tarball.sh build/$(PROJECT_NAME).tar.gz; \
(cat $(SCRIPTS_DIR)/butane.blocklist; echo; for file in $$(find "$$TARGET_CHROOT"); do echo "$${file#$$TARGET_CHROOT}"; done) | sort -u | grep -v -E '^$$' > "$(BUTANE_BLOCKLIST)"; \
run make install-examples; \
- echo "generate-butane-spec.sh $(TARGET_CHROOT) > $(PROJECT_NAME)-examples.bu"; \
- $(SCRIPTS_DIR)/generate-butane-spec.sh $(TARGET_CHROOT) $(BUTANE_BLOCKLIST) > $(PROJECT_NAME)-examples.bu; \
+ echo "generate-butane-spec.sh $(TARGET_CHROOT) > build/$(PROJECT_NAME)-examples.bu"; \
+ $(SCRIPTS_DIR)/generate-butane-spec.sh $(TARGET_CHROOT) $(BUTANE_BLOCKLIST) > build/$(PROJECT_NAME)-examples.bu; \
(cat $(SCRIPTS_DIR)/butane.blocklist; echo; for file in $$(find "$$TARGET_CHROOT"); do echo "$${file#$$TARGET_CHROOT}"; done) | sort -u | grep -v -E '^$$' > "$(BUTANE_BLOCKLIST)"; \
fi
-.PHONY: $(PROJECT_NAME).bu $(PROJECT_NAME)-examples.bu
+.PHONY: build/$(PROJECT_NAME).bu build/$(PROJECT_NAME)-examples.bu
# Generate the current project's Ignition files from the Butane specs.
-$(PROJECT_NAME).ign $(PROJECT_NAME)-examples.ign: %.ign: %.bu
+build/$(PROJECT_NAME).ign build/$(PROJECT_NAME)-examples.ign: %.ign: %.bu build
butane --strict -o $@ $<
# Build the Butane specifications + Ignition files suitable for Fedora CoreOS, including those of the dependencies of this project.
-butane: fcos-dev.ign fcos-test.ign
+package: build/$(PROJECT_NAME).tar.gz build/fcos-dev.ign build/fcos-test.ign
# Generate the local Butane spec + Ignition file (the one containing local customizations).
-$(TOP_LEVEL_DIR)/local.ign: $(TOP_LEVEL_DIR)/local.bu
+$(TOP_LEVEL_DIR)/build/local.ign: $(TOP_LEVEL_DIR)/local.bu
+ mkdir -p $(TOP_LEVEL_DIR)/build
butane --strict -o $@ $<
-.INTERMEDIATE: fcos-dev.bu fcos-test.bu
+.INTERMEDIATE: build/fcos-dev.bu build/fcos-test.bu
+
# Generate the Butane specs for development and testing by merging the current project's spec with those of the dependencies.
# The development spec also includes the examples of the dependencies.
# Whereas the testing spec only includes the main specs of the dependencies.
-fcos-dev.bu fcos-test.bu: DEPS := $(if $(filter-out base,$(PROJECT_NAME)),base $(DEPENDENCIES),$(DEPENDENCIES))
-fcos-dev.bu: DEPS := $(DEPS) $(addsuffix -examples,$(DEPS))
-fcos-dev.bu fcos-test.bu: %.bu: Makefile $(SCRIPTS_DIR)/default-butane-spec.sh
+build/fcos-dev.bu build/fcos-test.bu: DEPS := $(if $(filter-out base,$(PROJECT_NAME)),base $(DEPENDENCIES),$(DEPENDENCIES))
+build/fcos-dev.bu: DEPS := $(DEPS) $(addsuffix -examples,$(DEPS))
+build/fcos-dev.bu build/fcos-test.bu: %.bu: Makefile $(SCRIPTS_DIR)/default-butane-spec.sh build
$(SCRIPTS_DIR)/default-butane-spec.sh $(PROJECT_NAME) $(DEPS) > $@
# Generate the final Fedora CoreOS ignition files (dev & test) by merging the Butane spec with the local and project-specific ignition files, as well as those of the dependencies.
-fcos-dev.ign: $(TOP_LEVEL_DIR)/local.ign $(PROJECT_NAME).ign $(PROJECT_NAME)-examples.ign $(DEPENDENCIES_IGNITION_EXAMPLES_FILES)
-fcos-test.ign: $(TOP_LEVEL_DIR)/local.ign $(PROJECT_NAME).ign $(DEPENDENCIES_IGNITION_FILES)
-fcos-dev.ign fcos-test.ign: fcos-%.ign: fcos-%.bu
+build/fcos-dev.ign: $(TOP_LEVEL_DIR)/build/local.ign build/$(PROJECT_NAME).ign build/$(PROJECT_NAME)-examples.ign $(DEPENDENCIES_IGNITION_EXAMPLES_FILES)
+build/fcos-test.ign: $(TOP_LEVEL_DIR)/build/local.ign build/$(PROJECT_NAME).ign $(DEPENDENCIES_IGNITION_FILES)
+build/fcos-dev.ign build/fcos-test.ign: build/fcos-%.ign: build/fcos-%.bu build
@run() { echo $$*; "$$@"; }; \
set -Eeuo pipefail; \
tmp=$$(mktemp -d /tmp/butane-XXXXXX); \
@@ -426,7 +448,7 @@ fcos-dev.ign fcos-test.ign: fcos-%.ign: fcos-%.bu
run mv "$$qcow2" $@
# Copy the ignition file.
-/var/lib/libvirt/images/fcos-$(PROJECT_NAME)/fcos.ign: fcos-dev.ign
+/var/lib/libvirt/images/fcos-$(PROJECT_NAME)/fcos.ign: build/fcos-dev.ign
install -D -o root -g root -m 0644 $< $@
# Copy the Fedora CoreOS base image to create a new QCOW2 image for the VM.
@@ -488,6 +510,13 @@ units-pre::
units: units-pre
@for unit in $(SYSTEMD_UNIT_NAMES) $(QUADLET_UNIT_NAMES); do echo "$$unit"; done
+# List all dependencies of this project and also its transitive dependencies.
+list-dependencies:
+ @for dep in $(DEPENDENCIES); do \
+ echo "$$dep"; \
+ $(MAKE) -s -C $(COOKBOOKS_DIR)/$$dep list-dependencies 2>/dev/null; \
+ done | sort -u
+
# Custom commands to be run before cleaning persistent data and configuration files.
# This target can be extended by Makefiles sourcing this one.
clean-pre::
@@ -503,7 +532,7 @@ clean-post::
# Remove all persistent data and configuration files
clean: clean-pre pre-requisites
- rm -f $(PROJECT_NAME){,-examples}.bu *.ign butane.blocklist
+ rm -f build/$(PROJECT_NAME){,-examples}.bu build/*.ign
@run() { echo $$*; "$$@"; }; \
set -Eeuo pipefail; \
if [ "$(I_KNOW_WHAT_I_AM_DOING)" != "yes" ]; then \
@@ -517,7 +546,7 @@ clean: clean-pre pre-requisites
# All phony targets
.PHONY: all install install-config install-examples uninstall pre-requisites clean dryrun
-.PHONY: tail-logs butane help fcos-vm clean-vm console units units-pre remove-vm
+.PHONY: tail-logs package help fcos-vm clean-vm console units units-pre remove-vm
.PHONY: clean-pre clean-post install-pre install-post uninstall-pre uninstall-post
.PHONY: install-files install-files-pre install-files-post install-actions
-.PHONY: install-actions-pre install-actions-post pytest
+.PHONY: install-actions-pre install-actions-post pytest list-dependencies debug
diff --git a/scripts/generate-tarball.sh b/scripts/generate-tarball.sh
new file mode 100755
index 0000000..f886a30
--- /dev/null
+++ b/scripts/generate-tarball.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# This tool packages the current project as a tarball.
+#
+# The specification of the tarball is:
+# - metadata.json: the project's metadata (name, dependencies, file list, unit names, etc.)
+# - content.tar: the files to be installed on the target system, with their relative paths and permissions preserved.
+#
+# The tool takes its parameters from the environment variables defined in the Makefile:
+#
+# - PROJECT_NAME: the name of the project.
+# - ALL_DEPS: the list of all (direct and transitive) dependencies of the project.
+# - TARGET_CHROOT: the target chroot directory containing the files to be included in the tarball.
+# - BUTANE_BLOCKLIST: the path to a file containing a list of files and directories (one per line) to ignore
+# (i.e., files and directories that are already part of the CoreOS default installation
+# or belonging to another package).
+# - SYSTEMD_MAIN_UNIT_NAMES: the list of systemd main unit names to enable.
+#
+# The path to of the output tarball is $1.
+#
+
+set -Eeuo pipefail
+
+# Create a temporary directory to store intermediate files (metadata.json and content.tar)
+tmp_dir=$(mktemp -d)
+trap 'rm -rf "$tmp_dir"' EXIT
+
+# Generate the file list from the TARGET_CHROOT, excluding files and directories in the BUTANE_BLOCKLIST
+declare -a files_to_include=()
+filelist_file="$tmp_dir/filelist.txt"
+for path in $(find "$TARGET_CHROOT"); do
+ rel_path="${path#$TARGET_CHROOT}"
+ # Skip files & directories that are already part of the CoreOS default installation
+ if grep -qxF "$rel_path" "$BUTANE_BLOCKLIST"; then
+ continue
+ fi
+
+ # Skip the root directory and empty paths
+ if [ -z "$rel_path" ] || [[ "$rel_path" == "/" ]]; then
+ continue
+ fi
+
+ # The leading / is removed from the relative path in order for tar to find the file.
+ echo "${rel_path#/}" >> "$filelist_file"
+
+ # Although, the absolute path is stored in the metadata file.
+ files_to_include+=("$rel_path")
+done
+
+# Generate metadata.json
+metadata_file="$tmp_dir/metadata.yaml"
+cat < "$metadata_file"
+name: $PROJECT_NAME
+EOF
+if [ -n "${ALL_DEPS}" ]; then
+ echo "dependencies:" >> "$metadata_file"
+ for dep in ${ALL_DEPS};
+ do echo "- $dep"
+ done >> "$metadata_file"
+else
+ echo "dependencies: []" >> "$metadata_file"
+fi
+if [ "${#files_to_include[@]}" -gt 0 ]; then
+ echo "files:" >> "$metadata_file"
+ for file in "${files_to_include[@]}"; do
+ echo "- $file"
+ done >> "$metadata_file"
+else
+ echo "files: []" >> "$metadata_file"
+fi
+if [ -n "${SYSTEMD_MAIN_UNIT_NAMES}" ]; then
+ echo "systemd_main_units:" >> "$metadata_file"
+ for unit in ${SYSTEMD_MAIN_UNIT_NAMES}; do
+ echo "- $unit"
+ done >> "$metadata_file"
+else
+ echo "systemd_main_units: []" >> "$metadata_file"
+fi
+# Convert metadata.yaml to metadata.json
+yq -o json "$metadata_file" > "$tmp_dir/metadata.json"
+
+# Common tar options to ensure that the tarball is reproducible and does not contain unnecessary metadata
+declare -a tar_options=(
+ "--no-selinux"
+ "--no-recursion"
+ "--no-xattrs"
+ "--no-acls"
+)
+
+# Generate content.tar with the files to be included in the tarball, preserving their relative paths and permissions.
+tar -cf "$tmp_dir/content.tar" "${tar_options[@]}" -C "$TARGET_CHROOT" --verbatim-files-from --files-from="$filelist_file"
+
+# Generate the final tarball.
+tar -czf "$1" "${tar_options[@]}" -C "$tmp_dir" --owner=0 --group=0 metadata.json content.tar
diff --git a/tests/fcos_vm.py b/tests/fcos_vm.py
index 57225af..f8a0efc 100644
--- a/tests/fcos_vm.py
+++ b/tests/fcos_vm.py
@@ -31,11 +31,11 @@ FCOS_BASE_IMAGE = LIBVIRT_IMAGES_DIR / "library" / "fedora-coreos.qcow2"
BUTANE_VERSION = "1.4.0"
def ensure_fcos_ign(cookbook_dir: Path) -> Path:
- """Return the path to fcos-test.ign, building it via ``make butane`` if absent."""
- fcos_ign = cookbook_dir / "fcos-test.ign"
+ """Return the path to fcos-test.ign, building it via ``make package`` if absent."""
+ fcos_ign = cookbook_dir / "build" / "fcos-test.ign"
if not fcos_ign.exists():
subprocess.run(
- ["make", "-C", str(cookbook_dir), "butane"],
+ ["make", "-C", str(cookbook_dir), "package"],
check=True,
)
return fcos_ign