mirror of
https://github.com/hanzoai/ci.git
synced 2026-10-20 15:28:05 +00:00
One build can publish a site per brand
A white-labelled product is one codebase and several hosts, and a site keys on its HOST — so a brand per host is a site per host. The lane could publish one, which left every such product serving its brands from containers: an image to build, a registry to push, a Deployment to reconcile and replicas to schedule, so a directory of files could be read over HTTP. The brand arrived at runtime, baked into the image for one and mounted over /public from a ConfigMap for the next, and both need a pod to hold them. `sites:` is a list of slug and dir; `site:` still means one, and every caller using it is untouched. Both resolve to the same slug=dir lines before anything acts on them, so there is one path through the rest. bin/sitedeploy and bin/sitepublish stay single-site. A publish IS one site; WHICH sites to publish is orchestration, and orchestration is what the action and this workflow are. Neither gets a second copy of what a publish means. The branch gate is the list's, so every brand of one product goes live together. A rollout that reached one brand and not another is the drift this lane exists to remove. The pair list is read from a file rather than a pipe. The loop body runs curl, curl inherits stdin, and a piped list would be swallowed after the first entry — publishing one site and reporting success for all of them. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -29,10 +29,21 @@ description: Publish a built static export to the Hanzo PaaS Sites plane.
|
||||
inputs:
|
||||
slug:
|
||||
description: The project slug on the Sites plane (POST /v1/projects/<slug>/deployments)
|
||||
required: true
|
||||
required: false
|
||||
default: ''
|
||||
dir:
|
||||
description: The built export directory
|
||||
required: true
|
||||
required: false
|
||||
default: ''
|
||||
sites:
|
||||
description: >-
|
||||
Several sites from one build, one `slug=dir` pair per line. A white-labelled
|
||||
product is one codebase and several hosts, and a site keys on its HOST — so
|
||||
a brand per host is a site per host, and one build writes an export per
|
||||
brand. bin/sitedeploy stays single-site: a publish IS one site, and which
|
||||
sites to publish is orchestration, which is what this file is.
|
||||
required: false
|
||||
default:
|
||||
api:
|
||||
description: Cloud API base
|
||||
required: false
|
||||
@@ -101,4 +112,23 @@ runs:
|
||||
SITEDEPLOY_BRANCH: ${{ github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bash "$RUNNER_TEMP/sitedeploy-ci/bin/sitedeploy" '${{ inputs.slug }}' '${{ inputs.dir }}'
|
||||
pairs='${{ inputs.sites }}'
|
||||
[ -n "${pairs//[[:space:]]/}" ] || pairs='${{ inputs.slug }}=${{ inputs.dir }}'
|
||||
if [ "${pairs//[[:space:]]/}" = "=" ]; then
|
||||
echo "::error::nothing to publish: pass slug: and dir:, or sites: with one slug=dir per line."
|
||||
exit 1
|
||||
fi
|
||||
# A file, not a pipe. The loop body runs curl, curl inherits stdin, and
|
||||
# it would swallow the rest of the list — publishing the first site and
|
||||
# reporting success for all of them.
|
||||
printf '%s\n' "$pairs" > "$RUNNER_TEMP/site-pairs"
|
||||
n=0
|
||||
while IFS= read -r pair; do
|
||||
[ -n "${pair//[[:space:]]/}" ] || continue
|
||||
case "$pair" in *=*) ;; *) echo "::error::sites: entry '$pair' is not slug=dir"; exit 1 ;; esac
|
||||
echo "::group::publish ${pair%%=*}"
|
||||
bash "$RUNNER_TEMP/sitedeploy-ci/bin/sitedeploy" "${pair%%=*}" "${pair#*=}"
|
||||
echo "::endgroup::"
|
||||
n=$((n + 1))
|
||||
done < "$RUNNER_TEMP/site-pairs"
|
||||
[ "$n" -gt 0 ] || { echo "::error::sites: named no site"; exit 1; }
|
||||
|
||||
+42
-11
@@ -1888,24 +1888,46 @@ jobs:
|
||||
REF="${{ github.ref_name }}"
|
||||
# A caller with no `site:` ships no static export, and reading
|
||||
# .site.slug off null would abort the step instead of skipping it.
|
||||
if [ "$(yq -r '.site | type' hanzo.yml 2>/dev/null || echo '!!null')" != '!!map' ]; then
|
||||
echo "::notice::no site: in hanzo.yml — nothing to publish"; exit 0
|
||||
#
|
||||
# `site:` is one, `sites:` is several. A white-labelled product builds
|
||||
# once and ships an export per brand, and a site keys on its HOST, so a
|
||||
# brand per host is a site per host. Both spellings land on the same
|
||||
# slug=dir lines, so everything after this is one path.
|
||||
SITE_T=$(yq -r '.site | type' hanzo.yml 2>/dev/null || echo '!!null')
|
||||
SITES_T=$(yq -r '.sites | type' hanzo.yml 2>/dev/null || echo '!!null')
|
||||
if [ "$SITE_T" != '!!map' ] && [ "$SITES_T" != '!!seq' ]; then
|
||||
echo "::notice::no site:/sites: in hanzo.yml — nothing to publish"; exit 0
|
||||
fi
|
||||
if [ "$SITE_T" = '!!map' ] && [ "$SITES_T" = '!!seq' ]; then
|
||||
echo "::error::hanzo.yml declares BOTH site: and sites:. One list, one spelling."; exit 1
|
||||
fi
|
||||
# Same branch gate as deploy.on, including the tag case: a tagged release
|
||||
# publishes, a branch publishes only if it is named.
|
||||
ON=$(yq -o=json -I=0 '.site.on // []' hanzo.yml)
|
||||
# publishes, a branch publishes only if it is named. The gate is the
|
||||
# LIST's, so every brand of one product goes live together — a rollout
|
||||
# that reached one brand and not another is the drift this lane removes.
|
||||
if [ "$SITES_T" = '!!seq' ]; then
|
||||
ON=$(yq -o=json -I=0 '.sites[0].on // []' hanzo.yml)
|
||||
else
|
||||
ON=$(yq -o=json -I=0 '.site.on // []' hanzo.yml)
|
||||
fi
|
||||
IS_TAG=$([ "${{ github.ref_type }}" = "tag" ] && echo 1 || echo 0)
|
||||
if [ "$IS_TAG" != 1 ] && ! echo "$ON" | jq -e --arg b "$REF" 'index($b)' >/dev/null; then
|
||||
echo "branch $REF not in site.on — skipping publish"; exit 0
|
||||
fi
|
||||
SLUG=$(yq -r '.site.slug // ""' hanzo.yml)
|
||||
DIR=$(yq -r '.site.dir // ""' hanzo.yml)
|
||||
BUILD=$(yq -r '.site.build // ""' hanzo.yml)
|
||||
if [ -z "$SLUG" ] || [ -z "$DIR" ]; then
|
||||
echo "::error::site: needs both slug: (the /v1/sites project) and dir: (the built export)"; exit 1
|
||||
if [ "$SITES_T" = '!!seq' ]; then
|
||||
PAIRS=$(yq -r '.sites[] | "\(.slug)=\(.dir)"' hanzo.yml)
|
||||
BUILD=$(yq -r '.["site-build"] // ""' hanzo.yml)
|
||||
else
|
||||
SLUG=$(yq -r '.site.slug // ""' hanzo.yml)
|
||||
DIR=$(yq -r '.site.dir // ""' hanzo.yml)
|
||||
BUILD=$(yq -r '.site.build // ""' hanzo.yml)
|
||||
PAIRS="$SLUG=$DIR"
|
||||
fi
|
||||
if printf '%s\n' "$PAIRS" | grep -qE '^=|=$'; then
|
||||
echo "::error::every site needs both slug: (the /v1/sites project) and dir: (the built export)"; exit 1
|
||||
fi
|
||||
if [ -n "$BUILD" ]; then
|
||||
echo "::group::build $SLUG ($BUILD)"
|
||||
echo "::group::build ($BUILD)"
|
||||
sh -c "$BUILD"
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
@@ -1919,6 +1941,15 @@ jobs:
|
||||
# Slug grammar, index.html, the server's file/byte caps and the edge
|
||||
# BodyLimit are all checked inside sitepublish, before anything is sent,
|
||||
# so each refusal names its own number instead of arriving as a 4xx.
|
||||
HANZO_API="$API" "$CI_HOME/bin/sitepublish" "$SLUG" "$DIR"
|
||||
# One publish per site, from the one implementation. A file, not a
|
||||
# pipe: sitepublish runs curl, and curl inherits stdin — it would eat
|
||||
# the rest of the list and report success for sites it never sent.
|
||||
printf '%s\n' "$PAIRS" > "$RUNNER_TEMP/site-pairs"
|
||||
while IFS= read -r pair; do
|
||||
[ -n "${pair//[[:space:]]/}" ] || continue
|
||||
echo "::group::publish ${pair%%=*}"
|
||||
HANZO_API="$API" "$CI_HOME/bin/sitepublish" "${pair%%=*}" "${pair#*=}"
|
||||
echo "::endgroup::"
|
||||
done < "$RUNNER_TEMP/site-pairs"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user