#!/usr/bin/env bash set -euo pipefail export PATH="$PATH:/opt/homebrew/bin:/usr/local/bin:/etc/profiles/per-user/$USER/bin" cd "$(dirname "${BASH_SOURCE[0]}")/.." TAG_PREFIX="builds/" DEFAULT_VERSION_REMOTE="origin" if git remote get-url forgejo >/dev/null 2>&1; then DEFAULT_VERSION_REMOTE="forgejo" fi VERSION_REMOTE="${BURROW_VERSION_REMOTE:-$DEFAULT_VERSION_REMOTE}" VERSION_REQUIRE_REMOTE="${BURROW_VERSION_REQUIRE_REMOTE:-false}" COMMAND="${1:-}" truthy() { case "$1" in 1|true|TRUE|True|yes|YES|Yes|y|Y|on|ON|On) return 0 ;; *) return 1 ;; esac } list_local_build_tags() { git tag -l "${TAG_PREFIX}[0-9]*" } latest_local_build_tag() { list_local_build_tags | sort -n -t'/' -k2 | tail -n 1 } latest_remote_build_record() { local raw rc=0 raw="$(git ls-remote --tags --refs "$VERSION_REMOTE" "refs/tags/${TAG_PREFIX}[0-9]*" 2>/dev/null)" || rc=$? if [[ $rc -ne 0 ]]; then return $rc fi if [[ -z "$raw" ]]; then return 0 fi printf '%s\n' "$raw" | awk -F'\t' -v prefix="refs/tags/${TAG_PREFIX}" ' $2 ~ ("^" prefix "[0-9]+$") { build = $2 sub("^refs/tags/", "", build) print build "\t" $1 } ' | sort -n -t'/' -k2 | tail -n 1 } resolve_build_state() { local remote_record local_latest remote_status=0 BUILD_STATE_SOURCE="local" LATEST_BUILD="" LATEST_BUILD_COMMIT="" remote_record="$(latest_remote_build_record)" || remote_status=$? if [[ $remote_status -eq 0 ]]; then BUILD_STATE_SOURCE="remote" if [[ -n "$remote_record" ]]; then LATEST_BUILD="$(printf '%s\n' "$remote_record" | cut -f1)" LATEST_BUILD_COMMIT="$(printf '%s\n' "$remote_record" | cut -f2)" fi else if truthy "$VERSION_REQUIRE_REMOTE"; then echo "error: unable to resolve authoritative build tags from ${VERSION_REMOTE}" >&2 exit "$remote_status" fi echo "warning: unable to resolve authoritative build tags from ${VERSION_REMOTE}; falling back to local tags" >&2 local_latest="$(latest_local_build_tag)" if [[ -n "$local_latest" ]]; then LATEST_BUILD="$local_latest" LATEST_BUILD_COMMIT="$(git rev-parse -q --verify "refs/tags/${LATEST_BUILD}^{commit}" 2>/dev/null || true)" fi fi if [[ -z "$LATEST_BUILD" ]]; then LATEST_BUILD_NUMBER="0" else LATEST_BUILD_NUMBER="${LATEST_BUILD#$TAG_PREFIX}" fi } resolve_build_state HEAD_COMMIT="$(git rev-parse HEAD)" ANCESTRY_STATE="ok" if [[ -n "${LATEST_BUILD_NUMBER}" && "${LATEST_BUILD_NUMBER}" != "0" ]]; then if [[ -n "${LATEST_BUILD_COMMIT}" ]] && git cat-file -e "${LATEST_BUILD_COMMIT}^{commit}" 2>/dev/null; then if ! git merge-base --is-ancestor "${LATEST_BUILD_COMMIT}" HEAD; then ANCESTRY_STATE="not-descended" if [[ "$COMMAND" != "status" ]]; then echo "error: HEAD is not descended from build ${LATEST_BUILD_NUMBER}" >&2 exit 1 fi fi else ANCESTRY_STATE="missing-tag-commit" echo "notice: skipping build ancestry check (missing ${LATEST_BUILD} commit, likely shallow checkout)" >&2 fi fi BUILD_NUMBER="$LATEST_BUILD_NUMBER" if [[ "$COMMAND" == "increment" || "$COMMAND" == "pre-increment" ]]; then if [[ -n "${BURROW_BUILD_NUMBER_OVERRIDE:-}" ]]; then if [[ ! "${BURROW_BUILD_NUMBER_OVERRIDE}" =~ ^[0-9]+$ ]]; then echo "error: BURROW_BUILD_NUMBER_OVERRIDE must be an integer" >&2 exit 1 fi if (( BURROW_BUILD_NUMBER_OVERRIDE <= LATEST_BUILD_NUMBER )); then echo "error: BURROW_BUILD_NUMBER_OVERRIDE must be greater than ${LATEST_BUILD_NUMBER}" >&2 exit 1 fi NEW_BUILD_NUMBER="${BURROW_BUILD_NUMBER_OVERRIDE}" else NEW_BUILD_NUMBER=$((LATEST_BUILD_NUMBER + 1)) fi NEW_TAG="$TAG_PREFIX$NEW_BUILD_NUMBER" BUILD_NUMBER="$NEW_BUILD_NUMBER" fi if [[ "$COMMAND" == "increment" ]]; then git tag "$NEW_TAG" git push --quiet "$VERSION_REMOTE" "$NEW_TAG" fi CONFIG_PATH="Apple/Configuration/Version.xcconfig" if [[ "$COMMAND" != "status" && -f "$CONFIG_PATH" ]]; then current_config_version="$(sed -n 's/^[[:space:]]*CURRENT_PROJECT_VERSION[[:space:]]*=[[:space:]]*//p' "$CONFIG_PATH" 2>/dev/null | tail -n 1 || true)" if [[ "$current_config_version" != "$BUILD_NUMBER" ]]; then echo "CURRENT_PROJECT_VERSION = $BUILD_NUMBER" > "$CONFIG_PATH" fi git update-index --assume-unchanged "$CONFIG_PATH" 2>/dev/null || true fi if [[ "$COMMAND" == "status" ]]; then if [[ "${ANCESTRY_STATE}" == "ok" && "${LATEST_BUILD_NUMBER}" != "0" && -n "${LATEST_BUILD_COMMIT}" && "${HEAD_COMMIT}" == "${LATEST_BUILD_COMMIT}" ]]; then echo "clean" else echo "dirty" fi exit 0 fi echo "$BUILD_NUMBER"