Makefile
Rama main · réplica de lectura del repositorio original.
PROJECTDIR := $(shell pwd)
# Discover Helm charts that have a corresponding Replicated HelmChart manifest.
# Charts without a manifest (e.g. subcharts only used as dependencies) are
# excluded from the Replicated release to avoid lint errors.
CHARTDIR := $(PROJECTDIR)/charts
MANIFESTDIR := $(PROJECTDIR)/replicated
CHARTS := $(shell for f in $(MANIFESTDIR)/*.yaml $(MANIFESTDIR)/*.yml; do \
name=$$(yq '.spec.chart.name // ""' "$$f" 2>/dev/null); \
[ -n "$$name" ] && [ -d $(CHARTDIR)/$$name ] && echo $$name; \
done | sort -u)
# Discover all Replicated manifest files
MANIFESTS := $(shell find $(MANIFESTDIR) -name '*.yaml' -o -name '*.yml')
# All Chart.yaml files — used as dependencies so manifest builds re-run when any chart version changes
CHART_YAMLS := $(shell find $(CHARTDIR) -name 'Chart.yaml')
# Release metadata: version comes from the openhands chart, channel from the current git branch
VERSION ?= $(shell yq .version $(CHARTDIR)/openhands/Chart.yaml)
REPLICATED_APP ?= openhands
BRANCH := $(shell git branch --show-current)
CHANNEL := $(BRANCH)
ifeq ($(CHANNEL), main)
CHANNEL=Unstable
endif
# Guard: releasing from the main branch (which maps to the Unstable channel)
# or to the Unstable channel directly is reserved for CI. Require an explicit
# opt-in flag so a human can't accidentally publish local work to Unstable.
ALLOW_MAIN_RELEASE ?=
BUILDDIR := $(PROJECTDIR)/build
RELEASE_FILES :=
RELEASE_CHART_PACKAGES = $(filter %.tgz,$(RELEASE_FILES))
# ── Manifest targets ────────────────────────────────────────────────
# For each replicated manifest, generate a build rule that:
# 1. Copies the source YAML into build/
# 2. If the manifest references a chart (spec.chart.name), injects the
# matching chartVersion from charts/<name>/Chart.yaml
#
# This means replicated manifests don't need to hardcode chart versions —
# they're always pulled from the chart source of truth at build time.
#
# Note on escaping: this macro is expanded via $(eval $(call ...)), which
# double-expands variables. Shell variable references need $$$$ to survive
# both passes and arrive as $VAR in the shell. Make variables use $$ to
# defer expansion to recipe execution time.
define make-manifest-target
$(BUILDDIR)/$(notdir $1): $1 $(CHART_YAMLS) | $$(BUILDDIR)
cp $1 $$(BUILDDIR)/$$(notdir $1)
@CHART_NAME=$$$$(yq '.spec.chart.name // ""' $$(BUILDDIR)/$$(notdir $1)); \
if [ -n "$$$$CHART_NAME" ] && [ -f $(CHARTDIR)/$$$$CHART_NAME/Chart.yaml ]; then \
CHART_VER=$$$$(yq .version $(CHARTDIR)/$$$$CHART_NAME/Chart.yaml); \
yq -i ".spec.chart.chartVersion = \"$$$$CHART_VER\"" $$(BUILDDIR)/$$(notdir $1); \
echo "Updated $$(notdir $1) chartVersion to $$$$CHART_VER"; \
fi
RELEASE_FILES := $(RELEASE_FILES) $(BUILDDIR)/$(notdir $1)
manifests:: $(BUILDDIR)/$(notdir $1)
endef
$(foreach element,$(MANIFESTS),$(eval $(call make-manifest-target,$(element))))
# ── Chart targets ───────────────────────────────────────────────────
# For each Helm chart, package it into a versioned .tgz in build/.
# Dependencies include all yaml/tpl/schema files so changes trigger a rebuild.
define make-chart-target
$(eval VER := $(shell yq .version $(CHARTDIR)/$1/Chart.yaml))
$(BUILDDIR)/$1-$(VER).tgz : $(CHARTDIR)/$1 $(shell find $(CHARTDIR)/$1 -name '*.yaml' -o -name '*.yml' -o -name "*.tpl" -o -name "NOTES.txt" -o -name "values.schema.json") | $$(BUILDDIR)
@# Rewrite any dependency that points to a remote registry but exists as a
@# sibling chart to use a local file:// reference instead. This lets
@# `helm package -u` resolve unpublished chart versions during local builds.
@# The copy lives outside the chart directory. Held inside it, `helm package`
@# picks it up and every released chart ships a Chart.yaml.bak alongside the
@# real one, because the restore only runs after packaging.
@# Chart.lock is restored for the same reason: `helm package -u` regenerates it
@# to match the rewritten repositories, and a lock describing a Chart.yaml that
@# no longer exists fails `helm dependency build` with an out-of-sync error.
@mkdir -p $(BUILDDIR)/.chartbak/$1
@cp $(CHARTDIR)/$1/Chart.yaml $(BUILDDIR)/.chartbak/$1/Chart.yaml
@if [ -f $(CHARTDIR)/$1/Chart.lock ]; then cp $(CHARTDIR)/$1/Chart.lock $(BUILDDIR)/.chartbak/$1/Chart.lock; fi
@trap 'mv $(BUILDDIR)/.chartbak/$1/Chart.yaml $(CHARTDIR)/$1/Chart.yaml; \
if [ -f $(BUILDDIR)/.chartbak/$1/Chart.lock ]; then mv $(BUILDDIR)/.chartbak/$1/Chart.lock $(CHARTDIR)/$1/Chart.lock; fi' EXIT; \
for dep in $$$$(yq -r '.dependencies[].name // ""' $(CHARTDIR)/$1/Chart.yaml); do \
if [ -d $(CHARTDIR)/$$$$dep ]; then \
yq -i "(.dependencies[] | select(.name == \"$$$$dep\")).repository = \"file://../$$$$dep\"" $(CHARTDIR)/$1/Chart.yaml; \
fi; \
done; \
n=0; until helm package -u $(CHARTDIR)/$1 -d $(BUILDDIR)/; do \
n=$$$$((n+1)); \
if [ $$$$n -ge 5 ]; then echo "helm package failed after $$$$n attempts"; exit 1; fi; \
echo "helm package failed (likely transient upstream chart download); retry $$$$n/5 in $$$$((n*15))s"; \
sleep $$$$((n*15)); \
done
RELEASE_FILES := $(RELEASE_FILES) $(BUILDDIR)/$1-$(VER).tgz
charts:: $(BUILDDIR)/$1-$(VER).tgz
endef
$(foreach element,$(CHARTS),$(eval $(call make-chart-target,$(element))))
$(BUILDDIR):
mkdir -p $(BUILDDIR)
# ── Phony targets ───────────────────────────────────────────────────
# Remove generated release output and ignored Helm dependency archives. Runs
# before lint/release so stale subchart archives cannot contaminate packages.
.PHONY: clean
clean:
rm -rf $(BUILDDIR)
find $(CHARTDIR) -path '*/charts/*.tgz' -type f -exec rm -f {} +
# Fail if any packaged chart contains duplicate archive paths. Duplicate paths
# let stale dependency contents win during extraction even when sources are new.
.PHONY: check-duplicate-chart-entries
check-duplicate-chart-entries: $(RELEASE_CHART_PACKAGES)
@set -eu; \
for chart in $(RELEASE_CHART_PACKAGES); do \
listing=$$(tar tzf "$$chart") || { echo "ERROR: failed to list $$chart"; exit 1; }; \
duplicates=$$(printf '%s\n' "$$listing" | sort | uniq -d); \
if [ -n "$$duplicates" ]; then \
echo "ERROR: $$chart contains duplicate archive paths:"; \
echo "$$duplicates"; \
exit 1; \
fi; \
done
# Validate all built manifests and charts with the Replicated linter
.PHONY: lint
lint: clean $(RELEASE_FILES) check-duplicate-chart-entries
replicated release lint --app $(REPLICATED_APP) --yaml-dir $(BUILDDIR)
# Refuse to release from main or to the Unstable channel unless explicitly
# allowed via ALLOW_MAIN_RELEASE=1. CI passes this flag; humans should not.
.PHONY: check-release-guard
check-release-guard:
@if [ -z "$(ALLOW_MAIN_RELEASE)" ] && { [ "$(BRANCH)" = "main" ] || [ "$(CHANNEL)" = "Unstable" ]; }; then \
echo "ERROR: refusing to release from 'main' or to the 'Unstable' channel (branch=$(BRANCH) channel=$(CHANNEL))."; \
echo " This is reserved for CI. If you really mean to do this, re-run with ALLOW_MAIN_RELEASE=1."; \
exit 1; \
fi
# Echo the channel `release` publishes to, so CI can pin a deploy to it without
# re-deriving the branch mapping.
.PHONY: print-channel
print-channel:
@echo $(CHANNEL)
# Build everything, lint, then publish a release to the Replicated channel
.PHONY: release
release: check-release-guard clean $(RELEASE_FILES) check-duplicate-chart-entries lint
replicated release create \
--app $(REPLICATED_APP) \
--version $(VERSION) \
--yaml-dir $(BUILDDIR) \
--ensure-channel \
--promote $(CHANNEL)