Compare commits
1 commit
main
...
platform-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
397612c14b |
3 changed files with 304 additions and 0 deletions
65
hack/dev-overlay.md
Normal file
65
hack/dev-overlay.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Dev Overlay
|
||||
|
||||
Patch the cluster's platform OCI artifact with your local changes without rebuilding or uploading the entire packages tree.
|
||||
|
||||
## Why not `make image-packages`?
|
||||
|
||||
`make image-packages` (from `packages/core/installer/Makefile`) pushes the **entire** `packages/` directory from your current working tree as a new OCI artifact. This has two problems:
|
||||
|
||||
1. **Image version leakage.** Your working tree inherits image versions from the branch you're on (e.g. `cozystack-controller:v1.1.0` on main), which differ from the pinned versions in the cluster's release (e.g. `v1.1.4`). Uploading the whole tree overwrites them all.
|
||||
|
||||
2. **Single-branch only.** If you develop features across multiple branches (branch A changes component A, branch B changes component B), you can only upload from one branch at a time. The old workaround was to create a "frankenstein" branch by merging all feature branches onto a release tag — manual and error-prone.
|
||||
|
||||
## How dev-overlay works
|
||||
|
||||
Instead of replacing the entire artifact, dev-overlay **patches** it with only the files you changed:
|
||||
|
||||
1. Pulls the existing overlay from the registry (or the cluster's base OCI on first run)
|
||||
2. Runs `git diff origin/main -- packages/` against your working tree to find changed files
|
||||
3. Copies only those files into the artifact, deletes removed files, cleans up renames
|
||||
4. Pushes the patched artifact and points the operator at its digest
|
||||
|
||||
Because the diff is against `origin/main` (or a ref you choose), files you didn't touch — including values with pinned image versions — are never overwritten. The cluster keeps its release versions.
|
||||
|
||||
## Accumulation across branches
|
||||
|
||||
Changes from multiple branches stack on top of each other:
|
||||
|
||||
```
|
||||
# On branch A (changes component A)
|
||||
make dev-overlay
|
||||
|
||||
# Switch to branch B (changes component B)
|
||||
make dev-overlay
|
||||
```
|
||||
|
||||
After both runs, the overlay contains changes from both A and B. Files that only A touched are preserved when B runs (B's diff doesn't mention them). If both branches modify the same file, the last applied version wins.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
cd packages/core/installer
|
||||
|
||||
# Preview what would be applied
|
||||
make dev-overlay-diff
|
||||
|
||||
# Apply changes (defaults to diffing against origin/main)
|
||||
make dev-overlay
|
||||
|
||||
# Override the diff base
|
||||
make dev-overlay DEV_BASE_TAG=some-commit-hash
|
||||
```
|
||||
|
||||
## Resetting
|
||||
|
||||
To discard all overlay changes, delete the overlay tag from the registry and restore the operator to the original platform source. There is no automated undo — just point the operator back at the base artifact.
|
||||
|
||||
## How DEV_BASE_TAG affects correctness
|
||||
|
||||
`DEV_BASE_TAG` (default: `origin/main`) is the git ref your changes are diffed against. For image versions to be preserved correctly, it must be a ref where inherited values match your working tree:
|
||||
|
||||
- Your branch has `image: v1.1.0` (inherited from main)
|
||||
- `origin/main` also has `image: v1.1.0`
|
||||
- No diff detected — cluster keeps its `v1.1.4`
|
||||
|
||||
If you rebase onto a newer main that bumped a version, `origin/main` tracks that automatically. If you need a fixed point, pass a specific commit hash.
|
||||
216
hack/dev-overlay.sh
Executable file
216
hack/dev-overlay.sh
Executable file
|
|
@ -0,0 +1,216 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
# Dev overlay: overlay working-tree changes onto a deployed OCI artifact.
|
||||
#
|
||||
# Usage: dev-overlay.sh <diff|apply>
|
||||
#
|
||||
# Required env vars:
|
||||
# REGISTRY OCI registry to push to
|
||||
# Optional env vars:
|
||||
# DEV_BASE_TAG git ref to diff against (default: origin/main)
|
||||
# DEV_OVERLAY_TAG tag for the overlay artifact (default: dev-overlay)
|
||||
# OPERATOR_DEPLOY operator Deployment name (default: cozystack-operator)
|
||||
# OPERATOR_CONTAINER container name in the Deployment (default: $OPERATOR_DEPLOY)
|
||||
# PLATFORM_OCIREPO OCIRepository resource name (default: cozystack-platform)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)"
|
||||
|
||||
DEV_BASE_TAG="${DEV_BASE_TAG:-origin/main}"
|
||||
DEV_OVERLAY_TAG="${DEV_OVERLAY_TAG:-dev-overlay}"
|
||||
OPERATOR_DEPLOY="${OPERATOR_DEPLOY:-cozystack-operator}"
|
||||
OPERATOR_CONTAINER="${OPERATOR_CONTAINER:-$OPERATOR_DEPLOY}"
|
||||
PLATFORM_OCIREPO="${PLATFORM_OCIREPO:-cozystack-platform}"
|
||||
|
||||
cluster_oci_url() {
|
||||
kubectl get ocirepository "$PLATFORM_OCIREPO" -n cozy-system \
|
||||
-o jsonpath='{.spec.url}' 2>/dev/null || true
|
||||
}
|
||||
|
||||
resolve_cluster_pull_ref() {
|
||||
local url="$1"
|
||||
local tag digest
|
||||
tag=$(kubectl get ocirepository "$PLATFORM_OCIREPO" -n cozy-system \
|
||||
-o jsonpath='{.spec.ref.tag}' 2>/dev/null || true)
|
||||
digest=$(kubectl get ocirepository "$PLATFORM_OCIREPO" -n cozy-system \
|
||||
-o jsonpath='{.spec.ref.digest}' 2>/dev/null || true)
|
||||
|
||||
if [ -n "$tag" ]; then
|
||||
echo "${url}:${tag}"
|
||||
elif [ -n "$digest" ]; then
|
||||
echo "${url}@${digest}"
|
||||
fi
|
||||
}
|
||||
|
||||
pull_or_init_workdir() {
|
||||
local workdir="$1"
|
||||
local oci_url="$2"
|
||||
|
||||
if flux pull artifact "oci://${REGISTRY}/cozystack-packages:${DEV_OVERLAY_TAG}" \
|
||||
--output "$workdir" 2>/dev/null; then
|
||||
echo "Existing overlay found, accumulating on top"
|
||||
return
|
||||
fi
|
||||
|
||||
local base_pull
|
||||
base_pull=$(resolve_cluster_pull_ref "$oci_url")
|
||||
if [ -z "$base_pull" ]; then
|
||||
echo "ERROR: cluster OCIRepository has no tag or digest ref" >&2
|
||||
rm -rf "$workdir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "No overlay yet, pulling base from cluster: $base_pull"
|
||||
flux pull artifact "$base_pull" --output "$workdir"
|
||||
}
|
||||
|
||||
apply_branch_changes() {
|
||||
local workdir="$1"
|
||||
local f rel
|
||||
|
||||
# Copy changed/added/modified files from working tree
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
rel="${f#packages/}"
|
||||
mkdir -p "$workdir/$(dirname "$rel")"
|
||||
cp -r "$REPO_ROOT/$f" "$workdir/$rel"
|
||||
done < <(git -C "$REPO_ROOT" diff --name-only --diff-filter=ACMR \
|
||||
"${DEV_BASE_TAG}" -- packages/)
|
||||
|
||||
# Copy untracked (new) files not yet known to git
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
rel="${f#packages/}"
|
||||
mkdir -p "$workdir/$(dirname "$rel")"
|
||||
cp -r "$REPO_ROOT/$f" "$workdir/$rel"
|
||||
done < <(git -C "$REPO_ROOT" ls-files --others --exclude-standard -- packages/)
|
||||
|
||||
# Delete files removed relative to DEV_BASE_TAG
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
rel="${f#packages/}"
|
||||
rm -rf "$workdir/$rel"
|
||||
done < <(git -C "$REPO_ROOT" diff --name-only --diff-filter=D \
|
||||
"${DEV_BASE_TAG}" -- packages/)
|
||||
|
||||
# Delete old paths of renamed files to prevent duplicate Helm resources
|
||||
while IFS=$'\t' read -r _ old_path _; do
|
||||
[ -z "$old_path" ] && continue
|
||||
rel="${old_path#packages/}"
|
||||
rm -rf "$workdir/$rel"
|
||||
done < <(git -C "$REPO_ROOT" diff --diff-filter=R --name-status \
|
||||
"${DEV_BASE_TAG}" -- packages/)
|
||||
}
|
||||
|
||||
push_artifact() {
|
||||
local workdir="$1"
|
||||
local logfile
|
||||
logfile=$(mktemp)
|
||||
trap 'rm -f "$logfile"' RETURN
|
||||
|
||||
flux push artifact \
|
||||
"oci://${REGISTRY}/cozystack-packages:${DEV_OVERLAY_TAG}" \
|
||||
--path="$workdir" \
|
||||
--source=dev-overlay \
|
||||
--revision="dev:$(git -C "$REPO_ROOT" rev-parse HEAD)" \
|
||||
2>&1 | tee "$logfile" >&2
|
||||
|
||||
local digest
|
||||
digest=$(awk -F @ '/artifact successfully pushed/ {print $2}' "$logfile")
|
||||
if [ -z "$digest" ]; then
|
||||
echo "ERROR: could not parse digest from push output" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$digest"
|
||||
}
|
||||
|
||||
patch_operator_ref() {
|
||||
local ref="$1"
|
||||
local url="$2"
|
||||
|
||||
local jq_filter
|
||||
jq_filter=$(cat <<'JQ'
|
||||
.spec.template.spec.containers |= map(
|
||||
if .name == $container then
|
||||
.args |= map(
|
||||
if startswith("--platform-source-url=") then "--platform-source-url=" + $url
|
||||
elif startswith("--platform-source-ref=") then "--platform-source-ref=" + $ref
|
||||
else . end)
|
||||
else . end)
|
||||
JQ
|
||||
)
|
||||
|
||||
kubectl get deploy "$OPERATOR_DEPLOY" -n cozy-system -o json \
|
||||
| jq --arg ref "$ref" \
|
||||
--arg url "$url" \
|
||||
--arg container "$OPERATOR_CONTAINER" \
|
||||
"$jq_filter" \
|
||||
| kubectl apply -f -
|
||||
}
|
||||
|
||||
cmd_diff() {
|
||||
local oci_url base_pull
|
||||
oci_url=$(cluster_oci_url)
|
||||
base_pull=$(resolve_cluster_pull_ref "$oci_url")
|
||||
: "${base_pull:=${oci_url}:${DEV_BASE_TAG}}"
|
||||
|
||||
echo "Base: ${DEV_BASE_TAG} Branch: $(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD)"
|
||||
echo "Pull from: $base_pull"
|
||||
echo "Push to: oci://${REGISTRY}/cozystack-packages:${DEV_OVERLAY_TAG}"
|
||||
|
||||
local probe
|
||||
probe=$(mktemp -d)
|
||||
if flux pull artifact "oci://${REGISTRY}/cozystack-packages:${DEV_OVERLAY_TAG}" \
|
||||
--output "$probe" 2>/dev/null; then
|
||||
rm -rf "$probe"
|
||||
echo "Existing overlay found, changes will be layered on top"
|
||||
else
|
||||
rm -rf "$probe"
|
||||
echo "No overlay exists yet, $base_pull will be used as starting point"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Changed files (git diff) ==="
|
||||
git -C "$REPO_ROOT" diff --stat "${DEV_BASE_TAG}" -- packages/
|
||||
|
||||
local untracked
|
||||
untracked=$(git -C "$REPO_ROOT" ls-files --others --exclude-standard -- packages/)
|
||||
if [ -n "$untracked" ]; then
|
||||
echo ""
|
||||
echo "=== Untracked new files ==="
|
||||
echo "$untracked"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
git -C "$REPO_ROOT" diff "${DEV_BASE_TAG}" -- packages/
|
||||
}
|
||||
|
||||
cmd_apply() {
|
||||
local oci_url
|
||||
oci_url=$(cluster_oci_url)
|
||||
|
||||
local workdir
|
||||
workdir=$(mktemp -d)
|
||||
trap "rm -rf '$workdir'" EXIT
|
||||
|
||||
pull_or_init_workdir "$workdir" "$oci_url"
|
||||
apply_branch_changes "$workdir"
|
||||
|
||||
local digest
|
||||
digest=$(push_artifact "$workdir")
|
||||
|
||||
echo "Updating Deployment ${OPERATOR_DEPLOY} url=oci://${REGISTRY}/cozystack-packages ref=digest=${digest}"
|
||||
patch_operator_ref "digest=${digest}" "oci://${REGISTRY}/cozystack-packages"
|
||||
|
||||
echo "Operator restarting — watch: kubectl rollout status deploy/${OPERATOR_DEPLOY} -n cozy-system"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
diff) cmd_diff ;;
|
||||
apply) cmd_apply ;;
|
||||
*)
|
||||
echo "Usage: $0 <diff|apply>" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
@ -50,3 +50,26 @@ chart:
|
|||
PKG=$$(helm package . --version $(COZYSTACK_VERSION) | awk '{print $$NF}'); \
|
||||
trap 'rm -f "$$PKG"' EXIT; \
|
||||
if [ "$(PUSH)" = "1" ]; then helm push "$$PKG" oci://$(REGISTRY); fi
|
||||
|
||||
# === Dev overlay targets ===
|
||||
# See hack/dev-overlay.sh for full documentation and implementation.
|
||||
|
||||
DEV_BASE_TAG ?= origin/main
|
||||
DEV_OVERLAY_TAG ?= dev-overlay
|
||||
OPERATOR_DEPLOY ?= cozystack-operator
|
||||
OPERATOR_CONTAINER ?= $(OPERATOR_DEPLOY)
|
||||
PLATFORM_OCIREPO ?= cozystack-platform
|
||||
|
||||
DEV_OVERLAY_ENV = \
|
||||
REGISTRY="$(REGISTRY)" \
|
||||
DEV_BASE_TAG="$(DEV_BASE_TAG)" \
|
||||
DEV_OVERLAY_TAG="$(DEV_OVERLAY_TAG)" \
|
||||
OPERATOR_DEPLOY="$(OPERATOR_DEPLOY)" \
|
||||
OPERATOR_CONTAINER="$(OPERATOR_CONTAINER)" \
|
||||
PLATFORM_OCIREPO="$(PLATFORM_OCIREPO)"
|
||||
|
||||
dev-overlay-diff: ## Show what files would be added/removed by dev-overlay (dry run, no changes)
|
||||
@$(DEV_OVERLAY_ENV) ../../../hack/dev-overlay.sh diff
|
||||
|
||||
dev-overlay: ## Overlay current branch's changes onto deployed artifact, update operator ref
|
||||
@$(DEV_OVERLAY_ENV) ../../../hack/dev-overlay.sh apply
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue