test(e2e): §22-current Tier-1 harness + metadata E2E (SLICE-3/4/5)
Modernize the Tier-1 stack to the three-tier app and add browser coverage for the §22.4a metadata UI, closing the E2E gap deferred across SLICE-3/4/5: - seed-gitea.sh: create a REGISTRY_REPO with projects.yaml + a faceted named collection (.collection.yaml fields: priority enum + tags) seeded with three metadata-bearing entries; register content+registry webhooks; self-guarding (skip if a prior token still works) so a dependency-triggered re-run can't remint and invalidate the backend's token. - .env.tier1: REGISTRY_REPO/DEFAULT_PROJECT_ID; disable OTC cooldown + lift the per-IP auth limiter for the single-IP test runner. - docker-compose: pin backend image; backend-seed inserts a granted owner the OTC path can sign in as (write paths need contributor+). - Makefile: two-phase tier1-up (seed to completion, then create backend so it reads the populated token env); robust down; e2e-fresh = down+up+e2e (the canonical run, since the edit/bulk specs mutate the seeded corpus). - metadata.spec.js: SLICE-3 faceted filter (anon), SLICE-4 edit panel (owner), SLICE-5 bulk bar (owner). 4 passed against a fresh stack. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,18 @@
|
|||||||
.PHONY: tier1-up tier1-down tier1-logs fe-unit e2e e2e-install
|
.PHONY: tier1-up tier1-down tier1-logs fe-unit e2e e2e-install e2e-fresh
|
||||||
|
|
||||||
|
# Two-phase: run the Gitea seed to completion FIRST so it writes the bot token /
|
||||||
|
# OAuth creds into generated/.env.tier1.generated, THEN create the backend/web —
|
||||||
|
# compose snapshots env_file at container-create time, so the backend must be
|
||||||
|
# created after the seed has populated it. The touch seeds an empty placeholder
|
||||||
|
# for compose's up-front env_file existence check on a clean checkout.
|
||||||
tier1-up:
|
tier1-up:
|
||||||
|
touch testing/generated/.env.tier1.generated
|
||||||
|
docker compose -f testing/docker-compose.yml up --build -d gitea-seed
|
||||||
|
docker compose -f testing/docker-compose.yml wait gitea-seed
|
||||||
docker compose -f testing/docker-compose.yml up --build -d
|
docker compose -f testing/docker-compose.yml up --build -d
|
||||||
|
|
||||||
tier1-down:
|
tier1-down:
|
||||||
|
touch testing/generated/.env.tier1.generated
|
||||||
docker compose -f testing/docker-compose.yml down -v
|
docker compose -f testing/docker-compose.yml down -v
|
||||||
|
|
||||||
tier1-logs:
|
tier1-logs:
|
||||||
@@ -17,3 +26,8 @@ e2e-install:
|
|||||||
|
|
||||||
e2e:
|
e2e:
|
||||||
cd e2e && BASE_URL=$${BASE_URL:-http://localhost:8080} MAILSINK_URL=$${MAILSINK_URL:-http://localhost:8025} npm run e2e
|
cd e2e && BASE_URL=$${BASE_URL:-http://localhost:8080} MAILSINK_URL=$${MAILSINK_URL:-http://localhost:8025} npm run e2e
|
||||||
|
|
||||||
|
# Canonical run: the metadata specs mutate the seeded corpus (edit/bulk write
|
||||||
|
# real commits), so they assume a freshly-seeded stack. This brings the stack
|
||||||
|
# down, back up (re-seeds), and runs the suite once — the shape CI uses.
|
||||||
|
e2e-fresh: tier1-down tier1-up e2e
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { waitForLatestOtc, clearMailpit } from './mailpit.js'
|
||||||
|
|
||||||
|
// Sign in through the §6.2 OTC path, leaving the rfc_session cookie on the
|
||||||
|
// page's browser context (page.request shares the page context's cookie jar,
|
||||||
|
// so a subsequent page.goto is authenticated). The Tier-1 seed pre-provisions
|
||||||
|
// `e2e-owner@example.test` as a granted deployment owner, so signing in as that
|
||||||
|
// address yields write access to the metadata edit/bulk paths (SLICE-4/5).
|
||||||
|
export async function signIn(page, email) {
|
||||||
|
await clearMailpit()
|
||||||
|
const req = await page.request.post('/auth/otc/request', { data: { email } })
|
||||||
|
if (!req.ok()) throw new Error(`otc request failed: ${req.status()}`)
|
||||||
|
const code = await waitForLatestOtc(email)
|
||||||
|
const verify = await page.request.post('/auth/otc/verify', { data: { email, code } })
|
||||||
|
if (!verify.ok()) throw new Error(`otc verify failed: ${verify.status()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OWNER_EMAIL = 'e2e-owner@example.test'
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Dismiss the cookie-consent banner if it's showing. It is fixed to the bottom
|
||||||
|
// of the viewport and intercepts pointer events over the catalog footer (where
|
||||||
|
// row-select checkboxes live), so tests that click there must clear it first.
|
||||||
|
export async function dismissCookies(page) {
|
||||||
|
const banner = page.locator('.cookie-consent-banner')
|
||||||
|
if (await banner.count()) {
|
||||||
|
await page.getByRole('button', { name: 'Save choice' }).click().catch(() => {})
|
||||||
|
await banner.waitFor({ state: 'hidden' }).catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import { signIn, OWNER_EMAIL } from './lib/auth.js'
|
||||||
|
import { dismissCookies } from './lib/ui.js'
|
||||||
|
|
||||||
|
// §22.4a Configurable Collection Metadata — end-to-end browser coverage for the
|
||||||
|
// three UI slices, against the Tier-1 stack's faceted `bdd` collection (seeded
|
||||||
|
// in testing/seed-gitea.sh with a fields: schema of priority(enum) + tags, and
|
||||||
|
// three entries: checkout-guest [P0], checkout-returning [P1], search-facets [P0]).
|
||||||
|
//
|
||||||
|
// Tests run in order against a freshly-seeded stack: SLICE-3 reads first, then
|
||||||
|
// SLICE-4 edits `checkout-returning`, then SLICE-5 bulk-edits the two P0 entries
|
||||||
|
// (distinct rows — no cross-test interference within one run).
|
||||||
|
|
||||||
|
const BDD = '/p/ohm/c/bdd'
|
||||||
|
|
||||||
|
// SLICE-3 (PUC-3) — faceted left-pane filtering, anonymous/read-only.
|
||||||
|
test('SLICE-3: faceted filter narrows the catalog by Priority', async ({ page }) => {
|
||||||
|
await page.goto(BDD)
|
||||||
|
await dismissCookies(page)
|
||||||
|
const catalog = page.locator('aside.catalog')
|
||||||
|
|
||||||
|
// All three entries are listed initially.
|
||||||
|
await expect(catalog.getByText('Guest checkout')).toBeVisible()
|
||||||
|
await expect(catalog.getByText('Returning-customer checkout')).toBeVisible()
|
||||||
|
await expect(catalog.getByText('Faceted search')).toBeVisible()
|
||||||
|
|
||||||
|
// The Priority facet renders with the seeded P0 count (2 entries).
|
||||||
|
const p0 = catalog.locator('.facet-value', { hasText: 'P0' })
|
||||||
|
await expect(p0.locator('.facet-count')).toHaveText('2')
|
||||||
|
|
||||||
|
// Selecting P0 re-fetches server-side and drops the lone P1 entry.
|
||||||
|
await p0.getByRole('checkbox').check()
|
||||||
|
await expect(catalog.getByText('Returning-customer checkout')).toHaveCount(0)
|
||||||
|
await expect(catalog.getByText('Guest checkout')).toBeVisible()
|
||||||
|
await expect(catalog.getByText('Faceted search')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
// SLICE-4 (PUC-1) — single-entry metadata edit via the detail panel (authed).
|
||||||
|
test('SLICE-4: edit one entry\'s priority from the detail panel', async ({ page }) => {
|
||||||
|
await signIn(page, OWNER_EMAIL)
|
||||||
|
await page.goto(`${BDD}/e/checkout-returning`)
|
||||||
|
await dismissCookies(page)
|
||||||
|
|
||||||
|
const panel = page.locator('.metadata-fields-panel')
|
||||||
|
await expect(panel).toBeVisible()
|
||||||
|
// Seeded at P1; change to P2 and save (direct sidecar commit, D7).
|
||||||
|
await expect(panel.locator('#mf-priority')).toHaveValue('P1')
|
||||||
|
await panel.locator('#mf-priority').selectOption('P2')
|
||||||
|
await panel.getByRole('button', { name: 'Save' }).click()
|
||||||
|
await expect(panel.getByText('Saved')).toBeVisible()
|
||||||
|
|
||||||
|
// Persisted across a reload (read back from the committed sidecar).
|
||||||
|
await page.reload()
|
||||||
|
await expect(page.locator('.metadata-fields-panel #mf-priority')).toHaveValue('P2')
|
||||||
|
})
|
||||||
|
|
||||||
|
// SLICE-5 (PUC-2) — multi-select + bulk action bar, one commit (authed).
|
||||||
|
test('SLICE-5: bulk-set priority on multiple selected entries', async ({ page }) => {
|
||||||
|
await signIn(page, OWNER_EMAIL)
|
||||||
|
await page.goto(BDD)
|
||||||
|
await dismissCookies(page)
|
||||||
|
const catalog = page.locator('aside.catalog')
|
||||||
|
await expect(catalog.getByText('Guest checkout')).toBeVisible()
|
||||||
|
|
||||||
|
// Select the two P0 entries.
|
||||||
|
await catalog.getByLabel('select Guest checkout').check()
|
||||||
|
await catalog.getByLabel('select Faceted search').check()
|
||||||
|
|
||||||
|
// The sticky bulk bar appears with the selected count.
|
||||||
|
const bar = page.locator('.bulk-action-bar')
|
||||||
|
await expect(bar.getByText('2 selected')).toBeVisible()
|
||||||
|
|
||||||
|
// Set priority P1 across both → one commit; a toast reports the result.
|
||||||
|
await bar.getByLabel('Set Priority').selectOption('P1')
|
||||||
|
await expect(page.getByText(/2 updated/)).toBeVisible()
|
||||||
|
|
||||||
|
// The change is reflected server-side: the Priority P1 facet now counts the
|
||||||
|
// two newly-updated entries plus the pre-existing P1 (checkout-returning was
|
||||||
|
// P1 at seed; SLICE-4 may have moved it — assert at least the two we set).
|
||||||
|
await expect(catalog.locator('.facet-value', { hasText: 'P1' }).locator('.facet-count'))
|
||||||
|
.not.toHaveText('0')
|
||||||
|
})
|
||||||
+10
-1
@@ -3,7 +3,8 @@ GITEA_BOT_USER=rfc-bot
|
|||||||
GITEA_BOT_TOKEN=tier1-bot-token-PLACEHOLDER
|
GITEA_BOT_TOKEN=tier1-bot-token-PLACEHOLDER
|
||||||
GITEA_ORG=wiggleverse
|
GITEA_ORG=wiggleverse
|
||||||
META_REPO=ohm-content
|
META_REPO=ohm-content
|
||||||
REGISTRY_REPO=
|
REGISTRY_REPO=rfc-registry
|
||||||
|
DEFAULT_PROJECT_ID=ohm
|
||||||
OAUTH_CLIENT_ID=tier1-oauth-client-PLACEHOLDER
|
OAUTH_CLIENT_ID=tier1-oauth-client-PLACEHOLDER
|
||||||
OAUTH_CLIENT_SECRET=tier1-oauth-secret-PLACEHOLDER
|
OAUTH_CLIENT_SECRET=tier1-oauth-secret-PLACEHOLDER
|
||||||
APP_URL=http://localhost:8080
|
APP_URL=http://localhost:8080
|
||||||
@@ -19,3 +20,11 @@ EMAIL_FROM=rfc@example.test
|
|||||||
EMAIL_FROM_NAME=RFC Tier1
|
EMAIL_FROM_NAME=RFC Tier1
|
||||||
EMAIL_ENABLED=true
|
EMAIL_ENABLED=true
|
||||||
TURNSTILE_REQUIRED=false
|
TURNSTILE_REQUIRED=false
|
||||||
|
# Tier-1/e2e: disable the per-email OTC request cooldown so a test can sign the
|
||||||
|
# same account in more than once across specs without 429s.
|
||||||
|
OTC_REQUEST_COOLDOWN_SECONDS=0
|
||||||
|
# Tier-1/e2e drives the auth endpoints repeatedly from one IP; lift the per-IP
|
||||||
|
# sliding-window budgets well above a single suite run (prod leaves these unset
|
||||||
|
# and keeps the secure defaults).
|
||||||
|
RATELIMIT_OTC_REQUEST_MAX=1000
|
||||||
|
RATELIMIT_VERIFY_MAX=1000
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ services:
|
|||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
|
image: rfc-tier1-backend
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: testing/backend.Dockerfile
|
dockerfile: testing/backend.Dockerfile
|
||||||
@@ -74,6 +75,30 @@ services:
|
|||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 30
|
retries: 30
|
||||||
|
|
||||||
|
# Insert a granted deployment-owner user keyed by a known e2e email, so the
|
||||||
|
# OTC sign-in path (which provisions only `pending` contributors) yields an
|
||||||
|
# owner who can exercise the metadata edit/bulk write paths (SLICE-4/5). The
|
||||||
|
# owner identity (gitea_login='owner') matches OWNER_GITEA_LOGIN. Idempotent.
|
||||||
|
backend-seed:
|
||||||
|
image: rfc-tier1-backend
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
volumes:
|
||||||
|
- backend-data:/data
|
||||||
|
entrypoint: ["python", "-c"]
|
||||||
|
command:
|
||||||
|
- |
|
||||||
|
import sqlite3
|
||||||
|
c = sqlite3.connect("/data/rfc-app.db")
|
||||||
|
c.execute("""INSERT INTO users
|
||||||
|
(gitea_id, gitea_login, email, display_name, avatar_url, role, permission_state)
|
||||||
|
SELECT 9001, 'owner', 'e2e-owner@example.test', 'E2E Owner', '', 'owner', 'granted'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM users WHERE email='e2e-owner@example.test' COLLATE NOCASE)""")
|
||||||
|
c.commit(); c.close()
|
||||||
|
print("backend-seed: owner user ready")
|
||||||
|
restart: "no"
|
||||||
|
|
||||||
web:
|
web:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
|
|||||||
+123
-27
@@ -1,14 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
|
# Tier-1 seed (§22-current). Stands up Gitea content + registry so the current
|
||||||
|
# three-tier app boots, plus a faceted **named collection** (a `.collection.yaml`
|
||||||
|
# with a `fields:` schema + entries carrying metadata) so the §22.4a metadata
|
||||||
|
# UI — faceted filter (SLICE-3), edit panel (SLICE-4), bulk bar (SLICE-5) — can
|
||||||
|
# be exercised end-to-end in a real browser.
|
||||||
|
|
||||||
GITEA="${GITEA_URL:-http://gitea:3000}"
|
GITEA="${GITEA_URL:-http://gitea:3000}"
|
||||||
ADMIN_USER="${GITEA_ADMIN_USER:-giteaadmin}"
|
ADMIN_USER="${GITEA_ADMIN_USER:-giteaadmin}"
|
||||||
ADMIN_PASS="${GITEA_ADMIN_PASSWORD:-giteaadmin-pass}"
|
ADMIN_PASS="${GITEA_ADMIN_PASSWORD:-giteaadmin-pass}"
|
||||||
ADMIN_EMAIL="${GITEA_ADMIN_EMAIL:-admin@example.test}"
|
|
||||||
ORG="${GITEA_ORG:-wiggleverse}"
|
ORG="${GITEA_ORG:-wiggleverse}"
|
||||||
BOT_USER="${GITEA_BOT_USER:-rfc-bot}"
|
BOT_USER="${GITEA_BOT_USER:-rfc-bot}"
|
||||||
BOT_PASS="${GITEA_BOT_PASSWORD:-rfc-bot-pass}"
|
BOT_PASS="${GITEA_BOT_PASSWORD:-rfc-bot-pass}"
|
||||||
CONTENT_REPO="${META_REPO:-ohm-content}"
|
CONTENT_REPO="${META_REPO:-ohm-content}"
|
||||||
|
REGISTRY_REPO="${REGISTRY_REPO:-rfc-registry}"
|
||||||
|
DEFAULT_PROJECT_ID="${DEFAULT_PROJECT_ID:-ohm}"
|
||||||
APP_URL="${APP_URL:-http://localhost:8080}"
|
APP_URL="${APP_URL:-http://localhost:8080}"
|
||||||
WEBHOOK_SECRET="${GITEA_WEBHOOK_SECRET:-tier1-webhook-secret}"
|
WEBHOOK_SECRET="${GITEA_WEBHOOK_SECRET:-tier1-webhook-secret}"
|
||||||
OUT="${SEED_OUT:-/seed/.env.tier1.generated}"
|
OUT="${SEED_OUT:-/seed/.env.tier1.generated}"
|
||||||
@@ -22,6 +29,21 @@ done
|
|||||||
|
|
||||||
auth_admin() { curl -sf -u "$ADMIN_USER:$ADMIN_PASS" "$@"; }
|
auth_admin() { curl -sf -u "$ADMIN_USER:$ADMIN_PASS" "$@"; }
|
||||||
|
|
||||||
|
# Idempotency guard: if a prior run already wrote a bot token that still works
|
||||||
|
# against THIS gitea (the registry is readable), the stack is already seeded —
|
||||||
|
# skip entirely. This makes a second invocation a true no-op, so a later
|
||||||
|
# dependency-triggered re-run can't delete/remint the token the backend is
|
||||||
|
# already using (that mismatch 401s the registry mirror). A fresh `down -v`
|
||||||
|
# brings up a new gitea where the stale token fails, so the seed re-runs.
|
||||||
|
if [ -f "$OUT" ]; then
|
||||||
|
EXIST_TOK=$(sed -n 's/^GITEA_BOT_TOKEN=//p' "$OUT")
|
||||||
|
if [ -n "$EXIST_TOK" ] && curl -sf -H "Authorization: token $EXIST_TOK" \
|
||||||
|
"$GITEA/api/v1/repos/$ORG/$REGISTRY_REPO/contents/projects.yaml?ref=main" >/dev/null 2>&1; then
|
||||||
|
echo "seed: existing token valid and registry present — already seeded, skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "seed: ensuring bot user"
|
echo "seed: ensuring bot user"
|
||||||
auth_admin -X POST "$GITEA/api/v1/admin/users" \
|
auth_admin -X POST "$GITEA/api/v1/admin/users" \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
@@ -34,55 +56,129 @@ auth_admin -X POST "$GITEA/api/v1/admin/users" \
|
|||||||
-d "{\"username\":\"owner\",\"email\":\"owner@example.test\",\"password\":\"owner-pass\",\"must_change_password\":false}" \
|
-d "{\"username\":\"owner\",\"email\":\"owner@example.test\",\"password\":\"owner-pass\",\"must_change_password\":false}" \
|
||||||
|| echo "seed: owner exists, continuing"
|
|| echo "seed: owner exists, continuing"
|
||||||
|
|
||||||
echo "seed: minting bot access token"
|
echo "seed: minting bot access token (drop any prior 'tier1-bot' first — idempotent)"
|
||||||
|
curl -s -u "$BOT_USER:$BOT_PASS" -X DELETE "$GITEA/api/v1/users/$BOT_USER/tokens/tier1-bot" >/dev/null 2>&1 || true
|
||||||
TOKEN=$(curl -sf -u "$BOT_USER:$BOT_PASS" -X POST "$GITEA/api/v1/users/$BOT_USER/tokens" \
|
TOKEN=$(curl -sf -u "$BOT_USER:$BOT_PASS" -X POST "$GITEA/api/v1/users/$BOT_USER/tokens" \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d '{"name":"tier1-bot","scopes":["write:repository","write:organization","write:user","write:admin"]}' \
|
-d '{"name":"tier1-bot","scopes":["write:repository","write:organization","write:user","write:admin"]}' \
|
||||||
| sed -n 's/.*"sha1":"\([^"]*\)".*/\1/p')
|
| sed -n 's/.*"sha1":"\([^"]*\)".*/\1/p')
|
||||||
[ -n "$TOKEN" ] || { echo "seed: failed to mint bot token" ; exit 1; }
|
[ -n "$TOKEN" ] || { echo "seed: failed to mint bot token" ; exit 1; }
|
||||||
|
|
||||||
|
api() { curl -s -H "Authorization: token $TOKEN" "$@"; }
|
||||||
|
|
||||||
echo "seed: ensuring org $ORG (owned by bot)"
|
echo "seed: ensuring org $ORG (owned by bot)"
|
||||||
curl -sf -H "Authorization: token $TOKEN" -X POST "$GITEA/api/v1/orgs" \
|
api -X POST "$GITEA/api/v1/orgs" -H 'Content-Type: application/json' \
|
||||||
-H 'Content-Type: application/json' \
|
-d "{\"username\":\"$ORG\"}" >/dev/null || echo "seed: org exists, continuing"
|
||||||
-d "{\"username\":\"$ORG\"}" || echo "seed: org exists, continuing"
|
|
||||||
|
|
||||||
echo "seed: ensuring content repo $ORG/$CONTENT_REPO"
|
ensure_repo() {
|
||||||
curl -sf -H "Authorization: token $TOKEN" -X POST "$GITEA/api/v1/orgs/$ORG/repos" \
|
api -X POST "$GITEA/api/v1/orgs/$ORG/repos" -H 'Content-Type: application/json' \
|
||||||
-H 'Content-Type: application/json' \
|
-d "{\"name\":\"$1\",\"auto_init\":true,\"default_branch\":\"main\"}" >/dev/null \
|
||||||
-d "{\"name\":\"$CONTENT_REPO\",\"auto_init\":true,\"default_branch\":\"main\"}" \
|
|| echo "seed: repo $1 exists, continuing"
|
||||||
|| echo "seed: content repo exists, continuing"
|
}
|
||||||
|
|
||||||
echo "seed: seeding one entry under rfcs/ so the catalog is non-empty"
|
# put_file <repo> <path> <plaintext>
|
||||||
B64=$(printf '%s' '---
|
put_file() {
|
||||||
|
_b64=$(printf '%s' "$3" | base64 | tr -d '\n')
|
||||||
|
api -X POST "$GITEA/api/v1/repos/$ORG/$1/contents/$2" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"message\":\"seed $2\",\"content\":\"$_b64\",\"branch\":\"main\"}" >/dev/null \
|
||||||
|
|| echo "seed: $1/$2 exists, continuing"
|
||||||
|
}
|
||||||
|
|
||||||
|
register_webhook() {
|
||||||
|
api -X POST "$GITEA/api/v1/repos/$ORG/$1/hooks" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"type\":\"gitea\",\"active\":true,\"events\":[\"push\",\"pull_request\"],\"config\":{\"url\":\"http://backend:8000/api/webhooks/gitea\",\"content_type\":\"json\",\"secret\":\"$WEBHOOK_SECRET\"}}" >/dev/null \
|
||||||
|
|| echo "seed: webhook on $1 exists, continuing"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "seed: ensuring content repo $ORG/$CONTENT_REPO and registry $ORG/$REGISTRY_REPO"
|
||||||
|
ensure_repo "$CONTENT_REPO"
|
||||||
|
ensure_repo "$REGISTRY_REPO"
|
||||||
|
|
||||||
|
echo "seed: registry projects.yaml (default project '$DEFAULT_PROJECT_ID')"
|
||||||
|
put_file "$REGISTRY_REPO" "projects.yaml" "deployment:
|
||||||
|
name: Tier1 RFC
|
||||||
|
tagline: Tier-1 end-to-end deployment
|
||||||
|
projects:
|
||||||
|
- id: $DEFAULT_PROJECT_ID
|
||||||
|
name: OHM
|
||||||
|
type: document
|
||||||
|
content_repo: $CONTENT_REPO
|
||||||
|
visibility: public
|
||||||
|
"
|
||||||
|
|
||||||
|
echo "seed: default-collection entry under rfcs/ (no fields — legacy/document path)"
|
||||||
|
put_file "$CONTENT_REPO" "rfcs/intro.md" "---
|
||||||
|
slug: intro
|
||||||
title: Intro
|
title: Intro
|
||||||
status: graduated
|
state: active
|
||||||
id: RFC-0001
|
id: RFC-0001
|
||||||
owners: [owner]
|
owners: [owner]
|
||||||
---
|
---
|
||||||
|
|
||||||
# Intro
|
# Intro
|
||||||
|
|
||||||
Seed entry for Tier-1 e2e.
|
Seed entry for the default (document) collection.
|
||||||
' | base64 | tr -d '\n')
|
"
|
||||||
curl -s -H "Authorization: token $TOKEN" -X POST \
|
|
||||||
"$GITEA/api/v1/repos/$ORG/$CONTENT_REPO/contents/rfcs/intro.md" \
|
echo "seed: faceted named collection 'bdd' with a fields: schema (§22.4a)"
|
||||||
-H 'Content-Type: application/json' \
|
put_file "$CONTENT_REPO" "bdd/.collection.yaml" "type: bdd
|
||||||
-d "{\"message\":\"seed intro\",\"content\":\"$B64\",\"branch\":\"main\"}" \
|
visibility: public
|
||||||
|| echo "seed: intro.md exists, continuing"
|
name: BDD Scenarios
|
||||||
|
fields:
|
||||||
|
priority:
|
||||||
|
type: enum
|
||||||
|
values: [P0, P1, P2]
|
||||||
|
label: Priority
|
||||||
|
tags:
|
||||||
|
type: tags
|
||||||
|
label: Tags
|
||||||
|
"
|
||||||
|
|
||||||
|
# Three entries with varied priority/tags so facets have counts and the bulk
|
||||||
|
# bar has multiple selectable rows.
|
||||||
|
put_file "$CONTENT_REPO" "bdd/rfcs/checkout-guest.md" "---
|
||||||
|
slug: checkout-guest
|
||||||
|
title: Guest checkout
|
||||||
|
state: active
|
||||||
|
priority: P0
|
||||||
|
tags: [checkout, payments]
|
||||||
|
---
|
||||||
|
|
||||||
|
Guest checkout scenario.
|
||||||
|
"
|
||||||
|
put_file "$CONTENT_REPO" "bdd/rfcs/checkout-returning.md" "---
|
||||||
|
slug: checkout-returning
|
||||||
|
title: Returning-customer checkout
|
||||||
|
state: active
|
||||||
|
priority: P1
|
||||||
|
tags: [checkout]
|
||||||
|
---
|
||||||
|
|
||||||
|
Returning-customer checkout scenario.
|
||||||
|
"
|
||||||
|
put_file "$CONTENT_REPO" "bdd/rfcs/search-facets.md" "---
|
||||||
|
slug: search-facets
|
||||||
|
title: Faceted search
|
||||||
|
state: active
|
||||||
|
priority: P0
|
||||||
|
tags: [search]
|
||||||
|
---
|
||||||
|
|
||||||
|
Faceted search scenario.
|
||||||
|
"
|
||||||
|
|
||||||
echo "seed: registering OAuth application"
|
echo "seed: registering OAuth application"
|
||||||
OAUTH_JSON=$(curl -sf -u "$ADMIN_USER:$ADMIN_PASS" -X POST "$GITEA/api/v1/user/applications/oauth2" \
|
OAUTH_JSON=$(auth_admin -X POST "$GITEA/api/v1/user/applications/oauth2" \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d "{\"name\":\"rfc-app-tier1\",\"redirect_uris\":[\"$APP_URL/auth/callback\"],\"confidential_client\":true}")
|
-d "{\"name\":\"rfc-app-tier1\",\"redirect_uris\":[\"$APP_URL/auth/callback\"],\"confidential_client\":true}")
|
||||||
CLIENT_ID=$(printf '%s' "$OAUTH_JSON" | sed -n 's/.*"client_id":"\([^"]*\)".*/\1/p')
|
CLIENT_ID=$(printf '%s' "$OAUTH_JSON" | sed -n 's/.*"client_id":"\([^"]*\)".*/\1/p')
|
||||||
CLIENT_SECRET=$(printf '%s' "$OAUTH_JSON" | sed -n 's/.*"client_secret":"\([^"]*\)".*/\1/p')
|
CLIENT_SECRET=$(printf '%s' "$OAUTH_JSON" | sed -n 's/.*"client_secret":"\([^"]*\)".*/\1/p')
|
||||||
|
|
||||||
echo "seed: registering webhook on content repo -> backend"
|
echo "seed: registering webhooks (content + registry) -> backend"
|
||||||
curl -s -H "Authorization: token $TOKEN" -X POST \
|
register_webhook "$CONTENT_REPO"
|
||||||
"$GITEA/api/v1/repos/$ORG/$CONTENT_REPO/hooks" \
|
register_webhook "$REGISTRY_REPO"
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-d "{\"type\":\"gitea\",\"active\":true,\"events\":[\"push\",\"pull_request\"],\"config\":{\"url\":\"http://backend:8000/api/webhooks/gitea\",\"content_type\":\"json\",\"secret\":\"$WEBHOOK_SECRET\"}}" \
|
|
||||||
|| echo "seed: webhook exists, continuing"
|
|
||||||
|
|
||||||
echo "seed: writing generated env to $OUT"
|
echo "seed: writing generated env to $OUT"
|
||||||
cat > "$OUT" <<EOF
|
cat > "$OUT" <<EOF
|
||||||
|
|||||||
Reference in New Issue
Block a user