# Set SHELL to 'strict mode' without using .SHELLFLAGS for max compatibility.
# See https://fieldnotes.tech/how-to-shell-for-compatible-makefiles/
SHELL := /usr/bin/env bash -euo pipefail -c

# CONFIG is the name of the make target someone
# would invoke to update the main config file (config.yml).
CONFIG ?= ci-config
# VERIFY is the name of the make target someone
# would invoke to verify the config file.
VERIFY ?= ci-verify

CIRCLECI := circleci --skip-update-check
ifeq ($(DEBUG_CIRCLECI_CLI),YES)
CIRCLECI += --debug
endif

# For config processing, always refer to circleci.com not self-hosted circleci,
# because self-hosted does not currently support the necessary API.
CIRCLECI_CLI_HOST := https://circleci.com
export CIRCLECI_CLI_HOST

# Set up some documentation/help message variables.
# We do not attempt to install the CircleCI CLI from this Makefile.
CCI_INSTALL_LINK := https://circleci.com/docs/2.0/local-cli/\#installation
CCI_INSTALL_MSG := Please install CircleCI CLI. See $(CCI_INSTALL_LINK)
CCI_VERSION := $(shell $(CIRCLECI) version 2> /dev/null)
ifeq ($(CCI_VERSION),)
# Attempting to use the CLI fails with installation instructions.
CIRCLECI := echo '$(CCI_INSTALL_MSG)'; exit 1; \#
endif

SOURCE_DIR     := config
SOURCE_YML     := $(shell [ ! -d $(SOURCE_DIR) ] || find $(SOURCE_DIR) -name '*.yml')
CONFIG_SOURCE  := Makefile $(SOURCE_YML) | $(SOURCE_DIR)
OUT            := config.yml
TMP            := .tmp/config-processed
CONFIG_PACKED  := .tmp/config-packed

default: help

help:
	@echo "Usage:"
	@echo "  make $(CONFIG): recompile config.yml from $(SOURCE_DIR)/"
	@echo "  make $(VERIFY): verify that config.yml is a true mapping from $(SOURCE_DIR)/"
	@echo
	@echo "Diagnostics:"
	@[ -z "$(CCI_VERSION)" ] || echo "  circleci-cli version $(CCI_VERSION)"
	@[ -n "$(CCI_VERSION)" ] || echo "  $(CCI_INSTALL_MSG)"

$(SOURCE_DIR):
	@echo No source directory $(SOURCE_DIR) found.; exit 1

# Make sure our .tmp dir exists.
$(shell [ -d .tmp ] || mkdir .tmp)

.PHONY: $(CONFIG)
$(CONFIG): $(OUT)

.PHONY: $(VERIFY)
$(VERIFY): config-up-to-date
	@$(CIRCLECI) config validate $(OUT)

define GENERATED_FILE_HEADER
### ***
### WARNING: DO NOT manually EDIT or MERGE this file, it is generated by 'make $(CONFIG)'.
### INSTEAD: Edit or merge the source in $(SOURCE_DIR)/ then run 'make $(CONFIG)'.
### ***
endef
export GENERATED_FILE_HEADER

# GEN_CONFIG writes the config to a temporary file. If the whole process succeeds,
# it them moves that file to $@. This makes is an atomic operation, so if it fails
# make doesn't consider a half-baked file up to date.
define GEN_CONFIG
	@$(CIRCLECI) config pack $(SOURCE_DIR) > $(CONFIG_PACKED)
	@echo "$$GENERATED_FILE_HEADER" > $@.tmp || { rm -f $@; exit 1; }
	@$(CIRCLECI) config process $(CONFIG_PACKED) >> $@.tmp || { rm -f $@.tmp; exit 1; }
	@mv -f $@.tmp $@
endef

$(OUT): $(CONFIG_SOURCE) 
	$(GEN_CONFIG)
	@echo "$@ updated"

$(TMP): $(CONFIG_SOURCE) 
	$(GEN_CONFIG)

.PHONY: config-up-to-date
config-up-to-date: $(TMP) # Note this must not depend on $(OUT)!
	@if diff -w $(OUT) $<; then \
		echo "Generated $(OUT) is up to date!"; \
	else \
		echo "Generated $(OUT) is out of date, run make $(CONFIG) to update."; \
		exit 1; \
	fi
