fix(release): verify frontend release via homepage metadata
This commit is contained in:
parent
a0e6da97b1
commit
cf1ce8a4db
14
Dockerfile
14
Dockerfile
@ -21,6 +21,10 @@ ARG NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO=
|
|||||||
ARG NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=
|
ARG NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=
|
||||||
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=
|
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=
|
||||||
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=
|
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_IMAGE=
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_TAG=
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_COMMIT=
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_VERSION=
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# Stage 1 — Builder (Turbopack + standalone)
|
# Stage 1 — Builder (Turbopack + standalone)
|
||||||
@ -46,6 +50,10 @@ ARG NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO
|
|||||||
ARG NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION
|
ARG NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION
|
||||||
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO
|
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO
|
||||||
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION
|
ARG NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_IMAGE
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_TAG
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_COMMIT
|
||||||
|
ARG NEXT_PUBLIC_RELEASE_VERSION
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1 \
|
ENV NEXT_TELEMETRY_DISABLED=1 \
|
||||||
NEXT_PRIVATE_TURBOPACK=1 \
|
NEXT_PRIVATE_TURBOPACK=1 \
|
||||||
@ -65,7 +73,11 @@ ENV NEXT_TELEMETRY_DISABLED=1 \
|
|||||||
NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO=${NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO} \
|
NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO=${NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_PAYGO} \
|
||||||
NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=${NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION} \
|
NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=${NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION} \
|
||||||
NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO} \
|
NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO} \
|
||||||
NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION}
|
NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION} \
|
||||||
|
NEXT_PUBLIC_RELEASE_IMAGE=${NEXT_PUBLIC_RELEASE_IMAGE} \
|
||||||
|
NEXT_PUBLIC_RELEASE_TAG=${NEXT_PUBLIC_RELEASE_TAG} \
|
||||||
|
NEXT_PUBLIC_RELEASE_COMMIT=${NEXT_PUBLIC_RELEASE_COMMIT} \
|
||||||
|
NEXT_PUBLIC_RELEASE_VERSION=${NEXT_PUBLIC_RELEASE_VERSION}
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# 基础镜像升级到最新
|
# 基础镜像升级到最新
|
||||||
|
|||||||
@ -16,6 +16,22 @@ emit_lines() {
|
|||||||
require_env CANONICAL_DOMAIN
|
require_env CANONICAL_DOMAIN
|
||||||
|
|
||||||
local canonical_domain="${CANONICAL_DOMAIN}"
|
local canonical_domain="${CANONICAL_DOMAIN}"
|
||||||
|
local release_image_ref="${IMAGE_REF-}"
|
||||||
|
local release_image_tag=""
|
||||||
|
local release_commit=""
|
||||||
|
local release_version=""
|
||||||
|
|
||||||
|
if [[ -n "${release_image_ref}" ]]; then
|
||||||
|
release_image_tag="$(printf '%s' "${release_image_ref}" | sed -E 's#^.*:([^:@]+)$#\1#')"
|
||||||
|
release_version="${release_image_tag}"
|
||||||
|
|
||||||
|
if [[ "${release_image_tag}" =~ ^sha-([0-9a-f]{7,40})$ ]]; then
|
||||||
|
release_commit="${BASH_REMATCH[1]}"
|
||||||
|
elif [[ "${release_image_tag}" =~ ^[0-9a-f]{7,40}$ ]]; then
|
||||||
|
release_commit="${release_image_tag}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
printf 'NODE_BUILDER_IMAGE=%s\n' "${NODE_BUILDER_IMAGE:-node:22-bookworm}"
|
printf 'NODE_BUILDER_IMAGE=%s\n' "${NODE_BUILDER_IMAGE:-node:22-bookworm}"
|
||||||
printf 'NODE_RUNTIME_IMAGE=%s\n' "${NODE_RUNTIME_IMAGE:-node:22-slim}"
|
printf 'NODE_RUNTIME_IMAGE=%s\n' "${NODE_RUNTIME_IMAGE:-node:22-slim}"
|
||||||
printf 'CONTENTLAYER_BUILD=%s\n' "${CONTENTLAYER_BUILD:-true}"
|
printf 'CONTENTLAYER_BUILD=%s\n' "${CONTENTLAYER_BUILD:-true}"
|
||||||
@ -36,6 +52,10 @@ emit_lines() {
|
|||||||
printf 'NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=%s\n' "${NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION-}"
|
printf 'NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION=%s\n' "${NEXT_PUBLIC_STRIPE_PRICE_XSCOPEHUB_SUBSCRIPTION-}"
|
||||||
printf 'NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=%s\n' "${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO-}"
|
printf 'NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO=%s\n' "${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_PAYGO-}"
|
||||||
printf 'NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=%s\n' "${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION-}"
|
printf 'NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION=%s\n' "${NEXT_PUBLIC_STRIPE_PRICE_XCLOUDFLOW_SUBSCRIPTION-}"
|
||||||
|
printf 'NEXT_PUBLIC_RELEASE_IMAGE=%s\n' "${release_image_ref}"
|
||||||
|
printf 'NEXT_PUBLIC_RELEASE_TAG=%s\n' "${release_image_tag}"
|
||||||
|
printf 'NEXT_PUBLIC_RELEASE_COMMIT=%s\n' "${release_commit}"
|
||||||
|
printf 'NEXT_PUBLIC_RELEASE_VERSION=%s\n' "${release_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ "${MODE}" == "--stdout" ]]; then
|
if [[ "${MODE}" == "--stdout" ]]; then
|
||||||
|
|||||||
@ -5,7 +5,6 @@ CANONICAL_DOMAIN="${1:?usage: verify-frontend-release.sh <canonical-domain> <ser
|
|||||||
SERVED_DOMAINS="${2:?usage: verify-frontend-release.sh <canonical-domain> <served-domains> <expected-image-ref> [request-base-url]}"
|
SERVED_DOMAINS="${2:?usage: verify-frontend-release.sh <canonical-domain> <served-domains> <expected-image-ref> [request-base-url]}"
|
||||||
EXPECTED_IMAGE_REF="${3:?usage: verify-frontend-release.sh <canonical-domain> <served-domains> <expected-image-ref> [request-base-url]}"
|
EXPECTED_IMAGE_REF="${3:?usage: verify-frontend-release.sh <canonical-domain> <served-domains> <expected-image-ref> [request-base-url]}"
|
||||||
REQUEST_BASE_URL="${4:-https://${CANONICAL_DOMAIN}}"
|
REQUEST_BASE_URL="${4:-https://${CANONICAL_DOMAIN}}"
|
||||||
EXPECTED_DASHBOARD_URL="https://${CANONICAL_DOMAIN}"
|
|
||||||
|
|
||||||
curl_headers=(
|
curl_headers=(
|
||||||
-H 'user-agent: Mozilla/5.0 (compatible; console-release-validator/1.0; +https://www.svc.plus)'
|
-H 'user-agent: Mozilla/5.0 (compatible; console-release-validator/1.0; +https://www.svc.plus)'
|
||||||
@ -32,6 +31,7 @@ image_ref = os.environ["IMAGE_REF"].strip()
|
|||||||
match = re.search(r":([^:@]+)$", image_ref)
|
match = re.search(r":([^:@]+)$", image_ref)
|
||||||
tag = match.group(1) if match else ""
|
tag = match.group(1) if match else ""
|
||||||
commit = ""
|
commit = ""
|
||||||
|
version = tag
|
||||||
|
|
||||||
if re.fullmatch(r"[0-9a-f]{7,40}", tag, flags=re.IGNORECASE):
|
if re.fullmatch(r"[0-9a-f]{7,40}", tag, flags=re.IGNORECASE):
|
||||||
commit = tag
|
commit = tag
|
||||||
@ -40,26 +40,33 @@ else:
|
|||||||
if prefixed_match:
|
if prefixed_match:
|
||||||
commit = prefixed_match.group(1)
|
commit = prefixed_match.group(1)
|
||||||
|
|
||||||
if not image_ref or not tag or not commit:
|
if not image_ref or not tag or not commit or not version:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print(image_ref)
|
print(image_ref)
|
||||||
print(tag)
|
print(tag)
|
||||||
print(commit)
|
print(commit)
|
||||||
|
print(version)
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_release_metadata() {
|
parse_homepage_release_metadata() {
|
||||||
local payload="$1"
|
local homepage_html="$1"
|
||||||
RELEASE_PAYLOAD="${payload}" python3 - <<'PY'
|
HOMEPAGE_HTML="${homepage_html}" python3 - <<'PY'
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
payload = json.loads(os.environ["RELEASE_PAYLOAD"])
|
html = os.environ["HOMEPAGE_HTML"]
|
||||||
print(payload.get("releaseImageRef", ""))
|
|
||||||
print(payload.get("releaseImageTag", ""))
|
def extract_meta(name: str) -> str:
|
||||||
print(payload.get("releaseCommit", ""))
|
pattern = rf'<meta[^>]+name=["\']{re.escape(name)}["\'][^>]+content=["\']([^"\']*)["\']'
|
||||||
print(payload.get("dashboardUrl", ""))
|
match = re.search(pattern, html, flags=re.IGNORECASE)
|
||||||
|
return match.group(1).strip() if match else ""
|
||||||
|
|
||||||
|
print(extract_meta("svc-plus-release-image"))
|
||||||
|
print(extract_meta("svc-plus-release-tag"))
|
||||||
|
print(extract_meta("svc-plus-release-commit"))
|
||||||
|
print(extract_meta("svc-plus-release-version"))
|
||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,8 +86,8 @@ verify_domain() {
|
|||||||
local domain="$1"
|
local domain="$1"
|
||||||
local request_base_url="${REQUEST_BASE_URL%/}"
|
local request_base_url="${REQUEST_BASE_URL%/}"
|
||||||
local request_headers=("${curl_headers[@]}" -H "host: ${domain}")
|
local request_headers=("${curl_headers[@]}" -H "host: ${domain}")
|
||||||
local homepage_html asset_path release_payload release_metadata
|
local homepage_html asset_path release_metadata
|
||||||
local actual_image_ref actual_image_tag actual_release_commit actual_dashboard_url
|
local actual_image_ref actual_image_tag actual_release_commit actual_release_version
|
||||||
local release_lines
|
local release_lines
|
||||||
|
|
||||||
require_http_200 "${request_base_url}" "${request_headers[@]}"
|
require_http_200 "${request_base_url}" "${request_headers[@]}"
|
||||||
@ -96,49 +103,48 @@ verify_domain() {
|
|||||||
require_http_200 "${request_base_url}${asset_path}" "${request_headers[@]}"
|
require_http_200 "${request_base_url}${asset_path}" "${request_headers[@]}"
|
||||||
printf 'verified static asset for %s: %s%s\n' "${domain}" "${request_base_url}" "${asset_path}" >&2
|
printf 'verified static asset for %s: %s%s\n' "${domain}" "${request_base_url}" "${asset_path}" >&2
|
||||||
|
|
||||||
require_http_200 "${request_base_url}/api/ping" "${request_headers[@]}"
|
release_metadata="$(parse_homepage_release_metadata "${homepage_html}")"
|
||||||
release_payload="$(curl -fsSL "${request_headers[@]}" "${request_base_url}/api/ping")"
|
|
||||||
release_metadata="$(parse_release_metadata "${release_payload}")"
|
|
||||||
|
|
||||||
mapfile -t release_lines <<< "${release_metadata}"
|
mapfile -t release_lines <<< "${release_metadata}"
|
||||||
actual_image_ref="${release_lines[0]-}"
|
actual_image_ref="${release_lines[0]-}"
|
||||||
actual_image_tag="${release_lines[1]-}"
|
actual_image_tag="${release_lines[1]-}"
|
||||||
actual_release_commit="${release_lines[2]-}"
|
actual_release_commit="${release_lines[2]-}"
|
||||||
actual_dashboard_url="${release_lines[3]-}"
|
actual_release_version="${release_lines[3]-}"
|
||||||
|
|
||||||
if [[ -z "${actual_image_ref}" || -z "${actual_image_tag}" || -z "${actual_release_commit}" ]]; then
|
if [[ -z "${actual_image_ref}" || -z "${actual_image_tag}" || -z "${actual_release_commit}" || -z "${actual_release_version}" ]]; then
|
||||||
echo "Remote release metadata is incomplete for ${domain}: ${release_payload}" >&2
|
echo "Homepage release metadata is incomplete for ${domain}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! "${actual_image_tag}" =~ ^[0-9a-f]{7,40}$ ]]; then
|
if [[ ! "${actual_release_commit}" =~ ^[0-9a-f]{7,40}$ ]]; then
|
||||||
echo "Remote image tag must contain a commit id for ${domain}, got: ${actual_image_tag}" >&2
|
echo "Homepage release commit must contain a commit id for ${domain}, got: ${actual_release_commit}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${actual_release_commit}" != "${actual_image_tag}" ]]; then
|
if [[ "${actual_release_version}" != "${actual_image_tag}" ]]; then
|
||||||
echo "Remote release commit mismatch for ${domain}: expected ${actual_image_tag}, got ${actual_release_commit}" >&2
|
echo "Homepage release version mismatch for ${domain}: expected ${actual_image_tag}, got ${actual_release_version}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${actual_dashboard_url}" != "${EXPECTED_DASHBOARD_URL}" ]]; then
|
if [[ "${actual_release_commit}" != "${EXPECTED_RELEASE_COMMIT}" ]]; then
|
||||||
echo "Remote dashboardUrl mismatch for ${domain}: expected ${EXPECTED_DASHBOARD_URL}, got ${actual_dashboard_url}" >&2
|
echo "Homepage release commit mismatch for ${domain}: expected ${EXPECTED_RELEASE_COMMIT}, got ${actual_release_commit}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf 'verified release image for %s: %s\n' "${domain}" "${actual_image_ref}" >&2
|
printf 'verified homepage release image for %s: %s\n' "${domain}" "${actual_image_ref}" >&2
|
||||||
printf 'verified release commit for %s: %s\n' "${domain}" "${actual_release_commit}" >&2
|
printf 'verified homepage release commit for %s: %s\n' "${domain}" "${actual_release_commit}" >&2
|
||||||
printf 'verified dashboardUrl for %s: %s\n' "${domain}" "${actual_dashboard_url}" >&2
|
printf 'verified homepage release version for %s: %s\n' "${domain}" "${actual_release_version}" >&2
|
||||||
|
|
||||||
printf '%s\t%s\t%s\t%s\t%s\n' "${domain}" "${actual_image_ref}" "${actual_image_tag}" "${actual_release_commit}" "${actual_dashboard_url}"
|
printf '%s\t%s\t%s\t%s\t%s\n' "${domain}" "${actual_image_ref}" "${actual_image_tag}" "${actual_release_commit}" "${actual_release_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
mapfile -t expected_release_lines < <(parse_image_ref "${EXPECTED_IMAGE_REF}")
|
mapfile -t expected_release_lines < <(parse_image_ref "${EXPECTED_IMAGE_REF}")
|
||||||
EXPECTED_RELEASE_IMAGE_REF="${expected_release_lines[0]-}"
|
EXPECTED_RELEASE_IMAGE_REF="${expected_release_lines[0]-}"
|
||||||
EXPECTED_RELEASE_IMAGE_TAG="${expected_release_lines[1]-}"
|
EXPECTED_RELEASE_IMAGE_TAG="${expected_release_lines[1]-}"
|
||||||
EXPECTED_RELEASE_COMMIT="${expected_release_lines[2]-}"
|
EXPECTED_RELEASE_COMMIT="${expected_release_lines[2]-}"
|
||||||
|
EXPECTED_RELEASE_VERSION="${expected_release_lines[3]-}"
|
||||||
|
|
||||||
if [[ -z "${EXPECTED_RELEASE_IMAGE_REF}" || -z "${EXPECTED_RELEASE_IMAGE_TAG}" || -z "${EXPECTED_RELEASE_COMMIT}" ]]; then
|
if [[ -z "${EXPECTED_RELEASE_IMAGE_REF}" || -z "${EXPECTED_RELEASE_IMAGE_TAG}" || -z "${EXPECTED_RELEASE_COMMIT}" || -z "${EXPECTED_RELEASE_VERSION}" ]]; then
|
||||||
echo "Expected image ref is invalid: ${EXPECTED_IMAGE_REF}" >&2
|
echo "Expected image ref is invalid: ${EXPECTED_IMAGE_REF}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -175,10 +181,10 @@ fi
|
|||||||
reference_image_ref=""
|
reference_image_ref=""
|
||||||
reference_image_tag=""
|
reference_image_tag=""
|
||||||
reference_release_commit=""
|
reference_release_commit=""
|
||||||
reference_dashboard_url=""
|
reference_release_version=""
|
||||||
|
|
||||||
for row in "${verification_rows[@]}"; do
|
for row in "${verification_rows[@]}"; do
|
||||||
IFS=$'\t' read -r domain actual_image_ref actual_image_tag actual_release_commit actual_dashboard_url <<< "${row}"
|
IFS=$'\t' read -r domain actual_image_ref actual_image_tag actual_release_commit actual_release_version <<< "${row}"
|
||||||
|
|
||||||
if [[ "${actual_image_ref}" != "${EXPECTED_RELEASE_IMAGE_REF}" ]]; then
|
if [[ "${actual_image_ref}" != "${EXPECTED_RELEASE_IMAGE_REF}" ]]; then
|
||||||
echo "Release image mismatch for ${domain}: expected ${EXPECTED_RELEASE_IMAGE_REF}, got ${actual_image_ref}" >&2
|
echo "Release image mismatch for ${domain}: expected ${EXPECTED_RELEASE_IMAGE_REF}, got ${actual_image_ref}" >&2
|
||||||
@ -195,11 +201,21 @@ for row in "${verification_rows[@]}"; do
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${actual_release_version}" != "${EXPECTED_RELEASE_VERSION}" ]]; then
|
||||||
|
echo "Release version mismatch for ${domain}: expected ${EXPECTED_RELEASE_VERSION}, got ${actual_release_version}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z "${reference_image_ref}" ]]; then
|
if [[ -z "${reference_image_ref}" ]]; then
|
||||||
reference_image_ref="${actual_image_ref}"
|
reference_image_ref="${actual_image_ref}"
|
||||||
reference_image_tag="${actual_image_tag}"
|
reference_image_tag="${actual_image_tag}"
|
||||||
reference_release_commit="${actual_release_commit}"
|
reference_release_commit="${actual_release_commit}"
|
||||||
reference_dashboard_url="${actual_dashboard_url}"
|
reference_release_version="${actual_release_version}"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${actual_image_ref}" != "${reference_image_ref}" || "${actual_image_tag}" != "${reference_image_tag}" || "${actual_release_commit}" != "${reference_release_commit}" || "${actual_release_version}" != "${reference_release_version}" ]]; then
|
||||||
|
echo "Release metadata drift detected across served domains." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type { Metadata } from 'next'
|
|||||||
import Script from 'next/script'
|
import Script from 'next/script'
|
||||||
import { Analytics } from '@vercel/analytics/react'
|
import { Analytics } from '@vercel/analytics/react'
|
||||||
import { AppProviders } from './AppProviders'
|
import { AppProviders } from './AppProviders'
|
||||||
|
import { resolveWebReleaseMetadata } from '@/lib/webReleaseMetadata'
|
||||||
import { getConsoleIntegrationDefaults } from '@/server/consoleIntegrations'
|
import { getConsoleIntegrationDefaults } from '@/server/consoleIntegrations'
|
||||||
|
|
||||||
const DEFAULT_TITLE = 'Cloud-Neutral Console | Unified Cloud Native Tools'
|
const DEFAULT_TITLE = 'Cloud-Neutral Console | Unified Cloud Native Tools'
|
||||||
@ -76,12 +77,17 @@ const GA_ID = 'G-T4VM8G4Q42'
|
|||||||
|
|
||||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
const assistantDefaults = getConsoleIntegrationDefaults()
|
const assistantDefaults = getConsoleIntegrationDefaults()
|
||||||
|
const releaseMetadata = resolveWebReleaseMetadata()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html {...htmlAttributes}>
|
<html {...htmlAttributes}>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#6366f1" />
|
<meta name="theme-color" content="#6366f1" />
|
||||||
|
{releaseMetadata.image ? <meta name="svc-plus-release-image" content={releaseMetadata.image} /> : null}
|
||||||
|
{releaseMetadata.tag ? <meta name="svc-plus-release-tag" content={releaseMetadata.tag} /> : null}
|
||||||
|
{releaseMetadata.commit ? <meta name="svc-plus-release-commit" content={releaseMetadata.commit} /> : null}
|
||||||
|
{releaseMetadata.version ? <meta name="svc-plus-release-version" content={releaseMetadata.version} /> : null}
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
|||||||
37
src/lib/webReleaseMetadata.test.ts
Normal file
37
src/lib/webReleaseMetadata.test.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { resolveWebReleaseMetadata } from "@lib/webReleaseMetadata";
|
||||||
|
|
||||||
|
describe("webReleaseMetadata", () => {
|
||||||
|
it("returns trimmed public release metadata", () => {
|
||||||
|
expect(
|
||||||
|
resolveWebReleaseMetadata({
|
||||||
|
NEXT_PUBLIC_RELEASE_IMAGE: " ghcr.io/x-evor/console:abc123 ",
|
||||||
|
NEXT_PUBLIC_RELEASE_TAG: " abc123 ",
|
||||||
|
NEXT_PUBLIC_RELEASE_COMMIT: " abc123 ",
|
||||||
|
NEXT_PUBLIC_RELEASE_VERSION: " sha-abc123 ",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
image: "ghcr.io/x-evor/console:abc123",
|
||||||
|
tag: "abc123",
|
||||||
|
commit: "abc123",
|
||||||
|
version: "sha-abc123",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("normalizes empty public release metadata to null", () => {
|
||||||
|
expect(
|
||||||
|
resolveWebReleaseMetadata({
|
||||||
|
NEXT_PUBLIC_RELEASE_IMAGE: " ",
|
||||||
|
NEXT_PUBLIC_RELEASE_TAG: "",
|
||||||
|
NEXT_PUBLIC_RELEASE_COMMIT: undefined,
|
||||||
|
NEXT_PUBLIC_RELEASE_VERSION: " ",
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
image: null,
|
||||||
|
tag: null,
|
||||||
|
commit: null,
|
||||||
|
version: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
22
src/lib/webReleaseMetadata.ts
Normal file
22
src/lib/webReleaseMetadata.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export type TWebReleaseMetadata = {
|
||||||
|
image: string | null
|
||||||
|
tag: string | null
|
||||||
|
commit: string | null
|
||||||
|
version: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
type TReleaseMetadataEnv = Record<string, string | undefined>
|
||||||
|
|
||||||
|
function normalizeReleaseValue(value: string | undefined): string | null {
|
||||||
|
const normalizedValue = value?.trim()
|
||||||
|
return normalizedValue ? normalizedValue : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveWebReleaseMetadata(env: TReleaseMetadataEnv = process.env): TWebReleaseMetadata {
|
||||||
|
return {
|
||||||
|
image: normalizeReleaseValue(env.NEXT_PUBLIC_RELEASE_IMAGE),
|
||||||
|
tag: normalizeReleaseValue(env.NEXT_PUBLIC_RELEASE_TAG),
|
||||||
|
commit: normalizeReleaseValue(env.NEXT_PUBLIC_RELEASE_COMMIT),
|
||||||
|
version: normalizeReleaseValue(env.NEXT_PUBLIC_RELEASE_VERSION),
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user