diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index d964615d3..51b4086b6 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -2,54 +2,51 @@ name: Docker image # Build + publish the production image to GHCR (#1381 deliverable 3). # -# Triggers (deliberately tag-only — see "Why no main / PR triggers" -# below): +# Triggers: # - push to *.*.* tag → : + :latest + : + :. -# - workflow_dispatch → manual rerun, dispatched against a tag -# ref to rebuild a published release -# (e.g. if a publish failed midway). -# Dispatching from a non-tag ref is a +# - push to main → :main (rolling tip; not :latest) +# - workflow_dispatch → manual rerun. Dispatch against a tag +# ref to rebuild a published release, or +# against main to refresh :main. +# Dispatching from any other ref is a # no-op for tagging — metadata-action # emits an empty tag set and the publish # step fails loudly. # +# IMAGE_NAME is ${{ github.repository }}, so this workflow publishes +# to ghcr.io// (ghcr.io/srcdslab/sourcebans-pp here). +# # Multi-arch build via docker/build-push-action + buildx + qemu. Both # linux/amd64 and linux/arm64 are produced and pushed under a single -# manifest list, so `docker pull ghcr.io/sbpp/sourcebans-pp:latest` on -# either an Apple Silicon dev machine or a typical x86_64 VPS resolves -# to the right image without operator awareness. +# manifest list, so `docker pull ghcr.io/srcdslab/sourcebans-pp:latest` +# on either an Apple Silicon dev machine or a typical x86_64 VPS +# resolves to the right image without operator awareness. # # Signed via Sigstore cosign (keyless / OIDC). The ID-token permission # below is what enables the keyless signing flow: cosign requests an # OIDC token from GitHub's issuer, exchanges it with Fulcio for a # short-lived signing cert, signs the image's manifest, and records # the signature into Rekor (the public transparency log). Verifiers -# can `cosign verify ghcr.io/sbpp/sourcebans-pp: -# --certificate-identity-regexp=https://github.com/sbpp/sourcebans-pp/... +# can `cosign verify ghcr.io/srcdslab/sourcebans-pp: +# --certificate-identity-regexp=https://github.com/srcdslab/sourcebans-pp/... # --certificate-oidc-issuer=https://token.actions.githubusercontent.com` # without any pre-shared key. # -# Why no main / PR triggers: +# Why no PR triggers: # Multi-arch (amd64 + qemu-emulated arm64) image builds are the most # expensive job in this repo's CI matrix — roughly 8-15 minutes per -# run. Pre-fix this workflow ran on every push to main AND every PR -# touching a long path filter, which on a busy week burned through a -# disproportionate share of the project's free Actions minutes for -# images that nobody pulls (the floating `:main` and per-commit -# `:sha-` tags were nominally documented as "bleeding edge" -# but had no real consumers; self-hosters all pin to released semver -# tags per the docs). The image surface is small + stable: changes -# that affect the runtime contract (Dockerfile, entrypoint, schema -# files, init bootstrap, health.php, trust-proxy + telemetry hooks) -# are always shipped behind a release tag, so verifying-at-tag is -# both sufficient and well-aligned with when self-hosters actually -# pull a new image. Contributors who edit the Dockerfile / entrypoint -# locally are expected to run the literal `docker buildx build` -# command from the AGENTS.md "Quality gates" table to verify before -# opening a PR. +# run. PRs still do not pay that cost. Push-to-main publishes a +# floating `:main` tag so operators who want the tip of this repo +# can pull it without waiting for a semver cut. `:latest` stays +# bound to `*.*.*` tags only. Contributors who edit the Dockerfile / +# entrypoint locally are expected to run the literal +# `docker buildx build` command from the AGENTS.md "Quality gates" +# table to verify before opening a PR. on: push: + branches: + - main tags: - '*.*.*' workflow_dispatch: @@ -97,15 +94,13 @@ jobs: # docker/metadata-action computes the tag set from the trigger: # - X.Y.Z tag → :X.Y.Z, :X.Y, :X, :latest - # - workflow_dispatch → mirrors whatever ref it was dispatched - # against (typically a tag ref to - # rebuild a published release; a non-tag - # dispatch produces an empty tag set - # and the publish step fails loudly). + # - push / dispatch on main → :main + # - workflow_dispatch on a tag ref → same as the tag push + # - workflow_dispatch on any other ref → empty tag set, + # publish step fails loudly. # # The `:latest` tag is gated on `startsWith(github.ref, 'refs/tags/')` - # — a workflow_dispatch from a non-tag ref can't accidentally - # claim it. + # — a push or workflow_dispatch from main cannot claim it. - name: Compute image metadata (tags + labels) id: meta uses: docker/metadata-action@v5 @@ -116,12 +111,13 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }} + type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }} labels: | org.opencontainers.image.title=SourceBans++ - org.opencontainers.image.description=Self-hostable admin / ban / comms management for the Source engine — production image. - org.opencontainers.image.url=https://sbpp.github.io - org.opencontainers.image.source=https://github.com/sbpp/sourcebans-pp - org.opencontainers.image.documentation=https://sbpp.github.io/getting-started/quickstart-docker/ + org.opencontainers.image.description=Self-hostable admin / ban / comms management for the Source engine, production image. + org.opencontainers.image.url=https://github.com/${{ github.repository }} + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.documentation=https://github.com/${{ github.repository }}/tree/main/docs org.opencontainers.image.licenses=Elastic-2.0 org.opencontainers.image.vendor=SourceBans++ Dev Team org.opencontainers.image.revision=${{ github.sha }} @@ -130,10 +126,8 @@ jobs: # are persisted in the GitHub Actions cache between runs — buildx # keys the cache by the Dockerfile + the build context's hash, so # a Composer-only change won't bust the apt-install layer of the - # builder stage. (Cache hit rate is naturally low on the tag-only - # trigger — release tags are rare — but the cost of populating - # the cache on a release build is amortised across the next - # workflow_dispatch rerun for that tag.) + # builder stage. Main-branch publishes raise the cache hit + # rate; tag rebuilds still amortise across workflow_dispatch. - name: Build + push id: build uses: docker/build-push-action@v6 diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index f271fcee1..960288bbe 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -4,8 +4,9 @@ # stays out of the way of PRs that don't touch the docs site. # # Why it exists: catches `npm run build` failures (broken Markdown, -# missing assets, busted Starlight config) BEFORE the deploy trigger -# fires on main. Pair gate with docs-deploy-trigger.yml. +# missing assets, busted Starlight config) on PRs and merges that +# touch docs/. This repo has no Pages sibling; docs-deploy-trigger.yml +# is a documented no-op. name: docs-build diff --git a/.github/workflows/docs-deploy-trigger.yml b/.github/workflows/docs-deploy-trigger.yml index 66aedfc3c..36c6e0581 100644 --- a/.github/workflows/docs-deploy-trigger.yml +++ b/.github/workflows/docs-deploy-trigger.yml @@ -1,39 +1,14 @@ -# docs-deploy-trigger — fire a repository_dispatch into sbpp.github.io -# whenever main moves under docs/, kicking the Pages deploy in the -# sibling repo. +# docs-deploy-trigger — intentionally a no-op in this repo. # -# Cadence: only on push to main with a docs/** path filter. PRs use -# docs-build.yml to validate; this workflow is the production trigger. +# The sbpp copy of this file dispatches `docs-changed` into +# sbpp/sbpp.github.io so Pages publishes the Starlight site. This +# repo has no sibling Pages repo and must not fire that dispatch +# (it would require a PAT into someone else's org). # -# Required repo configuration BEFORE this workflow does anything (one-time -# cutover step): -# -# - Create a fine-grained PAT scoped to `sbpp/sbpp.github.io` only, -# with the `Actions: Read and write` repository permission. (Classic -# PATs work too, but the fine-grained variant is strictly narrower -# and the right default.) Max expiry is one year — set a calendar -# reminder to rotate. -# - Repo SECRET `DOCS_DEPLOY_PAT` = the token value. -# -# Until `DOCS_DEPLOY_PAT` is set, the dispatch step below is skipped via -# a precheck step that reads the secret into `env:` (where `secrets.*` IS -# allowed), tests for presence in shell, and emits a step output the -# dispatch step gates on. Every push to `docs/**` shows up as a green -# run with the dispatch step marked "Skipped", instead of red-failing on -# a missing credential. This stops the original anti-pattern -# (#1339-followup) where the dispatch hard-erred and an operator who -# hasn't done the cutover yet sees a stream of confusing failures. -# -# The naive shape (`if: secrets.DOCS_DEPLOY_PAT != ''` on the dispatch -# step itself) does NOT work: `secrets.*` is unavailable in `if:` at -# every scope (workflow / job / step) per the GitHub Actions context -# table, and the parser rejects the workflow file with -# "Unrecognized named-value: 'secrets'" before any job runs — the run -# fails red on every push including dependabot branches, defeating the -# whole point of the guard. -# -# The deploy shell in sbpp.github.io also has a `workflow_dispatch` -# trigger as a manual fallback while the PAT is pending. +# docs-build.yml still validates `npm run build` on PRs and merges +# that touch docs/. To publish a docs site later, replace this file +# with a real deploy (GitHub Pages from docs/, or a sibling repo) +# rather than restoring the sbpp/sbpp.github.io dispatch. name: docs-deploy-trigger @@ -45,55 +20,12 @@ on: - 'docs/**' - '.github/workflows/docs-deploy-trigger.yml' -# Allow at most one in-flight trigger at a time; if a second push lands -# while the first is still running, queue the second and skip any -# intermediate runs. The dispatched workflow in sbpp.github.io is -# itself idempotent (it always builds from sourcebans-pp@main), so -# coalescing here is safe. -concurrency: - group: docs-deploy-trigger - cancel-in-progress: false - jobs: - trigger: - name: Dispatch docs-changed event + skip: + name: No sibling Pages deploy runs-on: ubuntu-24.04 permissions: {} - steps: - # `secrets.*` isn't available in `if:` at any scope, so we can't - # gate the dispatch step directly on the PAT being configured. - # Read the secret into the precheck step's `env:` (where - # `secrets.*` IS allowed), test for presence in shell, and emit - # a `configured=true|false` step output. The dispatch step then - # gates on `steps.pat.outputs.configured == 'true'` — `steps.*` - # IS available in `if:`, so the gate works and the dispatch step - # cleanly shows as "Skipped" until the secret is set. - - name: Check whether DOCS_DEPLOY_PAT is configured - id: pat - env: - DOCS_DEPLOY_PAT: ${{ secrets.DOCS_DEPLOY_PAT }} - run: | - if [ -n "$DOCS_DEPLOY_PAT" ]; then - echo "configured=true" >> "$GITHUB_OUTPUT" - else - echo "configured=false" >> "$GITHUB_OUTPUT" - echo "::notice title=docs-deploy-trigger::DOCS_DEPLOY_PAT is unset; skipping repository_dispatch into sbpp.github.io. Configure the secret to enable automatic Pages deploys (the deploy shell still has a manual workflow_dispatch trigger as a fallback)." - fi - - # The dispatched workflow in sbpp.github.io listens for - # `event_type: docs-changed`. The client_payload carries the - # commit SHA and ref so the deploy job can pin its sourcebans-pp - # checkout to the exact commit that fired the dispatch (race - # guard for back-to-back pushes). - - name: Dispatch repository_dispatch into sbpp.github.io - if: steps.pat.outputs.configured == 'true' - env: - GH_TOKEN: ${{ secrets.DOCS_DEPLOY_PAT }} + - name: Skip run: | - gh api repos/sbpp/sbpp.github.io/dispatches \ - --method POST \ - --field event_type=docs-changed \ - --field 'client_payload[source_repo]=${{ github.repository }}' \ - --field 'client_payload[source_sha]=${{ github.sha }}' \ - --field 'client_payload[source_ref]=${{ github.ref }}' + echo "::notice title=docs-deploy-trigger::No Pages sibling is configured. Docs sources live under docs/; docs-build.yml still validates the Astro build." diff --git a/AGENTS.md b/AGENTS.md index 7a6860863..965142fad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ code change — never as a follow-up. CI doesn't gate this; it's on you. | Change the local dev stack (Docker, db-init, env vars) | `docker/README.md` first, link from `ARCHITECTURE.md` if it changes the dev mental model | | Edit user-facing install/quickstart | `docs/src/content/docs/getting-started/quickstart.mdx` (tarball flow) OR `quickstart-docker.mdx` (Docker flow). Keep the `` arms in `overview.mdx` + `prerequisites.mdx` consistent across the two paths (the README is a tiny landing page that links to docs — don't grow it back into a manual). | | Add or change a wizard step (page handler / View / template / shared helper) | `AGENTS.md` (Install wizard convention block) + the "Edit a step of the install wizard" row in "Where to find what" | -| Touch `docker/Dockerfile.prod`, `docker/php/prod-*`, `docker/apache/sbpp-prod.conf`, `docker-compose.prod.yml`, `.env.example.prod`, `docker/caddy/Caddyfile.example`, or `web/health.php` | `AGENTS.md` (Quality gates: `docker-image.yml` row; "Where to find what": the "Build / extend the production Docker image" + "Deploy / configure the production Docker stack" rows) + `docs/src/content/docs/getting-started/quickstart-docker.mdx` (the operator-facing doc) + `docker/README.md` (dev-vs-prod pointer). The `Plugin build specifics` block in AGENTS.md has a sibling `Prod Docker image specifics` block — keep them in sync with the workflow's tag mapping / sign step. The Docker-image gate is **release-only** (fires on `*.*.*` tag pushes + manual `workflow_dispatch` reruns); contributors who edit the Dockerfile / entrypoint MUST run the `docker buildx build` command from the Quality gates row locally before opening the PR — there is no per-PR CI gate to catch a broken image build. | +| Touch `docker/Dockerfile.prod`, `docker/php/prod-*`, `docker/apache/sbpp-prod.conf`, `docker-compose.prod.yml`, `.env.example.prod`, `docker/caddy/Caddyfile.example`, or `web/health.php` | `AGENTS.md` (Quality gates: `docker-image.yml` row; "Where to find what": the "Build / extend the production Docker image" + "Deploy / configure the production Docker stack" rows) + `docs/src/content/docs/getting-started/quickstart-docker.mdx` (the operator-facing doc) + `docker/README.md` (dev-vs-prod pointer). The `Plugin build specifics` block in AGENTS.md has a sibling `Prod Docker image specifics` block — keep them in sync with the workflow's tag mapping / sign step. The Docker-image workflow fires on `*.*.*` tag pushes (`:latest` + semver) and on push to `main` (`:main`). There is no per-PR image build; contributors who edit the Dockerfile / entrypoint MUST run the `docker buildx build` command from the Quality gates row locally before opening the PR. | | Change a user-facing install / upgrade / troubleshooting flow (PHP or SourceMod version requirements, installer wizard steps, `config.php` behavior, `web/updater/` runner output, plugin `databases.cfg` / `sourcebans.cfg` shape, error messages a self-hoster will see) | The relevant page under `docs/src/content/docs/` (the Starlight site published at sbpp.github.io). | | Add or remove a config knob a self-hoster sets (`config.php` keys, `databases.cfg` fields, plugin convars users tune) | `docs/` page that documents that knob, plus the matching `docs/src/content/docs/updating/*.mdx` page if it's a breaking change between releases | | Ship a new feature with a self-hoster-visible setup step (Discord forwarder, demos, theming, etc.) | New page or section under the right `docs/` group + sidebar entry in `docs/astro.config.mjs` | @@ -205,9 +205,10 @@ services: ## Quality gates CI runs six gates on every PR. Match them locally before opening one. -A seventh gate (the production Docker image) runs **only on release -tag pushes** — see the row's note + the `Prod Docker image specifics` -block below for the rationale and the contributor-side responsibility. +A seventh gate (the production Docker image) runs on `*.*.*` tag +pushes (semver + `:latest`) and on push to `main` (`:main` only). +PRs do not build the image. See the row's note + the `Prod Docker +image specifics` block below. | Gate | Local | CI workflow | | -------------- | ------------------------------------ | ---------------------- | @@ -217,7 +218,7 @@ block below for the rationale and the contributor-side responsibility. | API contract | `./sbpp.sh composer api-contract` | `api-contract.yml` | | Playwright E2E | `./sbpp.sh e2e` | `e2e.yml` | | Plugin build | `bash game/addons/sourcemod/scripting/scripts/resolve-plugin-version.sh` then `(cd game/addons/sourcemod/scripting && spcomp -i include sbpp_*.sp)` | `plugin-build.yml` | -| Prod Docker image (release-only) | `docker buildx build --platform linux/amd64,linux/arm64 -f docker/Dockerfile.prod .` | `docker-image.yml` (tag pushes + `workflow_dispatch` only — no per-PR run; contributor MUST run the local command before merging Dockerfile / entrypoint changes) | +| Prod Docker image | `docker buildx build --platform linux/amd64,linux/arm64 -f docker/Dockerfile.prod .` | `docker-image.yml` (`*.*.*` tag pushes publish `:latest` + semver; push to `main` publishes `:main`; `workflow_dispatch` rebuilds the ref it was dispatched against. No per-PR run; contributor MUST run the local command before merging Dockerfile / entrypoint changes) | PHPStan specifics: @@ -369,42 +370,35 @@ Plugin build specifics: Prod Docker image specifics: -- **Trigger surface is deliberately narrow: tag pushes + manual - `workflow_dispatch` only.** No `push: branches: [main]`, no - `pull_request:`. A multi-arch (amd64 + qemu-emulated arm64) build - is the most expensive job in this repo's CI matrix (~8-15 minutes - per run); pre-narrow this workflow ran on every push to main AND - every PR touching a long path filter, burning a disproportionate - share of the project's free Actions minutes for floating `:main` / - `:sha-` tags that nobody pulls (self-hosters all pin to - released semver tags per the docs). The image surface is small + - stable — runtime-affecting changes (Dockerfile, entrypoint, schema - files, init bootstrap, health.php, trust-proxy + telemetry hooks) - always ship behind a release tag, so verifying-at-tag is sufficient - AND well-aligned with when self-hosters actually pull a new image. - The trade-off the project explicitly accepts: a Dockerfile / - entrypoint regression that landed via a green PR isn't caught by - CI until the next `*.*.*` tag push. **Contributors who edit any - file the runtime image bakes in (Dockerfile, entrypoint, php.ini, - Apache conf, sb-db.php, health.php, schema files, init bootstrap, - Auth/Host.php, Telemetry.php) MUST run the literal `docker buildx - build --platform linux/amd64,linux/arm64 -f docker/Dockerfile.prod .` - command from the Quality gates table locally before opening the - PR — the local command IS the per-PR gate.** +- **Trigger surface:** `*.*.*` tag pushes, push to `main`, and + manual `workflow_dispatch`. No `pull_request:`. A multi-arch + (amd64 + qemu-emulated arm64) build is the most expensive job in + this repo's CI matrix (~8-15 minutes per run). PRs do not pay that + cost. Push-to-main publishes a floating `:main` tag so operators + can pull the tip of this repo without waiting for a semver cut. + `:latest` stays bound to `*.*.*` tags only. Runtime-affecting + changes (Dockerfile, entrypoint, schema files, init bootstrap, + health.php, trust-proxy + telemetry hooks) that land via a green + PR are visible on `:main` as soon as they merge. **Contributors + who edit any file the runtime image bakes in (Dockerfile, + entrypoint, php.ini, Apache conf, sb-db.php, health.php, schema + files, init bootstrap, Auth/Host.php, Telemetry.php) MUST run the + literal `docker buildx build --platform linux/amd64,linux/arm64 + -f docker/Dockerfile.prod .` command from the Quality gates table + locally before opening the PR.** PRs still have no image-build + gate. - Multi-arch (linux/amd64 + linux/arm64) via `docker/setup-qemu-action@v3` + `docker/setup-buildx-action@v3`. The arm64 leg runs under qemu on the amd64 GitHub-hosted runner — roughly 2x build time vs - native, acceptable for the release-only publish cadence. + native, acceptable for the tag + `:main` publish cadence. - Tag mapping via `docker/metadata-action@v5`: `X.Y.Z` tag → `:X.Y.Z` - + `:X.Y` + `:X` + `:latest`. `:latest` is gated on - `startsWith(github.ref, 'refs/tags/')` so a `workflow_dispatch` - from a non-tag ref can't accidentally claim it. There are no - rolling `:main` / `:sha-` tags — see the table in + + `:X.Y` + `:X` + `:latest`. Push to `main` → `:main`. `:latest` is + gated on `startsWith(github.ref, 'refs/tags/')` so a push or + `workflow_dispatch` from `main` cannot claim it. A + `workflow_dispatch` from a non-tag, non-main ref produces an empty + tag set and `docker push` fails loudly. See the table in `docs/src/content/docs/getting-started/quickstart-docker.mdx` - for the operator-facing tag list. A `workflow_dispatch` from a - non-tag ref produces an empty tag set and `docker push` fails - loudly (the documented "rebuilding a non-released ref isn't a - meaningful operation" gate). + for the operator-facing tag list. - Sigstore cosign signs each tag against the immutable digest (`@`, not the mutable `:`) in keyless / OIDC mode. The job-level `id-token: write` permission is what @@ -413,8 +407,8 @@ Prod Docker image specifics: identity (workflow path) and the issuer (GitHub Actions OIDC endpoint) — see `docs/src/content/docs/getting-started/quickstart-docker.mdx` for the canonical `cosign verify` command. The cosign step is - always-on (no PR exemption is needed — PRs don't trigger the - workflow at all under the tag-only contract). + always-on (no PR exemption is needed. PRs do not trigger this + workflow). - No local `./sbpp.sh` wrapper. The dev stack doesn't ship a way to invoke `docker buildx` from inside the dev container itself (no Docker-in-Docker), and the prod image build is a host-side @@ -3998,7 +3992,7 @@ the spec, target a 1920px viewport, not 1440px. dependency on `Actions.*` — exactly the asymmetry the bug reporter saw. **The bug only surfaces under `docker/Dockerfile.prod` → - `ghcr.io/sbpp/sourcebans-pp:*`** (the dev compose stack + `ghcr.io/srcdslab/sourcebans-pp:*`** (the dev compose stack uses a different Apache config; tarball installs don't ship Apache at all). When extending the deny list, prefer `` for root-only configs that have a @@ -4841,7 +4835,7 @@ the spec, target a 1920px viewport, not 1440px. | Need to … | Look at | | -------------------------------------- | -------------------------------------------------------- | | Understand request lifecycle | `ARCHITECTURE.md` ("Page request lifecycle" / "JSON API request lifecycle") | -| Edit a docs page or add a new one (the Astro + Starlight site published at sbpp.github.io) | `docs/src/content/docs//.md` (or `.mdx` when the page uses tabs / cards / asides — e.g. `getting-started/quickstart.mdx`, `setup/mariadb.mdx`). New pages also need a sidebar entry in `docs/astro.config.mjs` (the `sidebar:` array). Site config + theme tokens live in `docs/astro.config.mjs` + `docs/src/styles/sbpp.css`. The Starlight chrome ships from `@astrojs/starlight`; layout overrides land under `docs/src/components/` (see `ThemeProvider.astro` for the canonical override shape). Local dev: `cd docs && npm install && npm run dev`. CI gates: `.github/workflows/docs-build.yml` (per-PR build), `docs-deploy-trigger.yml` (main → repository_dispatch into sbpp.github.io), `docs-screenshots.yml` (gated on the `affects-ui` label, runs `docs/scripts/capture.mjs`). Source of truth is here; sbpp.github.io is the deploy shell only (#1333). | +| Edit a docs page or add a new one (the Astro + Starlight site published at sbpp.github.io) | `docs/src/content/docs//.md` (or `.mdx` when the page uses tabs / cards / asides — e.g. `getting-started/quickstart.mdx`, `setup/mariadb.mdx`). New pages also need a sidebar entry in `docs/astro.config.mjs` (the `sidebar:` array). Site config + theme tokens live in `docs/astro.config.mjs` + `docs/src/styles/sbpp.css`. The Starlight chrome ships from `@astrojs/starlight`; layout overrides land under `docs/src/components/` (see `ThemeProvider.astro` for the canonical override shape). Local dev: `cd docs && npm install && npm run dev`. CI gates: `.github/workflows/docs-build.yml` (per-PR build), `docs-deploy-trigger.yml` (no-op in this repo; no Pages sibling), `docs-screenshots.yml` (gated on the `affects-ui` label, runs `docs/scripts/capture.mjs`). Source of truth is the `docs/` tree. | | Refresh installer / panel screenshots used in docs pages | `docs/scripts/capture.mjs` (Playwright; `npm run capture` in `docs/`). Output lands under `docs/src/assets/auto/{install,panel}/.png` so docs pages keep referencing the same path across runs. CI does this automatically on PRs labelled `affects-ui`; locally run after `./sbpp.sh up`. STEAM_API_KEY is the all-zero dummy `00000000000000000000000000000000`. | | Add a JSON action | `web/api/handlers/_register.php` + `web/api/handlers/.php` | | Soft-retire / hard-delete admins, keep ban+comm issuer names, or bulk-select on the admins list (#1509) | Soft-retire: `admins.enabled` + `admins.deactivate` / `admins.reactivate` in `web/api/handlers/admins.php` (Active/Inactive chips + dialogs in `page_admin_admins_list.tpl`). Hard delete still snapshots `bans.admin_name` / `comms.admin_name` before DELETE (migration `811.php`). Issuer display: `COALESCE(NULLIF(*.admin_name, ''), AD.user)` → template paints **Unknown**, never "deleted admin" on the Admin cell (comments still say "deleted admin" per #1500). Bulk: `admins.bulk` (`op` = `deactivate` \| `reactivate` \| `remove` \| `set_web_group` \| `set_srv_group`, partial `applied`/`skipped`) + checkbox column / sticky bar in `page_admin_admins_list.tpl`. Guards: no self on deactivate/remove; owners skipped. Tests: `AdminsTest` + `AdminEnabledAttributionTest` + `admin-deactivate-bulk.spec.ts`. | @@ -4943,5 +4937,5 @@ the spec, target a 1920px viewport, not 1440px. | Run a stack in parallel with another worktree | Worktree-local `docker-compose.override.yml` (see "Parallel stacks") | | Local dev stack details | `docker/README.md` | | Build / extend the production Docker image (multi-stage build, hardened runtime, entrypoint state machine, healthcheck) | `docker/Dockerfile.prod` (multi-stage: `builder` runs `composer install --no-dev` against `web/`; `runtime` carries pdo_mysql + intl + zip + mbstring + gmp ONLY — no nodejs / npm / git / dev-prepend) + `docker/php/prod-entrypoint.sh` (pure POSIX shell state machine: `*_FILE` secret resolution → DATABASE_URL parse → defaults → Apache config (PORT + mod_remoteip from `SBPP_TRUSTED_PROXIES`) → wait-for-DB → render config.php (only when missing) → first-boot install (schema + data + seed admin from `INITIAL_ADMIN_*` env vars) → headless updater migrations → strip install/ + updater/ from writable layer → ensure writable cache/templates_c/demos → `exec apache2-foreground`) + `docker/php/prod-php.ini` (production OPcache: `validate_timestamps=0`, `display_errors=Off`, `log_errors=On`, errors → `/dev/stderr`, UTC default, `expose_php=Off`) + `docker/apache/sbpp-prod.conf` (denies dotfiles + vendor/ + configs/ + includes/ + install/ + updater/ + cache/ + templates_c/ + config.php + composer.{json,lock}; `RemoteIPHeader X-Forwarded-For` for the trusted-proxy chain) + `web/health.php` (DB-aware unauthenticated healthcheck — `init.php` bootstraps the panel; `$GLOBALS['PDO']->query('SELECT 1')` returns 200 OK or 503 + plain-text reason; Cache-Control: no-store + X-Robots-Tag: noindex). The production image MUST NOT define `SBPP_DEV_KEEP_INSTALL`; the entrypoint's `strip_install_dirs` step is what makes the panel-runtime guard pass instead (#1381). | -| Deploy / configure the production Docker stack (compose, env vars, reverse-proxy) | `docker-compose.prod.yml` (pulls `ghcr.io/sbpp/sourcebans-pp:${SBPP_IMAGE_TAG:-latest}` — NOT a build context; DB port NOT exposed by default; commented `caddy:` service block for opt-in TLS) + `.env.example.prod` (every supported env var grouped by required / recommended / first-boot / optional / advanced; documents the `*_FILE` Docker-secret pattern and the `SBPP_CONFIG_PATH` Docker-secret-mount pattern; uses `${VAR:?...}` compose syntax for required vars so a fresh-deploy operator who forgot `SB_SECRET_KEY` / `DB_PASS` / `DB_ROOT_PASS` gets a useful container-startup error) + `docker/caddy/Caddyfile.example` (one-line `reverse_proxy web:80` + zstd/gzip encode + static-asset cache headers). Three persistent volumes: `dbdata` + `demos` (MUST persist — DB rows + uploaded ban-evidence); `cache` + `smarty` (CAN be ephemeral — rebuild on demand). Operators run `docker compose -f docker-compose.prod.yml up -d` from a directory carrying both files; upgrades are `docker compose pull && up -d` (image is immutable, entrypoint runs idempotent migrations on every boot, named volumes survive the swap) (#1381). | +| Deploy / configure the production Docker stack (compose, env vars, reverse-proxy) | `docker-compose.prod.yml` (pulls `ghcr.io/srcdslab/sourcebans-pp:${SBPP_IMAGE_TAG:-latest}` — NOT a build context; DB port NOT exposed by default; commented `caddy:` service block for opt-in TLS) + `.env.example.prod` (every supported env var grouped by required / recommended / first-boot / optional / advanced; documents the `*_FILE` Docker-secret pattern and the `SBPP_CONFIG_PATH` Docker-secret-mount pattern; uses `${VAR:?...}` compose syntax for required vars so a fresh-deploy operator who forgot `SB_SECRET_KEY` / `DB_PASS` / `DB_ROOT_PASS` gets a useful container-startup error) + `docker/caddy/Caddyfile.example` (one-line `reverse_proxy web:80` + zstd/gzip encode + static-asset cache headers). Three persistent volumes: `dbdata` + `demos` (MUST persist — DB rows + uploaded ban-evidence); `cache` + `smarty` (CAN be ephemeral — rebuild on demand). Operators run `docker compose -f docker-compose.prod.yml up -d` from a directory carrying both files; upgrades are `docker compose pull && up -d` (image is immutable, entrypoint runs idempotent migrations on every boot, named volumes survive the swap) (#1381). | | Honour `SBPP_CONFIG_PATH` so config.php can live outside the panel root (Docker-secret mount) | `sbpp_resolve_config_path()` in `web/init-recovery.php` is the single source of truth; `web/init.php` calls it to resolve the require-site. `web/install/already-installed.php`'s `sbpp_install_is_already_installed()` re-implements the env-var read inline (per its self-contained no-Composer docblock) so the wizard-side and runtime-side guards agree on the install-state sentinel path. Pre-#1381 both halves hard-coded the panel-root path; with a Docker-secret-mounted config the runtime would 302-to-/install/ while the wizard would happily start over. Regression tests: `testResolveConfigPathHonorsEnvVar` + `testWizardGuardHonorsConfigPathEnvVar` in `web/tests/integration/InstallGuardTest.php`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55506b938..27422105a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ the project's developer guides. - **[`ARCHITECTURE.md`](ARCHITECTURE.md)** — codebase tour: how the web panel boots, request lifecycle, database access patterns, and how the SourceMod plugins fit in. -- **[Docs site](https://sbpp.github.io/)** (sources under +- **[Docs site](docs/)** (sources under [`docs/`](docs/)) — self-hoster documentation (install, upgrade, configure, FAQ). Self-hoster-visible changes ship docs updates in the same PR. diff --git a/README.md b/README.md index 388b2e272..589f5a951 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@

