Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d1d8cce46 | ||
|
|
3c3bb5dfbf | ||
|
|
13a7fd9d19 | ||
|
|
4fd43cd17d | ||
|
|
110e7fcd78 | ||
|
|
f3d85f5239 | ||
|
|
9a9d7e3787 |
+50
@@ -0,0 +1,50 @@
|
|||||||
|
# Dotcom Arcade: notes for agents
|
||||||
|
|
||||||
|
Dotcom Arcade is a heavily modified fork of
|
||||||
|
[flashpoint-web](https://github.com/darkraise/flashpoint-web) by darkraise
|
||||||
|
(GPL-3.0), with game streaming added under `streaming/`. Coding standards and
|
||||||
|
architecture notes are in `CLAUDE.md`; feature and deployment docs are in
|
||||||
|
`docs/`.
|
||||||
|
|
||||||
|
## Upstream
|
||||||
|
|
||||||
|
- **Upstream** is https://github.com/darkraise/flashpoint-web, branch `master`.
|
||||||
|
It's read-only from here: don't push to it or open pull requests there unless
|
||||||
|
asked.
|
||||||
|
- **Most of upstream's code is still used:** the backend, the frontend's data
|
||||||
|
layer, the player, and the account and admin pages. Fixes to these should come
|
||||||
|
in from upstream rather than being rewritten here.
|
||||||
|
- **To see what upstream has changed** since it was last merged, and how each
|
||||||
|
touched file stands here, run `scripts/upstream-status.sh`.
|
||||||
|
- **To pull changes in**, follow `docs/08-development/upstream-sync.md`. In
|
||||||
|
short:
|
||||||
|
- merge upstream's `master` on a branch (don't cherry-pick);
|
||||||
|
- keep files removed here removed;
|
||||||
|
- keep the Dotcom Arcade branding;
|
||||||
|
- keep upstream's migration file names;
|
||||||
|
- have the pull request merged with a merge commit, never squashed, so the
|
||||||
|
next sync starts from the right place.
|
||||||
|
- **Parts that are only ours:**
|
||||||
|
- `streaming/`;
|
||||||
|
- `scripts/`;
|
||||||
|
- `frontend/src/components/portal/`;
|
||||||
|
- `views/GamePage.tsx`;
|
||||||
|
- `lib/saves/flashSaves.ts`;
|
||||||
|
- the discovery backend;
|
||||||
|
- `.gitea/`;
|
||||||
|
- migrations upstream doesn't have.
|
||||||
|
|
||||||
|
## GitHub mirror
|
||||||
|
|
||||||
|
`mirror/` publishes `main` to a read-only GitHub mirror from the operator's
|
||||||
|
host, never from a session; see `docs/08-development/github-mirror.md`. GitHub
|
||||||
|
runs `.github/workflows`, the forge `.gitea/workflows`; keep the two image
|
||||||
|
workflows in step.
|
||||||
|
|
||||||
|
## CI
|
||||||
|
|
||||||
|
Workflows are in `.gitea/workflows`. Jobs that run docker builds use the
|
||||||
|
`dejima-privileged` runner label; anything else uses `dejima-confined`. The
|
||||||
|
privileged job image has no Docker client, so install `docker-ce-cli` first.
|
||||||
|
Never hardcode the forge's host name: read it at run time, or take it from the
|
||||||
|
`REGISTRY_HOST` repository variable.
|
||||||
+1
-1
@@ -13,7 +13,7 @@ JWT_SECRET=replace-with-a-secure-random-string
|
|||||||
# Built from source by default. To pull published images instead, set the
|
# Built from source by default. To pull published images instead, set the
|
||||||
# registry path they live under (ending in /), then:
|
# registry path they live under (ending in /), then:
|
||||||
# docker compose pull && docker compose up -d
|
# docker compose pull && docker compose up -d
|
||||||
# IMAGE_PREFIX=registry.example.com/owner/
|
# IMAGE_PREFIX=ghcr.io/branderson/
|
||||||
# IMAGE_TAG=latest
|
# IMAGE_TAG=latest
|
||||||
# "Latest release" API URL to check for updates (GitHub or Gitea); off when unset
|
# "Latest release" API URL to check for updates (GitHub or Gitea); off when unset
|
||||||
# APP_UPDATE_URL=https://git.example.com/api/v1/repos/owner/dotcom-arcade/releases/latest
|
# APP_UPDATE_URL=https://git.example.com/api/v1/repos/owner/dotcom-arcade/releases/latest
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Builds the app, session manager and streamer images on the GitHub mirror and
|
||||||
|
# pushes them to GitHub's container registry, under the repository's owner.
|
||||||
|
# The forge runs .gitea/workflows/images.yml instead; Gitea ignores this
|
||||||
|
# directory while .gitea/workflows exists.
|
||||||
|
name: Images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
tags: ['v*.*.*']
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: ${{ matrix.name }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: app
|
||||||
|
image: dotcom-arcade
|
||||||
|
context: .
|
||||||
|
- name: session-manager
|
||||||
|
image: dotcom-arcade-session-manager
|
||||||
|
context: streaming/session-manager
|
||||||
|
- name: streamer
|
||||||
|
image: dotcom-arcade-streamer
|
||||||
|
context: streaming/streamer
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Lowercase the owner
|
||||||
|
id: owner
|
||||||
|
shell: bash
|
||||||
|
run: echo "name=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ghcr.io/${{ steps.owner.outputs.name }}/${{ matrix.image }}
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
type=ref,event=branch
|
||||||
|
|
||||||
|
- uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: ${{ matrix.context }}
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
platforms: linux/amd64
|
||||||
|
build-args: |
|
||||||
|
VERSION=${{ steps.meta.outputs.version }}
|
||||||
|
provenance: false
|
||||||
+15
-105
@@ -1,44 +1,11 @@
|
|||||||
# Contributing to Dotcom Arcade
|
# Contributing to Dotcom Arcade
|
||||||
|
|
||||||
Thanks for your interest in contributing! We welcome bug fixes, features, documentation improvements, and questions.
|
Dotcom Arcade is developed on a private forge and published to GitHub as a
|
||||||
|
read-only mirror, so issues and pull requests aren't accepted there. You're
|
||||||
|
welcome to fork it under the [GPL-3.0](LICENSE).
|
||||||
|
|
||||||
## What is Dotcom Arcade?
|
It's built on [flashpoint-web](https://github.com/darkraise/flashpoint-web).
|
||||||
|
Fixes to code the two still share may belong upstream.
|
||||||
A self-hosted web application for browsing and playing games from [Flashpoint Archive](https://flashpointarchive.org/).
|
|
||||||
|
|
||||||
**Tech Stack:** Node.js 20+, Express, React 18, TypeScript, SQLite, TanStack Query, Tailwind CSS
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Where to Start](#where-to-start)
|
|
||||||
- [Getting Started](#getting-started)
|
|
||||||
- [Development Setup](#development-setup)
|
|
||||||
- [Running Tests](#running-tests)
|
|
||||||
- [Making Changes](#making-changes)
|
|
||||||
- [Submitting a Pull Request](#submitting-a-pull-request)
|
|
||||||
- [Code Style](#code-style)
|
|
||||||
- [Reporting Bugs](#reporting-bugs)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
## Where to Start
|
|
||||||
|
|
||||||
- Browse issues labeled [`good first issue`](../../labels/good%20first%20issue) or [`help wanted`](../../labels/help%20wanted)
|
|
||||||
- Check the [project documentation](docs/) for architecture and patterns
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Fork the repository on GitHub, then clone your fork
|
|
||||||
git clone https://github.com/YOUR_USERNAME/flashpoint-web.git
|
|
||||||
cd flashpoint-web
|
|
||||||
|
|
||||||
# Add upstream remote
|
|
||||||
git remote add upstream https://github.com/ORIGINAL_OWNER/flashpoint-web.git
|
|
||||||
|
|
||||||
# Keep your fork synced
|
|
||||||
git fetch upstream
|
|
||||||
git rebase upstream/master
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
@@ -47,6 +14,7 @@ git rebase upstream/master
|
|||||||
```
|
```
|
||||||
backend/ # Express REST API + game content serving (:3100)
|
backend/ # Express REST API + game content serving (:3100)
|
||||||
frontend/ # React + Vite application (:5173)
|
frontend/ # React + Vite application (:5173)
|
||||||
|
streaming/ # Session manager and streamer for games browsers can't run
|
||||||
docs/ # Documentation
|
docs/ # Documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -69,6 +37,7 @@ The app (UI and API) runs on `http://localhost:80`.
|
|||||||
### Option 2: Manual Setup (Recommended for Development)
|
### Option 2: Manual Setup (Recommended for Development)
|
||||||
|
|
||||||
**Prerequisites:**
|
**Prerequisites:**
|
||||||
|
|
||||||
- Node.js 20+
|
- Node.js 20+
|
||||||
- Flashpoint Archive installed locally
|
- Flashpoint Archive installed locally
|
||||||
|
|
||||||
@@ -99,80 +68,21 @@ cd backend && npm test -- --coverage
|
|||||||
cd frontend && npm test
|
cd frontend && npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Making Changes
|
|
||||||
|
|
||||||
### Branch Naming
|
|
||||||
|
|
||||||
```
|
|
||||||
feat/add-playlist-export
|
|
||||||
fix/game-search-pagination
|
|
||||||
refactor/auth-service
|
|
||||||
docs/update-api-reference
|
|
||||||
```
|
|
||||||
|
|
||||||
### Commit Messages
|
|
||||||
|
|
||||||
Follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
||||||
|
|
||||||
```
|
|
||||||
<type>(<scope>): <description>
|
|
||||||
|
|
||||||
feat(games): add advanced search filters
|
|
||||||
fix(auth): resolve token refresh race condition
|
|
||||||
docs: update deployment guide
|
|
||||||
refactor(api): simplify error handling
|
|
||||||
```
|
|
||||||
|
|
||||||
**Types:** `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
1. Create a branch from `master`
|
|
||||||
2. Make your changes
|
|
||||||
3. Run `npm run typecheck` and `npm test`
|
|
||||||
4. Update documentation if needed
|
|
||||||
5. Submit a pull request
|
|
||||||
|
|
||||||
## Submitting a Pull Request
|
|
||||||
|
|
||||||
### Before Submitting
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git fetch upstream
|
|
||||||
git rebase upstream/master
|
|
||||||
npm run typecheck
|
|
||||||
npm run format
|
|
||||||
npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### PR Guidelines
|
|
||||||
|
|
||||||
- Keep PRs focused and reasonably sized
|
|
||||||
- Reference related issues (`Fixes #123`)
|
|
||||||
- Include a clear description of changes
|
|
||||||
- Update documentation for new features
|
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
See [CLAUDE.md](CLAUDE.md) for detailed coding standards. Key points:
|
See [CLAUDE.md](CLAUDE.md) for detailed coding standards. Key points:
|
||||||
|
|
||||||
- **TypeScript:** No `any` types, use `unknown` or proper interfaces
|
- **TypeScript:** No `any` types, use `unknown` or proper interfaces
|
||||||
- **Backend:** Wrap async handlers with `asyncHandler()`, static routes before parameterized
|
- **Backend:** Wrap async handlers with `asyncHandler()`, static routes before
|
||||||
- **Frontend:** Use `@/` imports, all API calls through `lib/api.ts`, use theme tokens
|
parameterized
|
||||||
|
- **Frontend:** Use `@/` imports, all API calls through `lib/api.ts`, use theme
|
||||||
|
tokens
|
||||||
- **General:** Validate inputs, handle errors properly, clean up resources
|
- **General:** Validate inputs, handle errors properly, clean up resources
|
||||||
|
|
||||||
Run `npm run format` before committing.
|
Run `npm run format` before committing. Commit messages follow
|
||||||
|
[Conventional Commits](https://www.conventionalcommits.org/).
|
||||||
## Reporting Bugs
|
|
||||||
|
|
||||||
Before reporting, search [existing issues](../../issues) to avoid duplicates.
|
|
||||||
|
|
||||||
Include:
|
|
||||||
- Steps to reproduce
|
|
||||||
- Expected vs actual behavior
|
|
||||||
- Environment details (OS, Node version, browser)
|
|
||||||
- Screenshots or logs if applicable
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under [GPL-3.0](LICENSE). By contributing, you agree that your contributions will be licensed under the same license.
|
This project is licensed under [GPL-3.0](LICENSE). By contributing, you agree
|
||||||
|
that your contributions will be licensed under the same license.
|
||||||
|
|||||||
@@ -135,6 +135,9 @@ PGID=1000
|
|||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To pull prebuilt images instead of building them, see
|
||||||
|
[prebuilt images](docs/09-deployment/docker-deployment.md#prebuilt-images).
|
||||||
|
|
||||||
Open `http://<server>/` and create the admin account. On first start the app
|
Open `http://<server>/` and create the admin account. On first start the app
|
||||||
indexes the collection, which takes a few minutes on a full Ultimate install.
|
indexes the collection, which takes a few minutes on a full Ultimate install.
|
||||||
Under **Settings → App** you can set the site name and tagline.
|
Under **Settings → App** you can set the site name and tagline.
|
||||||
@@ -192,6 +195,11 @@ and development. Useful starting points:
|
|||||||
- [Ruffle](https://ruffle.rs/), for running Flash in modern browsers.
|
- [Ruffle](https://ruffle.rs/), for running Flash in modern browsers.
|
||||||
- [Selkies](https://github.com/selkies-project/selkies), which streams the
|
- [Selkies](https://github.com/selkies-project/selkies), which streams the
|
||||||
games.
|
games.
|
||||||
|
- [Texgriddy](https://github.com/Weestuarty/texgriddy-es-de) by Weestuarty, an
|
||||||
|
ES-DE theme that inspired the site's look. The look was recreated from scratch
|
||||||
|
in CSS; none of Texgriddy's files, images or fonts are used.
|
||||||
|
- [Fredoka](https://fontsource.org/fonts/fredoka) (SIL Open Font License), the
|
||||||
|
display font.
|
||||||
|
|
||||||
## Legal
|
## Legal
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ the Dotcom Arcade application.
|
|||||||
5. **[Testing Guide](./testing-guide.md)** - Testing approach and best practices
|
5. **[Testing Guide](./testing-guide.md)** - Testing approach and best practices
|
||||||
6. **[Debugging Guide](./debugging.md)** - Debugging tips and troubleshooting
|
6. **[Debugging Guide](./debugging.md)** - Debugging tips and troubleshooting
|
||||||
7. **[Common Pitfalls](./common-pitfalls.md)** - Known issues and solutions
|
7. **[Common Pitfalls](./common-pitfalls.md)** - Known issues and solutions
|
||||||
|
8. **[Upstream Sync](./upstream-sync.md)** - Merging changes from flashpoint-web
|
||||||
|
9. **[GitHub Mirror](./github-mirror.md)** - Publishing to GitHub and GHCR
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# GitHub mirror
|
||||||
|
|
||||||
|
Dotcom Arcade is developed on a private forge. `mirror/github-mirror.sh`
|
||||||
|
publishes its `main` and the tags on `main` to
|
||||||
|
[github.com/branderson/dotcom-arcade](https://github.com/branderson/dotcom-arcade),
|
||||||
|
hourly, from a host that can push to GitHub. Issues and pull requests are off
|
||||||
|
there. Actions stays on, since it builds the images that `ghcr.io/branderson/`
|
||||||
|
serves (see [publishing images](../09-deployment/docker-deployment.md#publishing-images)).
|
||||||
|
|
||||||
|
The mirror's `main` is the forge's plus one commit, which adds a read-only notice
|
||||||
|
under the README's title. Every forge commit is in the mirror unchanged, and
|
||||||
|
each sync fast-forwards. If the README loses its `# ` title, the sync stops
|
||||||
|
instead of publishing without the notice.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Set up the timer as described at the top of
|
||||||
|
`mirror/dotcom-arcade-github-mirror.service`. Give it a deploy key with write
|
||||||
|
access to the GitHub repository.
|
||||||
|
2. Run `github-mirror.sh --dry-run` to build what a sync would publish without
|
||||||
|
pushing it.
|
||||||
|
3. Once the first sync has pushed, run `github-mirror.sh --settings` (needs
|
||||||
|
`gh`). It turns off issues, pull requests, projects, wiki and discussions.
|
||||||
|
4. The first image build creates its packages as private. In each package's
|
||||||
|
settings on GitHub, set its visibility to public.
|
||||||
|
|
||||||
|
The timer runs a copy of the script, not the checkout's. Reinstall it after
|
||||||
|
reviewing a change to `mirror/`.
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# Syncing with upstream flashpoint-web
|
||||||
|
|
||||||
|
Dotcom Arcade is a heavily modified fork of
|
||||||
|
[flashpoint-web](https://github.com/darkraise/flashpoint-web) by darkraise
|
||||||
|
(branch `master`). Most of upstream's code is still used here: the backend, the
|
||||||
|
frontend's data layer, the player, and the account and admin pages. Upstream's
|
||||||
|
fixes and features to those parts should keep coming in, so the two projects
|
||||||
|
don't drift apart.
|
||||||
|
|
||||||
|
## What's whose
|
||||||
|
|
||||||
|
Comparing this repository with the last upstream commit merged into it:
|
||||||
|
|
||||||
|
- **Unchanged here:** most of the backend, the API client, hooks, stores and
|
||||||
|
types, the player, the account and admin views, and most docs. Upstream
|
||||||
|
changes to these merge as they are.
|
||||||
|
- **Changed here:** files upstream also owns that this project has modified.
|
||||||
|
The main ones:
|
||||||
|
- `GameService` (mature filter, release ranges, shuffle, related games);
|
||||||
|
- `server.ts` and `config.ts` (stream proxy, update check);
|
||||||
|
- `App.tsx` (portal routes);
|
||||||
|
- `GamePlayer` (streaming, saves);
|
||||||
|
- the browse layout and filters;
|
||||||
|
- the rebrand across names and docs.
|
||||||
|
|
||||||
|
Upstream changes to these need merging by hand.
|
||||||
|
- **Removed here:**
|
||||||
|
- the Flash and HTML5 sections;
|
||||||
|
- the GitHub stars endpoint;
|
||||||
|
- the old homepage pieces;
|
||||||
|
- the dev compose file;
|
||||||
|
- upstream's CI workflow and design notes.
|
||||||
|
|
||||||
|
These stay removed.
|
||||||
|
- **Only here:**
|
||||||
|
- `streaming/`;
|
||||||
|
- `scripts/`;
|
||||||
|
- `frontend/src/components/portal/`;
|
||||||
|
- `views/GamePage.tsx`;
|
||||||
|
- `lib/saves/flashSaves.ts`;
|
||||||
|
- `.gitea/`;
|
||||||
|
- the discovery backend;
|
||||||
|
- migrations upstream doesn't have.
|
||||||
|
|
||||||
|
Upstream never touches these, but a change elsewhere can still need matching
|
||||||
|
work here.
|
||||||
|
|
||||||
|
`scripts/upstream-status.sh` lists upstream's new commits and puts every file
|
||||||
|
they touch in one of these groups, so this list doesn't need keeping up to date
|
||||||
|
by hand.
|
||||||
|
|
||||||
|
## When
|
||||||
|
|
||||||
|
Run `scripts/upstream-status.sh` when upstream publishes a release, and at
|
||||||
|
least monthly. Sync when it shows new commits.
|
||||||
|
|
||||||
|
## How
|
||||||
|
|
||||||
|
1. On a branch from `main`, merge upstream:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/upstream-status.sh
|
||||||
|
git checkout -b upstream-sync-$(date +%Y-%m-%d) origin/main
|
||||||
|
git merge refs/remotes/upstream/master
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Resolve conflicts by group:
|
||||||
|
- **Changed here:** keep this project's behavior and take upstream's fix or
|
||||||
|
feature with it. Where they truly conflict, prefer this project's
|
||||||
|
behavior, and note what was left out in the pull request.
|
||||||
|
- **Removed here:** keep the file deleted
|
||||||
|
(`git rm <file>`). If upstream's change matters elsewhere, such as a bug
|
||||||
|
fix in shared logic, port that part to where the code lives now.
|
||||||
|
- **New upstream:** take it, unless it belongs to something removed here.
|
||||||
|
New UI usually lands in the old application layout. Decide whether it
|
||||||
|
belongs in the portal, and if so, follow up with a portal version.
|
||||||
|
- **Branding:** keep "Dotcom Arcade", our image and container names, and our
|
||||||
|
update check (`APP_UPDATE_URL`); don't take back upstream's.
|
||||||
|
3. Migrations: keep upstream's migration files under their own names, even
|
||||||
|
when a number clashes with ours. The migrations table records full names,
|
||||||
|
so `009_app_tagline` and an upstream `009_something` both run, in name
|
||||||
|
order. Renaming an upstream migration would run it a second time on
|
||||||
|
databases that already applied it. Check that ours and theirs don't depend
|
||||||
|
on each other's order.
|
||||||
|
4. Check the result:
|
||||||
|
- backend and frontend typecheck, tests and build;
|
||||||
|
- the session manager's tests if `streaming/` was affected;
|
||||||
|
- `docker compose up -d --build` with and without the `streaming` profile;
|
||||||
|
- the docs, for anything the merge made inaccurate.
|
||||||
|
5. Open a pull request listing upstream's commits and how each conflict was
|
||||||
|
resolved. **Merge it with a merge commit, not squash or rebase.** The merge
|
||||||
|
records how far upstream has been synced, so the next run of
|
||||||
|
`scripts/upstream-status.sh` starts from the right place.
|
||||||
|
|
||||||
|
## Sending changes upstream
|
||||||
|
|
||||||
|
Fixes made here to code upstream still shares, such as backend bugs, may be
|
||||||
|
worth offering upstream. Do that separately, from a branch based on upstream's
|
||||||
|
`master`, and only when the project's maintainer decides to.
|
||||||
@@ -28,18 +28,29 @@ All three are built from this repository by `docker compose up -d --build`.
|
|||||||
|
|
||||||
### Prebuilt images
|
### Prebuilt images
|
||||||
|
|
||||||
To pull published images instead of building, set `IMAGE_PREFIX` in `.env` to
|
Images are published to GitHub's container registry. To pull them instead of
|
||||||
the registry path they live under, ending in `/`, and optionally `IMAGE_TAG`:
|
building, set `IMAGE_PREFIX` in `.env`, ending in `/`, and optionally
|
||||||
|
`IMAGE_TAG`. `latest` is the newest release, `1.2` the newest of a minor
|
||||||
|
version, and `main` follows the `main` branch.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
IMAGE_PREFIX=registry.example.com/owner/
|
IMAGE_PREFIX=ghcr.io/branderson/
|
||||||
IMAGE_TAG=latest
|
IMAGE_TAG=latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Then run `docker compose pull && docker compose up -d`.
|
Then run `docker compose pull && docker compose up -d`. Images from another
|
||||||
|
registry work the same way, with its path as the prefix.
|
||||||
|
|
||||||
### Publishing images
|
### Publishing images
|
||||||
|
|
||||||
|
Two workflows build the same images with the same tags:
|
||||||
|
|
||||||
|
- `.github/workflows/images.yml` runs on the
|
||||||
|
[GitHub mirror](../08-development/github-mirror.md) and pushes to
|
||||||
|
`ghcr.io/<owner>/` with the workflow's own token. It needs no setup.
|
||||||
|
- `.gitea/workflows/images.yml` runs on the forge. Gitea ignores
|
||||||
|
`.github/workflows` while `.gitea/workflows` exists.
|
||||||
|
|
||||||
`.gitea/workflows/images.yml` builds all three images on Gitea Actions and
|
`.gitea/workflows/images.yml` builds all three images on Gitea Actions and
|
||||||
pushes them to the Gitea instance's container registry, under the repository's
|
pushes them to the Gitea instance's container registry, under the repository's
|
||||||
owner. Pushes to `main` publish a `main` tag; `v1.2.3` tags publish `1.2.3`,
|
owner. Pushes to `main` publish a `main` tag; `v1.2.3` tags publish `1.2.3`,
|
||||||
|
|||||||
@@ -30,9 +30,15 @@ tokens are applied to `<body>` while the portal is mounted, which takes
|
|||||||
precedence over the app theme on `<html>` and also reaches dialogs and menus
|
precedence over the app theme on `<html>` and also reaches dialogs and menus
|
||||||
rendered into the body.
|
rendered into the body.
|
||||||
|
|
||||||
One scheme ships, **Chilled** (slate background, sage boxes). Adding a scheme
|
One scheme ships, **Slate** (slate background, sage boxes). Adding a scheme
|
||||||
means adding an entry to `PORTAL_SCHEMES`; no component changes are needed.
|
means adding an entry to `PORTAL_SCHEMES`; no component changes are needed.
|
||||||
|
|
||||||
|
The portal's look is inspired by
|
||||||
|
[Texgriddy](https://github.com/Weestuarty/texgriddy-es-de), an ES-DE theme by
|
||||||
|
Weestuarty: the framed page, glossy tiles with a metallic ring, outlined info
|
||||||
|
boxes, capsule buttons and the slate and sage palette. It was recreated from
|
||||||
|
scratch in CSS; none of Texgriddy's files, images or fonts are used.
|
||||||
|
|
||||||
Display text uses Fredoka (OFL, bundled via `@fontsource-variable/fredoka`);
|
Display text uses Fredoka (OFL, bundled via `@fontsource-variable/fredoka`);
|
||||||
body text uses Verdana.
|
body text uses Verdana.
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ for Flashpoint Ultimate.
|
|||||||
- [Testing Guide](08-development/testing-guide.md) - Testing approach
|
- [Testing Guide](08-development/testing-guide.md) - Testing approach
|
||||||
- [Debugging](08-development/debugging.md) - Debugging tips
|
- [Debugging](08-development/debugging.md) - Debugging tips
|
||||||
- [Common Pitfalls](08-development/common-pitfalls.md) - Known issues
|
- [Common Pitfalls](08-development/common-pitfalls.md) - Known issues
|
||||||
|
- [Upstream Sync](08-development/upstream-sync.md) - Merging changes from flashpoint-web
|
||||||
|
- [GitHub Mirror](08-development/github-mirror.md) - Publishing to GitHub and GHCR
|
||||||
|
|
||||||
### 09. Deployment
|
### 09. Deployment
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function PortalLayout() {
|
|||||||
const searchRef = useRef<HTMLInputElement>(null);
|
const searchRef = useRef<HTMLInputElement>(null);
|
||||||
const [rolling, setRolling] = useState(false);
|
const [rolling, setRolling] = useState(false);
|
||||||
|
|
||||||
useEffect(() => applyPortalScheme('chilled'), []);
|
useEffect(() => applyPortalScheme('slate'), []);
|
||||||
useMatureSync();
|
useMatureSync();
|
||||||
|
|
||||||
const playRandom = async () => {
|
const playRandom = async () => {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export interface PortalScheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PORTAL_SCHEMES = {
|
export const PORTAL_SCHEMES = {
|
||||||
chilled: {
|
slate: {
|
||||||
label: 'Chilled',
|
label: 'Slate',
|
||||||
tokens: {
|
tokens: {
|
||||||
'--p-bg': '#4c6878',
|
'--p-bg': '#4c6878',
|
||||||
'--p-bg-mix': '#2f4553',
|
'--p-bg-mix': '#2f4553',
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# User unit for the GitHub mirror, on a host that can push to GitHub. Never in
|
||||||
|
# a dejima session: a sandbox may push only to its own forge.
|
||||||
|
#
|
||||||
|
# install -m 755 mirror/github-mirror.sh ~/.local/bin/dotcom-arcade-github-mirror
|
||||||
|
# cp mirror/dotcom-arcade-github-mirror.service mirror/dotcom-arcade-github-mirror.timer ~/.config/systemd/user/
|
||||||
|
# systemctl --user daemon-reload
|
||||||
|
# systemctl --user enable --now dotcom-arcade-github-mirror.timer
|
||||||
|
#
|
||||||
|
# A copy, not a link into the checkout: the timer holds a key that can push to
|
||||||
|
# GitHub, and a session working in this checkout can edit the script.
|
||||||
|
# Reinstall it after reviewing an update.
|
||||||
|
#
|
||||||
|
# ~/.config/dotcom-arcade/github-mirror.env sets MIRROR_SOURCE, the forge
|
||||||
|
# clone URL, and whatever else mirror/github-mirror.sh documents. The GitHub
|
||||||
|
# key, a deploy key with write access to the repository, belongs behind an ssh
|
||||||
|
# Host alias named in MIRROR_TARGET, so it is not also offered to the forge:
|
||||||
|
#
|
||||||
|
# MIRROR_SOURCE=git@forge.example.com:owner/dotcom-arcade.git
|
||||||
|
# MIRROR_TARGET=git@github-dotcom-arcade:branderson/dotcom-arcade.git
|
||||||
|
[Unit]
|
||||||
|
Description=Mirror Dotcom Arcade's main and release tags to GitHub
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
EnvironmentFile=%h/.config/dotcom-arcade/github-mirror.env
|
||||||
|
ExecStart=%h/.local/bin/dotcom-arcade-github-mirror
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Mirror Dotcom Arcade's main and release tags to GitHub, hourly
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=hourly
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Executable
+133
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Publishes main and its release tags to a read-only mirror on GitHub.
|
||||||
|
#
|
||||||
|
# MIRROR_SOURCE=<forge clone URL> mirror/github-mirror.sh sync
|
||||||
|
# ... mirror/github-mirror.sh --dry-run build and print, push nothing
|
||||||
|
# mirror/github-mirror.sh --settings configure the GitHub repo (needs gh)
|
||||||
|
# mirror/github-mirror.sh --transform DIR rewrite a checkout in place
|
||||||
|
#
|
||||||
|
# Runs on a host that can push to GitHub, never in a dejima session: a sandbox
|
||||||
|
# may push only to its own forge.
|
||||||
|
#
|
||||||
|
# The GitHub copy is not byte-for-byte. Its main is one extra commit on top of
|
||||||
|
# the forge's main, whose tree is the forge's with the rewrites in transform()
|
||||||
|
# applied, and whose parents are the forge commit and the previous mirror
|
||||||
|
# commit. So every forge SHA is in the mirror unchanged, and each sync is a
|
||||||
|
# fast-forward for anyone tracking it. Tags are pushed as they are, pointing at
|
||||||
|
# the untransformed forge commits.
|
||||||
|
#
|
||||||
|
# Environment:
|
||||||
|
# MIRROR_SOURCE forge clone URL (required to sync)
|
||||||
|
# MIRROR_REPO GitHub repository, default branderson/dotcom-arcade
|
||||||
|
# MIRROR_TARGET push URL, default git@github.com:$MIRROR_REPO.git
|
||||||
|
# MIRROR_STATE working bare repository, default ~/.cache/dotcom-arcade-github-mirror.git
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MIRROR_REPO="${MIRROR_REPO:-branderson/dotcom-arcade}"
|
||||||
|
MIRROR_TARGET="${MIRROR_TARGET:-git@github.com:$MIRROR_REPO.git}"
|
||||||
|
MIRROR_STATE="${MIRROR_STATE:-${XDG_CACHE_HOME:-$HOME/.cache}/dotcom-arcade-github-mirror.git}"
|
||||||
|
|
||||||
|
say() { echo "github-mirror: $*"; }
|
||||||
|
die() { say "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
WORK=""
|
||||||
|
cleanup() {
|
||||||
|
[ -n "$WORK" ] || return 0
|
||||||
|
git -C "$MIRROR_STATE" worktree remove --force "$WORK" 2>/dev/null || true
|
||||||
|
rm -rf "$WORK"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rewrites a checkout for GitHub. Each rewrite is checked, so a README that
|
||||||
|
# drifted away from what this expects stops the mirror instead of publishing
|
||||||
|
# the forge's version. Running it twice changes nothing.
|
||||||
|
transform() {
|
||||||
|
local readme="$1/README.md"
|
||||||
|
# perl -i would write a symlink's target back as the README, publishing it.
|
||||||
|
[ -f "$readme" ] && [ ! -L "$readme" ] || die "README.md is missing or a symlink in $1"
|
||||||
|
|
||||||
|
# The first thing a visitor to a mirror does is open an issue or a pull
|
||||||
|
# request. Under the title.
|
||||||
|
grep -q '^> \*\*This is a read-only mirror' "$readme" || perl -0pi -e '
|
||||||
|
s{^(# [^\n]*)\n}{$1\n\n> **This is a read-only mirror.** Dotcom Arcade is developed on a private\n> forge, and issues and pull requests are not accepted here.\n}m
|
||||||
|
' "$readme"
|
||||||
|
grep -q '^> \*\*This is a read-only mirror' "$readme" \
|
||||||
|
|| die "README.md: no title to put the mirror notice under"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Issues, pull requests, wiki and the like off; Actions stays on, since it
|
||||||
|
# publishes the images. Settings rather than content, so a sync does not
|
||||||
|
# reapply them; run once after creating the repository.
|
||||||
|
settings() {
|
||||||
|
command -v gh >/dev/null || die "--settings needs the gh CLI"
|
||||||
|
gh api -X PATCH "repos/$MIRROR_REPO" --silent \
|
||||||
|
-F has_issues=false -F has_pull_requests=false -F has_projects=false \
|
||||||
|
-F has_wiki=false -F has_discussions=false
|
||||||
|
}
|
||||||
|
|
||||||
|
sync() {
|
||||||
|
[ -n "${MIRROR_SOURCE:-}" ] || die "MIRROR_SOURCE is not set"
|
||||||
|
local git=(git -C "$MIRROR_STATE")
|
||||||
|
trap cleanup EXIT
|
||||||
|
[ -d "$MIRROR_STATE" ] || git init -q --bare "$MIRROR_STATE"
|
||||||
|
|
||||||
|
"${git[@]}" fetch -q --no-tags --prune "$MIRROR_SOURCE" \
|
||||||
|
'+refs/heads/main:refs/source/main' '+refs/tags/*:refs/source/tags/*'
|
||||||
|
local src prev=""
|
||||||
|
src="$("${git[@]}" rev-parse refs/source/main)"
|
||||||
|
|
||||||
|
# An absent main (exit 2) is a first run; anything else is a real failure.
|
||||||
|
local rc=0
|
||||||
|
git ls-remote -q --exit-code "$MIRROR_TARGET" refs/heads/main >/dev/null || rc=$?
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
"${git[@]}" fetch -q --no-tags "$MIRROR_TARGET" '+refs/heads/main:refs/mirror/main'
|
||||||
|
prev="$("${git[@]}" rev-parse refs/mirror/main)"
|
||||||
|
elif [ "$rc" -ne 2 ]; then
|
||||||
|
die "cannot read $MIRROR_TARGET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
WORK="$(mktemp -d)"
|
||||||
|
"${git[@]}" worktree add -q --detach "$WORK" "$src"
|
||||||
|
transform "$WORK"
|
||||||
|
git -C "$WORK" add -A
|
||||||
|
local tree
|
||||||
|
tree="$(git -C "$WORK" write-tree)"
|
||||||
|
|
||||||
|
if [ -n "$prev" ] && [ "$("${git[@]}" rev-parse "$prev^1")" = "$src" ] \
|
||||||
|
&& [ "$("${git[@]}" rev-parse "$prev^{tree}")" = "$tree" ]; then
|
||||||
|
say "already at ${src:0:12}"
|
||||||
|
else
|
||||||
|
# The forge commit's committer and date, so the same input always
|
||||||
|
# makes the same commit.
|
||||||
|
local name email date commit
|
||||||
|
IFS=$'\t' read -r name email date < <("${git[@]}" log -1 --format='%cn%x09%ce%x09%cI' "$src")
|
||||||
|
commit="$(GIT_AUTHOR_NAME="$name" GIT_AUTHOR_EMAIL="$email" GIT_AUTHOR_DATE="$date" \
|
||||||
|
GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" GIT_COMMITTER_DATE="$date" \
|
||||||
|
"${git[@]}" commit-tree "$tree" -p "$src" ${prev:+-p "$prev"} \
|
||||||
|
-m "Mirror ${src:0:12} for GitHub" \
|
||||||
|
-m "README header added by mirror/github-mirror.sh.")"
|
||||||
|
"${git[@]}" update-ref refs/mirror/next "$commit"
|
||||||
|
[ -n "$DRY_RUN" ] || "${git[@]}" push -q "$MIRROR_TARGET" "$commit:refs/heads/main"
|
||||||
|
say "main is ${src:0:12} as ${commit:0:12}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only tags on main: one on another branch would publish unreleased work.
|
||||||
|
local ref
|
||||||
|
local refspecs=()
|
||||||
|
while IFS= read -r ref; do
|
||||||
|
if "${git[@]}" merge-base --is-ancestor "$ref" "$src"; then
|
||||||
|
refspecs+=("$ref:refs/tags/${ref#refs/source/tags/}")
|
||||||
|
say "tag ${ref#refs/source/tags/}"
|
||||||
|
fi
|
||||||
|
done < <("${git[@]}" for-each-ref --format='%(refname)' refs/source/tags/)
|
||||||
|
[ "${#refspecs[@]}" -eq 0 ] || [ -n "$DRY_RUN" ] \
|
||||||
|
|| "${git[@]}" push -q "$MIRROR_TARGET" "${refspecs[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
DRY_RUN=""
|
||||||
|
case "${1:-}" in
|
||||||
|
"") sync ;;
|
||||||
|
--dry-run) DRY_RUN=1; sync ;;
|
||||||
|
--settings) settings ;;
|
||||||
|
--transform) [ -n "${2:-}" ] || die "--transform needs a directory"; transform "$2" ;;
|
||||||
|
*) sed -n '4,7p' "$0" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Shows what upstream flashpoint-web has changed since it was last merged, and
|
||||||
|
# how this project stands on each file it touches. Changes nothing but the
|
||||||
|
# refs/remotes/upstream/master ref. See docs/08-development/upstream-sync.md.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
URL=${UPSTREAM_URL:-https://github.com/darkraise/flashpoint-web.git}
|
||||||
|
BRANCH=master
|
||||||
|
UPSTREAM=refs/remotes/upstream/$BRANCH
|
||||||
|
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
git fetch -q --no-tags "$URL" "+$BRANCH:$UPSTREAM"
|
||||||
|
base=$(git merge-base HEAD "$UPSTREAM")
|
||||||
|
count=$(git rev-list --count "$base..$UPSTREAM")
|
||||||
|
|
||||||
|
echo "Last merged from upstream: $(git log -1 --format='%h %ad %s' --date=short "$base")"
|
||||||
|
if ((count == 0)); then
|
||||||
|
echo "Up to date with upstream $BRANCH."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$count new upstream commit(s):"
|
||||||
|
git log --format=' %h %ad %s' --date=short "$base..$UPSTREAM"
|
||||||
|
echo
|
||||||
|
echo "Files they change, by where this project stands on each:"
|
||||||
|
echo " unchanged here: merges as is changed here: merge both sides"
|
||||||
|
echo " removed here: keep it removed new upstream: take it, unless it belongs to a removed area"
|
||||||
|
echo
|
||||||
|
git diff --name-only "$base" "$UPSTREAM" | while IFS= read -r file; do
|
||||||
|
if ! git cat-file -e "HEAD:$file" 2>/dev/null; then
|
||||||
|
if git cat-file -e "$base:$file" 2>/dev/null; then
|
||||||
|
echo "removed here $file"
|
||||||
|
else
|
||||||
|
echo "new upstream $file"
|
||||||
|
fi
|
||||||
|
elif git diff --quiet "$base" HEAD -- "$file"; then
|
||||||
|
echo "unchanged here $file"
|
||||||
|
else
|
||||||
|
echo "changed here $file"
|
||||||
|
fi
|
||||||
|
done | sort
|
||||||
Reference in New Issue
Block a user