- +
SourceBans++

- GitHub release + GitHub release License: Elastic 2.0 - GitHub issues - GitHub All Releases + GitHub issues + GitHub All Releases Discord

@@ -24,26 +24,26 @@ engine. ## Links -- **Docs:** (install, upgrade, configure, FAQ) -- **Releases:** -- **Issues:** +- **Docs:** sources under [`docs/`](docs/) (install, upgrade, configure, FAQ) +- **Releases:** +- **Issues:** - **Discord:** - **AlliedModders thread:** ## Install Download the latest `sourcebans-pp-X.Y.Z.webpanel-only.zip` from -[Releases](https://github.com/sbpp/sourcebans-pp/releases), unzip into +[Releases](https://github.com/srcdslab/sourcebans-pp/releases), unzip into your web root, and visit `/install/` in a browser. The wizard walks you through the rest. Full install + plugin setup guide: -[**Quickstart**](https://sbpp.github.io/getting-started/quickstart/). +[`docs/src/content/docs/getting-started/quickstart.mdx`](docs/src/content/docs/getting-started/quickstart.mdx). ## Upgrade -[**Updating SourceBans++**](https://sbpp.github.io/updating/) covers -the upgrade path for each major version boundary. +[`docs/src/content/docs/updating/index.md`](docs/src/content/docs/updating/index.md) +covers the upgrade path for each major version boundary. ## Contributing diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 23e3172aa..9d07efea2 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -37,7 +37,7 @@ services: web: - image: ghcr.io/sbpp/sourcebans-pp:${SBPP_IMAGE_TAG:-latest} + image: ghcr.io/srcdslab/sourcebans-pp:${SBPP_IMAGE_TAG:-latest} container_name: sbpp-prod-web restart: unless-stopped depends_on: diff --git a/docker/README.md b/docker/README.md index 2f1367d09..6ecbbcf19 100644 --- a/docker/README.md +++ b/docker/README.md @@ -8,9 +8,8 @@ up on the next request — no rebuilds needed. > **Dev or prod?** This page documents the **development** stack. For > the **production** install path (immutable image pulled from GHCR, > hardened entrypoint, no install wizard, multi-arch / cosign-signed) -> see the -> [Docker quickstart](https://sbpp.github.io/getting-started/quickstart-docker/) -> docs page plus `docker-compose.prod.yml` + `.env.example.prod` at the +> see `docs/src/content/docs/getting-started/quickstart-docker.mdx` +> plus `docker-compose.prod.yml` + `.env.example.prod` at the > repo root. The two stacks are deliberately separate — the dev stack > ships `admin/admin`, bind-mounts the worktree, defines > `SBPP_DEV_KEEP_INSTALL` (a panel-takeover guard bypass), and exposes diff --git a/docs/README.md b/docs/README.md index fbd025768..eb1eb25a1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -229,7 +229,7 @@ returns false. Create the label via the repo's Issues → Labels page ## Source of truth -These docs live in [`sbpp/sourcebans-pp` under `docs/`](https://github.com/sbpp/sourcebans-pp/tree/main/docs). -The site at is published from there by CI -on every merge to `main`. Open PRs against this directory; the deploy -shell repo doesn't accept content PRs anymore. +These docs live in [`srcdslab/sourcebans-pp` under `docs/`](https://github.com/srcdslab/sourcebans-pp/tree/main/docs). +There is no Pages sibling for this repo today. Open PRs against this +directory. `docs-build.yml` validates the Astro build; `docs-deploy-trigger.yml` +is a no-op until a Pages deploy is wired. diff --git a/docs/src/content/docs/configuring/announcements.mdx b/docs/src/content/docs/configuring/announcements.mdx index a693d45f7..4f5ef8cc4 100644 --- a/docs/src/content/docs/configuring/announcements.mdx +++ b/docs/src/content/docs/configuring/announcements.mdx @@ -75,7 +75,7 @@ visibility for things like: - A new SourceMod or MariaDB version requirement is shipping. Operators can audit content via the **public diff history** of the -[`docs/public/announcements.json`](https://github.com/sbpp/sourcebans-pp/blob/main/docs/public/announcements.json) +[`docs/public/announcements.json`](https://github.com/srcdslab/sourcebans-pp/blob/main/docs/public/announcements.json) file. Every change ships through a pull request with the same review process as the rest of the project. There's no separate announcements server or admin-only API endpoint to compromise. The @@ -112,20 +112,13 @@ it in a hook that runs later. ## How announcements are proposed -If you maintain a fork or care about a specific upcoming -announcement, the workflow is the same as any other change to the -docs site: - -1. Open a PR against - [`sbpp/sourcebans-pp`](https://github.com/sbpp/sourcebans-pp). -2. Edit `docs/public/announcements.json` to prepend your entry. - The panel sorts by `published_at`, but the file convention is - "newest at the top of the array" so reviewers see the most - relevant change first. -3. The maintainers will review and merge. The - [docs deploy trigger workflow](https://github.com/sbpp/sourcebans-pp/blob/main/.github/workflows/docs-deploy-trigger.yml) - ships the updated file to `https://sbpp.github.io/announcements.json` - within a few minutes of merge. +If you maintain a copy of the panel and want to ship your own +announcements, edit `docs/public/announcements.json` in this repo +and host the file at an `https://` URL. Point the panel at it with +`define('SB_ANNOUNCEMENTS_URL', 'https://…/announcements.json');` in +`config.php`. The default fetch URL is still +`https://sbpp.github.io/announcements.json`. This repo does not +publish a Pages sibling. The strict schema, audit history, and one-file source make this auditable: any operator with internet access can review every diff --git a/docs/src/content/docs/customization/translating.md b/docs/src/content/docs/customization/translating.md index 6314c373e..d3eb46784 100644 --- a/docs/src/content/docs/customization/translating.md +++ b/docs/src/content/docs/customization/translating.md @@ -54,7 +54,7 @@ translations, and select your new theme in the panel's settings. SourceBans++ uses **Smarty 5** under the hood, which dropped the `{php}` tag. If you find a `{php}` block in an old fork theme, replace it with -[`{load_template}`](https://github.com/sbpp/sourcebans-pp/blob/main/web/includes/SmartyCustomFunctions.php) +[`{load_template}`](https://github.com/srcdslab/sourcebans-pp/blob/main/web/includes/SmartyCustomFunctions.php) or move the logic into a PHP helper. The panel refuses to render templates that still contain `{php}`. ::: @@ -113,7 +113,7 @@ falls back to English. ## Contributing translations back We always welcome translation PRs against -[`sbpp/sourcebans-pp`](https://github.com/sbpp/sourcebans-pp), for +[`srcdslab/sourcebans-pp`](https://github.com/srcdslab/sourcebans-pp), for both panel theme translations and plugin phrase translations. For the panel, the practical path is to keep the default theme's diff --git a/docs/src/content/docs/faq/index.md b/docs/src/content/docs/faq/index.md index bfee39eee..3f5009a9d 100644 --- a/docs/src/content/docs/faq/index.md +++ b/docs/src/content/docs/faq/index.md @@ -50,15 +50,15 @@ We don't sell hosting, support, or plugins ourselves. - **Then:** our [Discord](https://discord.gg/tzqYqmAtF5) `#help-support` channel. - **Bug reports:** open a - [GitHub issue](https://github.com/sbpp/sourcebans-pp/issues) with + [GitHub issue](https://github.com/srcdslab/sourcebans-pp/issues) with reproduction steps. ### I want to contribute, where do I start? Take a stab at the issues labelled `good first issue` or `help wanted` -on the [issue tracker](https://github.com/sbpp/sourcebans-pp/issues). +on the [issue tracker](https://github.com/srcdslab/sourcebans-pp/issues). The -[`AGENTS.md`](https://github.com/sbpp/sourcebans-pp/blob/main/AGENTS.md) +[`AGENTS.md`](https://github.com/srcdslab/sourcebans-pp/blob/main/AGENTS.md) file in the repo root is the contributor cheatsheet: conventions, the local Docker dev stack, and the "where to find what" index. diff --git a/docs/src/content/docs/getting-started/overview.mdx b/docs/src/content/docs/getting-started/overview.mdx index 0c9789699..1520c9145 100644 --- a/docs/src/content/docs/getting-started/overview.mdx +++ b/docs/src/content/docs/getting-started/overview.mdx @@ -93,7 +93,7 @@ gets stood up. **Best when:** you own the host, want one-command upgrades (`docker compose pull && up -d`), automatic TLS, or reproducible deploys across machines. Ships a multi-arch - image (`ghcr.io/sbpp/sourcebans-pp`) signed with Sigstore cosign. + image (`ghcr.io/srcdslab/sourcebans-pp`) signed with Sigstore cosign. Step it out with the **[Docker quickstart](/getting-started/quickstart-docker/)**. @@ -109,7 +109,7 @@ gets stood up. injection, `DATABASE_URL` parsing, `/health.php` probes, and `*_FILE` secrets. Until the deploy buttons land, point any platform that runs container images at - `ghcr.io/sbpp/sourcebans-pp:latest` and set the env vars from + `ghcr.io/srcdslab/sourcebans-pp:latest` and set the env vars from the **[Docker quickstart](/getting-started/quickstart-docker/)**. diff --git a/docs/src/content/docs/getting-started/quickstart-docker.mdx b/docs/src/content/docs/getting-started/quickstart-docker.mdx index d50a7a007..6cae216c1 100644 --- a/docs/src/content/docs/getting-started/quickstart-docker.mdx +++ b/docs/src/content/docs/getting-started/quickstart-docker.mdx @@ -16,7 +16,7 @@ Compose** in a few minutes. By the end you'll have: - A MariaDB database persisted to a named Docker volume, - An initial admin account ready to log in. -The Docker image (`ghcr.io/sbpp/sourcebans-pp`) is a multi-arch build +The Docker image (`ghcr.io/srcdslab/sourcebans-pp`) is a multi-arch build (linux/amd64 + linux/arm64) signed with Sigstore cosign. It ships no install wizard, no developer tools, and runs the panel as the non-root `www-data` user. @@ -54,18 +54,18 @@ host. The image carries everything. ## Step 1 — Get the compose stack The compose file + env template + Caddy stub live in the -[sourcebans-pp repo](https://github.com/sbpp/sourcebans-pp). The +[sourcebans-pp repo](https://github.com/srcdslab/sourcebans-pp). The simplest path is a sparse fetch of the three files you actually need into a deployment directory of your own: ```sh mkdir -p ~/sourcebans-prod cd ~/sourcebans-prod -curl -O https://raw.githubusercontent.com/sbpp/sourcebans-pp/main/docker-compose.prod.yml -curl -O https://raw.githubusercontent.com/sbpp/sourcebans-pp/main/.env.example.prod +curl -O https://raw.githubusercontent.com/srcdslab/sourcebans-pp/main/docker-compose.prod.yml +curl -O https://raw.githubusercontent.com/srcdslab/sourcebans-pp/main/.env.example.prod mkdir -p docker/caddy curl -o docker/caddy/Caddyfile.example \ - https://raw.githubusercontent.com/sbpp/sourcebans-pp/main/docker/caddy/Caddyfile.example + https://raw.githubusercontent.com/srcdslab/sourcebans-pp/main/docker/caddy/Caddyfile.example ``` A `git clone` of the whole repo works too if you'd rather track @@ -289,18 +289,20 @@ Image tags: | `X.Y.Z` | A specific version. Recommended for production. | | `X.Y` | Floats forward across patch releases of `X.Y.*`. | | `X` | Floats forward across minor releases of `X.*.*`. | +| `main` | Tip of `main`. Rolling. Not a release. | -Only tagged release builds are published. There is no rolling -`:main` or per-commit `:sha-` tag. To test an unreleased fix, -`git checkout` the relevant ref and build the image locally with -`docker buildx build -f docker/Dockerfile.prod -t sbpp:dev .`. +Tagged release builds publish `:latest` plus the semver aliases. +Push-to-main publishes `:main` only. There is no per-commit +`:sha-` tag. To test an unreleased fix that is not yet on +`main`, `git checkout` the relevant ref and build the image locally +with `docker buildx build -f docker/Dockerfile.prod -t sbpp:dev .`. Every published tag is signed with Sigstore cosign in keyless mode. Verify the signature with: ```sh -cosign verify ghcr.io/sbpp/sourcebans-pp:1.7.0 \ - --certificate-identity-regexp='https://github.com/sbpp/sourcebans-pp/.github/workflows/docker-image.yml@.*' \ +cosign verify ghcr.io/srcdslab/sourcebans-pp:latest \ + --certificate-identity-regexp='https://github.com/srcdslab/sourcebans-pp/.github/workflows/docker-image.yml@.*' \ --certificate-oidc-issuer='https://token.actions.githubusercontent.com' ``` @@ -348,7 +350,7 @@ DigitalOcean App Platform, Railway, Render, and Fly.io. Until those land (tracked in [issue #1382](https://github.com/sbpp/sourcebans-pp/issues/1382)), deploy on those platforms by pointing the relevant service at the -`ghcr.io/sbpp/sourcebans-pp:latest` image and setting the same env +`ghcr.io/srcdslab/sourcebans-pp:latest` image and setting the same env vars from `.env.example.prod`. The image already speaks the platform-conventional knobs: diff --git a/docs/src/content/docs/getting-started/quickstart.mdx b/docs/src/content/docs/getting-started/quickstart.mdx index d4dd7db6d..5d069c642 100644 --- a/docs/src/content/docs/getting-started/quickstart.mdx +++ b/docs/src/content/docs/getting-started/quickstart.mdx @@ -40,7 +40,7 @@ You'll also want: ## Step 1 — Download Grab the latest release zip from the -[Releases page](https://github.com/sbpp/sourcebans-pp/releases). You +[Releases page](https://github.com/srcdslab/sourcebans-pp/releases). You want the `sourcebans-pp-X.Y.Z.webpanel-only.zip` (web side) and the `sourcebans-pp-X.Y.Z.plugin-only.zip` (game side), or the combined zip if you prefer. diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 412a6d8fe..524614781 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -14,7 +14,7 @@ hero: icon: right-arrow variant: primary - text: View on GitHub - link: https://github.com/sbpp/sourcebans-pp + link: https://github.com/srcdslab/sourcebans-pp icon: external variant: minimal --- diff --git a/docs/src/content/docs/sponsor.mdx b/docs/src/content/docs/sponsor.mdx index 0347702a6..f8d70a1e3 100644 --- a/docs/src/content/docs/sponsor.mdx +++ b/docs/src/content/docs/sponsor.mdx @@ -40,7 +40,7 @@ takes to land the modernization work shipping across the v2.x line. Money is one way; there are others. The project always welcomes: - **Bug reports with reproduction details** on - [GitHub Issues](https://github.com/sbpp/sourcebans-pp/issues). + [GitHub Issues](https://github.com/srcdslab/sourcebans-pp/issues). - **Pull requests**: see [`CONTRIBUTING.md`](https://github.com/srcdslab/sourcebans-pp/blob/main/CONTRIBUTING.md) for the guide. diff --git a/docs/src/content/docs/updating/1-8-to-2-0.mdx b/docs/src/content/docs/updating/1-8-to-2-0.mdx index e6bd12cc4..b85654c53 100644 --- a/docs/src/content/docs/updating/1-8-to-2-0.mdx +++ b/docs/src/content/docs/updating/1-8-to-2-0.mdx @@ -94,7 +94,7 @@ If you maintain a fork theme: 2. Port your fork against the v2.0 default theme. Diff `web/themes/default/` between the 1.8.x and 2.0 trees to see what changed. The new conventions live in - [`AGENTS.md`](https://github.com/sbpp/sourcebans-pp/blob/main/AGENTS.md) + [`AGENTS.md`](https://github.com/srcdslab/sourcebans-pp/blob/main/AGENTS.md) (typed View DTOs, the `?section=…` admin sub-route pattern, the `core/admin_sidebar.tpl` partial, empty-state classes in `theme.css`). @@ -143,7 +143,7 @@ deduplicated. - SMTP credentials, the Steam API key value. The complete field list lives in the source at -[`web/includes/Telemetry/schema-1.lock.json`](https://github.com/sbpp/sourcebans-pp/blob/main/web/includes/Telemetry/schema-1.lock.json). +[`web/includes/Telemetry/schema-1.lock.json`](https://github.com/srcdslab/sourcebans-pp/blob/main/web/includes/Telemetry/schema-1.lock.json). ### How to opt out @@ -217,7 +217,7 @@ Specifically **not** sent: - Nothing the panel pulls from `sb_settings` or any other table. The audit trail is the file in this repo at -[`docs/public/announcements.json`](https://github.com/sbpp/sourcebans-pp/blob/main/docs/public/announcements.json). +[`docs/public/announcements.json`](https://github.com/srcdslab/sourcebans-pp/blob/main/docs/public/announcements.json). Every announcement is a git commit before it lands on your dashboard, so operators can review the full history before the deploy chain ships it to `sbpp.github.io`. diff --git a/docs/src/content/docs/updating/index.md b/docs/src/content/docs/updating/index.md index a38e31ad5..8aa2ea9cd 100644 --- a/docs/src/content/docs/updating/index.md +++ b/docs/src/content/docs/updating/index.md @@ -29,7 +29,7 @@ button is enough. Don't skip this step. ## Download Grab the latest release zip from the -[Releases page](https://github.com/sbpp/sourcebans-pp/releases). You +[Releases page](https://github.com/srcdslab/sourcebans-pp/releases). You want the `sourcebans-pp-X.Y.Z.webpanel-only.zip` for the web side and `sourcebans-pp-X.Y.Z.plugin-only.zip` for the game side. @@ -130,7 +130,7 @@ need that value generated before they can log in. 4. If you use a custom theme, note that **Smarty 5 (which 1.7.0+ uses) dropped the `{php}` tag**. Custom themes that relied on `{php}` need to switch to the - [`{load_template}`](https://github.com/sbpp/sourcebans-pp/blob/main/web/includes/SmartyCustomFunctions.php) + [`{load_template}`](https://github.com/srcdslab/sourcebans-pp/blob/main/web/includes/SmartyCustomFunctions.php) tag instead. ### Upgrading the plugin from <= 1.5.4.7 diff --git a/web/api/handlers/system.php b/web/api/handlers/system.php index 9da76a2be..0f3087e88 100644 --- a/web/api/handlers/system.php +++ b/web/api/handlers/system.php @@ -20,7 +20,7 @@ function _api_system_release_upstream_url(): string if (defined('SB_RELEASE_LATEST_URL') && is_string(SB_RELEASE_LATEST_URL) && SB_RELEASE_LATEST_URL !== '') { return SB_RELEASE_LATEST_URL; } - return 'https://api.github.com/repos/sbpp/sourcebans-pp/releases/latest'; + return 'https://api.github.com/repos/srcdslab/sourcebans-pp/releases/latest'; } /** @@ -198,7 +198,7 @@ function _api_system_release_format(string $tagName, string $htmlUrl, string $lo /** * Public action: report whether a newer SourceBans++ release is - * available. Sources from `api.github.com/repos/sbpp/sourcebans-pp/releases/latest` + * available. Sources from `api.github.com/repos/srcdslab/sourcebans-pp/releases/latest` * with a 1-day on-disk cache + stale-while-error fallback (the cached * payload is served regardless of TTL when the upstream call fails) so a * busy panel can't blow through GitHub's 60 req/hr unauthenticated limit diff --git a/web/composer.json b/web/composer.json index f96b9620c..9c1e2cdc9 100644 --- a/web/composer.json +++ b/web/composer.json @@ -1,17 +1,17 @@ { - "name": "sbpp/sourcebans-pp", + "name": "srcdslab/sourcebans-pp", "description": "Admin, ban, and comms management system for the Source engine.", "type": "project", "keywords": [], - "homepage": "https://sbpp.github.io", - "readme": "https://github.com/sbpp/sourcebans-pp/blob/main/README.md", + "homepage": "https://github.com/srcdslab/sourcebans-pp", + "readme": "https://github.com/srcdslab/sourcebans-pp/blob/main/README.md", "license": "Elastic-2.0", "support": { - "issues": "https://github.com/sbpp/sourcebans-pp/issues", + "issues": "https://github.com/srcdslab/sourcebans-pp/issues", "forum": "https://forums.alliedmods.net/forumdisplay.php?f=152", - "wiki": "https://sbpp.github.io/faq/", - "source": "https://github.com/sbpp/sourcebans-pp", - "docs": "https://sbpp.github.io/docs/", + "wiki": "https://github.com/srcdslab/sourcebans-pp/tree/main/docs", + "source": "https://github.com/srcdslab/sourcebans-pp", + "docs": "https://github.com/srcdslab/sourcebans-pp/tree/main/docs", "chat": "https://discord.gg/tzqYqmAtF5" }, "config": { diff --git a/web/init-recovery.php b/web/init-recovery.php index b1ae4b5a1..15643419f 100644 --- a/web/init-recovery.php +++ b/web/init-recovery.php @@ -170,7 +170,7 @@ function sbpp_render_install_blocked_page(string $scenario): never 'heading' => 'How to fix it', 'body' => '

Easiest — download the release zip:

' . '
    ' - . '
  1. Grab the latest sourcebans-pp-X.Y.Z.webpanel-only.zip from the releases page.
  2. ' + . '
  3. Grab the latest sourcebans-pp-X.Y.Z.webpanel-only.zip from the releases page.
  4. ' . '
  5. Extract it on your computer. Make sure web/includes/vendor/ exists in the extracted tree.
  6. ' . '
  7. Re-upload the contents to your web root, overwriting the current files.
  8. ' . '
  9. Reload this page.
  10. ' diff --git a/web/install/recovery.php b/web/install/recovery.php index 0b72c93e6..cfcfb175b 100644 --- a/web/install/recovery.php +++ b/web/install/recovery.php @@ -189,7 +189,7 @@
  11. Grab the latest sourcebans-pp-X.Y.Z.webpanel-only.zip from - the releases page.
  12. diff --git a/web/scripts/api-contract.js b/web/scripts/api-contract.js index 302ef9911..b749a2f73 100644 --- a/web/scripts/api-contract.js +++ b/web/scripts/api-contract.js @@ -567,7 +567,7 @@ */ /** * Public action: report whether a newer SourceBans++ release is available. - * Sources from `api.github.com/repos/sbpp/sourcebans-pp/releases/latest` with + * Sources from `api.github.com/repos/srcdslab/sourcebans-pp/releases/latest` with * a 1-day on-disk cache + stale-while-error fallback (the cached payload is * served regardless of TTL when the upstream call fails) so a busy panel can't * blow through GitHub's 60 req/hr unauthenticated limit and a transient GitHub diff --git a/web/tests/api/SystemTest.php b/web/tests/api/SystemTest.php index cc089d3cb..1e4e05fa5 100644 --- a/web/tests/api/SystemTest.php +++ b/web/tests/api/SystemTest.php @@ -32,7 +32,7 @@ public function testCheckVersionIsPublicAndReturnsShape(): void $this->assertTrue($env['ok']); $this->assertSame('1.8.4', $env['data']['release_latest']); $this->assertSame( - 'https://github.com/sbpp/sourcebans-pp/releases/tag/1.8.4', + 'https://github.com/srcdslab/sourcebans-pp/releases/tag/1.8.4', $env['data']['release_url'] ); $this->assertTrue($env['data']['release_update']); @@ -124,7 +124,7 @@ public function testCheckVersionStaleCacheServedWhenUpstreamUnreachable(): void $this->assertTrue($env['ok']); $this->assertSame('1.7.2', $env['data']['release_latest']); $this->assertSame( - 'https://github.com/sbpp/sourcebans-pp/releases/tag/1.7.2', + 'https://github.com/srcdslab/sourcebans-pp/releases/tag/1.7.2', $env['data']['release_url'] ); $this->assertNotSame('Error', $env['data']['release_latest']); @@ -154,13 +154,13 @@ public function testCheckVersionDevSentinelShortCircuitsBeforeVersionCompare(): // `Sbpp\Tests\Api` namespace. $resp = \_api_system_release_format( '1.8.4', - 'https://github.com/sbpp/sourcebans-pp/releases/tag/1.8.4', + 'https://github.com/srcdslab/sourcebans-pp/releases/tag/1.8.4', 'dev' ); $this->assertSame('1.8.4', $resp['release_latest']); $this->assertSame( - 'https://github.com/sbpp/sourcebans-pp/releases/tag/1.8.4', + 'https://github.com/srcdslab/sourcebans-pp/releases/tag/1.8.4', $resp['release_url'] ); $this->assertSame('Tracking development build.', $resp['release_msg']); @@ -196,7 +196,7 @@ private function primeReleaseCache(string $tagName, ?int $cachedAt = null): void } file_put_contents($cache . 'github_release_latest.json', (string) json_encode([ 'tag_name' => $tagName, - 'html_url' => 'https://github.com/sbpp/sourcebans-pp/releases/tag/' . $tagName, + 'html_url' => 'https://github.com/srcdslab/sourcebans-pp/releases/tag/' . $tagName, 'cached_at' => $cachedAt ?? time(), ])); } diff --git a/web/tests/api/__snapshots__/system/check_version_latest_release.json b/web/tests/api/__snapshots__/system/check_version_latest_release.json index 1d5654548..83d54ae53 100644 --- a/web/tests/api/__snapshots__/system/check_version_latest_release.json +++ b/web/tests/api/__snapshots__/system/check_version_latest_release.json @@ -2,7 +2,7 @@ "ok": true, "data": { "release_latest": "test", - "release_url": "https://github.com/sbpp/sourcebans-pp/releases/tag/test", + "release_url": "https://github.com/srcdslab/sourcebans-pp/releases/tag/test", "release_msg": "You have the Latest Release.", "release_update": false } diff --git a/web/tests/api/__snapshots__/system/check_version_stale_while_error.json b/web/tests/api/__snapshots__/system/check_version_stale_while_error.json index df466f6fe..175b4fc9a 100644 --- a/web/tests/api/__snapshots__/system/check_version_stale_while_error.json +++ b/web/tests/api/__snapshots__/system/check_version_stale_while_error.json @@ -2,7 +2,7 @@ "ok": true, "data": { "release_latest": "1.7.2", - "release_url": "https://github.com/sbpp/sourcebans-pp/releases/tag/1.7.2", + "release_url": "https://github.com/srcdslab/sourcebans-pp/releases/tag/1.7.2", "release_msg": "A New Release is Available.", "release_update": true } diff --git a/web/tests/api/__snapshots__/system/check_version_update_available.json b/web/tests/api/__snapshots__/system/check_version_update_available.json index 116b88de5..3c48ac6d2 100644 --- a/web/tests/api/__snapshots__/system/check_version_update_available.json +++ b/web/tests/api/__snapshots__/system/check_version_update_available.json @@ -2,7 +2,7 @@ "ok": true, "data": { "release_latest": "1.8.4", - "release_url": "https://github.com/sbpp/sourcebans-pp/releases/tag/1.8.4", + "release_url": "https://github.com/srcdslab/sourcebans-pp/releases/tag/1.8.4", "release_msg": "A New Release is Available.", "release_update": true } diff --git a/web/tests/e2e/scripts/upload-screenshots.sh b/web/tests/e2e/scripts/upload-screenshots.sh index 0554d2ed3..7515b396d 100755 --- a/web/tests/e2e/scripts/upload-screenshots.sh +++ b/web/tests/e2e/scripts/upload-screenshots.sh @@ -25,7 +25,7 @@ SLUG="${2:?usage: upload-screenshots.sh }" REPO_ROOT="$(git rev-parse --show-toplevel)" SHOTS_DIR="$REPO_ROOT/web/tests/e2e/screenshots" -RAW_BASE="https://raw.githubusercontent.com/sbpp/sourcebans-pp/screenshots-archive/screenshots/pr-${PR}/${SLUG}" +RAW_BASE="https://raw.githubusercontent.com/srcdslab/sourcebans-pp/screenshots-archive/screenshots/pr-${PR}/${SLUG}" # 1. Run the gallery spec. ( diff --git a/web/tests/e2e/specs/smoke/routing-truthiness.spec.ts b/web/tests/e2e/specs/smoke/routing-truthiness.spec.ts index c0a132a3d..330283892 100644 --- a/web/tests/e2e/specs/smoke/routing-truthiness.spec.ts +++ b/web/tests/e2e/specs/smoke/routing-truthiness.spec.ts @@ -234,9 +234,9 @@ test.describe('#1207 routing + truthiness fixes', () => { // Locate the link by its containing footer + the // attribute we ship as the canonical contract — never by // visible text alone. - const link = page.locator('footer.sbpp-footer a[href*="github.com/sbpp/sourcebans-pp"]'); + const link = page.locator('footer.sbpp-footer a[href*="github.com/srcdslab/sourcebans-pp"]'); await expect(link).toBeVisible(); - await expect(link).toHaveAttribute('href', 'https://github.com/sbpp/sourcebans-pp'); + await expect(link).toHaveAttribute('href', 'https://github.com/srcdslab/sourcebans-pp'); await expect(link).toHaveAttribute('target', '_blank'); await expect(link).toHaveAttribute('rel', /noopener/); diff --git a/web/tests/integration/ProdApacheConfigTest.php b/web/tests/integration/ProdApacheConfigTest.php index 03c86812b..17326f809 100644 --- a/web/tests/integration/ProdApacheConfigTest.php +++ b/web/tests/integration/ProdApacheConfigTest.php @@ -8,7 +8,7 @@ /** * Issue #1419: production Docker image (`docker/Dockerfile.prod` → - * `ghcr.io/sbpp/sourcebans-pp:*`) ships a hardening Apache conf + * `ghcr.io/srcdslab/sourcebans-pp:*`) ships a hardening Apache conf * (`docker/apache/sbpp-prod.conf`) that uses `` / * `` to deny access to root-level config files * (`composer.json`, `phpstan.*`, `phpunit.xml*`, `package.json`, diff --git a/web/tests/integration/ThemeConfParseTest.php b/web/tests/integration/ThemeConfParseTest.php index 0f1731149..2decf1bd8 100644 --- a/web/tests/integration/ThemeConfParseTest.php +++ b/web/tests/integration/ThemeConfParseTest.php @@ -22,7 +22,7 @@ public function testDefaultThemeConfParsesSingleQuotedDefines(): void $this->assertSame('SourceBans++ Default', ThemeConf::parseDefine($src, 'theme_name', '')); $this->assertSame('SourceBans++ Dev Team', ThemeConf::parseDefine($src, 'theme_author', 'Unknown')); $this->assertSame('2.0.0', ThemeConf::parseDefine($src, 'theme_version', '?')); - $this->assertSame('https://github.com/sbpp/sourcebans-pp', ThemeConf::parseDefine($src, 'theme_link', '')); + $this->assertSame('https://github.com/srcdslab/sourcebans-pp', ThemeConf::parseDefine($src, 'theme_link', '')); $this->assertSame('screenshot.jpg', ThemeConf::parseDefine($src, 'theme_screenshot', '')); } @@ -106,7 +106,7 @@ public function testDefaultThemeDiscoveryRowMatchesManifest(): void $this->assertSame('SourceBans++ Dev Team', $row['author']); $this->assertSame('2.0.0', $row['version']); - $this->assertSame('https://github.com/sbpp/sourcebans-pp', $row['link']); + $this->assertSame('https://github.com/srcdslab/sourcebans-pp', $row['link']); $this->assertStringContainsString( 'themes/' . $filename . '/', 'themes/' . $filename . '/' . ThemeConf::sanitizeScreenshotFilename( diff --git a/web/themes/default/core/footer.tpl b/web/themes/default/core/footer.tpl index a56e49839..e2ad84807 100644 --- a/web/themes/default/core/footer.tpl +++ b/web/themes/default/core/footer.tpl @@ -89,7 +89,7 @@ as overlapping the credit on mobile. *}