mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-10-15 18:35:43 -04:00
chore: set up changesets for versioning and releases (#68)
This commit is contained in:
25 files changed
+425
-1
No files matched your search
@@ -0,0 +1,5 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a tool to manage versioning and changelogs. Read the full documentation at https://changesets.dev.
|
||||
|
||||
We also have a quick list of common questions to get you started engaging with this project in our [FAQ](https://changesets.dev/faq).
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
commit: e5209f80fec06509cceaa0c1b0e54ff88d8ab72b
|
||||
|
||||
Rate-limit sign-ins by the real client IP, so spoofed `X-Forwarded-For` headers can't get around the limit
|
||||
|
||||
**If Sofa sits behind a reverse proxy on another machine** (for example a VPS reaching Sofa over the internet or Tailscale, or Cloudflare proxying straight to it), add the proxy's address to the new `TRUSTED_PROXIES` setting, or every user will share the proxy's rate limit. Proxies on the same host or Docker network, or elsewhere on your LAN at a private address (10.x, 172.16–31.x, 192.168.x), work without changes. See [Configuration](https://sofa.watch/docs/configuration#network).
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
Restore backups made by older versions of Sofa: a restored database is now migrated before it replaces the live one
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@4.0.1/schema.json",
|
||||
"baseBranch": "main",
|
||||
"access": "restricted",
|
||||
"format": "oxfmt",
|
||||
"changelog": ["@changesets/changelog-github", { "repo": "jakejarvis/sofa" }],
|
||||
"commit": false,
|
||||
"ignore": ["@sofa/native"],
|
||||
"fixed": [
|
||||
[
|
||||
"@sofa/server",
|
||||
"@sofa/web",
|
||||
"@sofa/api",
|
||||
"@sofa/auth",
|
||||
"@sofa/config",
|
||||
"@sofa/core",
|
||||
"@sofa/db",
|
||||
"@sofa/i18n",
|
||||
"@sofa/logger",
|
||||
"@sofa/test",
|
||||
"@sofa/tmdb",
|
||||
"@sofa/tsconfig"
|
||||
]
|
||||
],
|
||||
"linked": [],
|
||||
"updateInternalDependencies": "patch",
|
||||
"privatePackages": { "version": true, "tag": false }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
commit: cb5e0c19e7efbb9d3a6e3e0a933e33ad203bb01d
|
||||
|
||||
Remove a single watch from your history, or log a watch on a specific date from a title's page
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
commit: b63fbce60da8383c3e5e403966d8ed72572f42a5
|
||||
author: @souocare
|
||||
|
||||
Find titles by IMDb ID: searching for an ID like `tt0133093` returns that movie or show
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
Make Trakt and Simkl imports more reliable
|
||||
|
||||
- Accept Trakt's official export (ZIP or JSON, including files with a UTF-8 BOM) and Simkl backups with nested items
|
||||
- Keep rewatches and the dates titles were added, and import Trakt season and episode entries
|
||||
- Skip invalid items instead of failing the whole import, and skip specials
|
||||
- Keep date-only values on the right calendar day
|
||||
- Stop promptly when an import is cancelled, even near the end
|
||||
- Refresh each imported show once afterward to pick up new episodes
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
Fix several library, discovery, and stats issues
|
||||
|
||||
- Fully load TV shows the first time you open them
|
||||
- Leave titles you've rated or watched out of recommendations
|
||||
- Only offer aired episodes in Continue Watching
|
||||
- Don't add duplicate watches when marking a season or show watched
|
||||
- Line up stats totals with their charts, and fix month rollover
|
||||
- Keep one failing title from stalling scheduled refreshes; manual jobs now start in the background and say when they're already running
|
||||
- Remove duplicate cast and crew entries
|
||||
- Keep the `/3` path when `TMDB_API_BASE_URL` points at a custom server
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
pr: #52
|
||||
|
||||
Add a "Mark Unwatched" button for movies you've already watched (the `M` shortcut toggles it too)
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@sofa/server": minor
|
||||
---
|
||||
|
||||
commit: a4439dddb11fdf25ffac5660a3a64d1b2706d333
|
||||
|
||||
Back up the database automatically before applying migrations on upgrade. These backups appear as "Pre-upgrade backup" in Settings and are kept in the `backups` folder of your data directory.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
commit: 04bcb66436dc14c3630d29a1c1357dc1ec13747a
|
||||
|
||||
Mark episodes watched straight from Continue Watching and Upcoming
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
commit: e625f319a20ca51ed7b5cdae331b8b88b5d0c164
|
||||
|
||||
Add a "Recently aired" view to Upcoming that lists aired episodes you haven't watched yet
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
Make metadata refreshes lighter: ended shows refresh less often, returning shows refetch only their recent seasons, empty TMDB lookups are remembered, and episode art is processed in the background
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
Harden the server against malicious requests
|
||||
|
||||
- Send security headers, and reject API writes that a browser sends from another site (Sofa's own address comes from `BETTER_AUTH_URL` or `CORS_ORIGIN`)
|
||||
- Limit request body sizes, and require a signed-in session before accepting large uploads
|
||||
- Limit the uncompressed size and file count of uploaded import ZIPs
|
||||
- Validate cached image and avatar file names, and serve those files so browsers can't run scripts from them
|
||||
- Only let the mobile app's sign-in proxy redirect to your OIDC provider's authorization endpoint
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
commit: b292fe37abbd73cee98c918edb915018989c657c
|
||||
|
||||
Add a watch history page listing every movie and episode you've watched, and a "Last watched" sort for the library
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
"@sofa/web": patch
|
||||
---
|
||||
|
||||
Fix a batch of web app issues
|
||||
|
||||
- Show translated error messages instead of raw server errors, and fix the "Tomorrow" and "Next week" labels
|
||||
- Refresh lists and counts after you change a title, and make Retry buttons actually retry
|
||||
- Clear cached data when you sign out or switch accounts
|
||||
- Keep infinite lists from dropping their first page, and load more titles in Explore rows
|
||||
- Ignore title keyboard shortcuts while a dialog or menu is open
|
||||
- Explain why a password change failed
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@sofa/server": patch
|
||||
---
|
||||
|
||||
Fix scrobbling from media servers: episodes from Plex and Emby no longer match the wrong show, Emby's native webhook payloads are accepted, and a show is refreshed when a scrobbled episode isn't known yet
|
||||
@@ -16,6 +16,8 @@ on:
|
||||
- "tsconfig.json"
|
||||
- ".github/workflows/docker.yml"
|
||||
tags: ["v*"]
|
||||
# Dispatched by the Release workflow for each new tag
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions:
|
||||
contents: write # push the release branch, create tags and releases
|
||||
pull-requests: write # open the "Version Packages" PR
|
||||
actions: write # dispatch the Docker workflow for a new tag
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
# Tags and releases the current version unless it already has a tag, so it only does
|
||||
# something right after a "Version Packages" PR merges
|
||||
- name: Publish release
|
||||
run: bun run release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Opens or updates the "Version Packages" PR while changesets are pending
|
||||
- name: Create release PR
|
||||
uses: changesets/action@v2
|
||||
with:
|
||||
version-script: bun run version-packages
|
||||
commit-message: "chore(release): version packages"
|
||||
pr-title: "chore(release): version packages"
|
||||
env:
|
||||
# Read by @changesets/changelog-github to link PRs and authors
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -14,6 +14,7 @@ bun run test # Run tests
|
||||
bun run generate:openapi # Regenerate OpenAPI spec + docs API pages (run after contract/schema changes)
|
||||
bun run i18n:extract # Run LingUI's string extraction
|
||||
bun run i18n:claude # Prompt Claude Code to fill in untranslated strings
|
||||
bun run changeset # Add a changeset describing a user-facing change (see Releases)
|
||||
|
||||
# Database commands (run from packages/db/)
|
||||
cd packages/db && bun run db:push # Push schema changes to SQLite database
|
||||
@@ -35,6 +36,8 @@ bun run check-types
|
||||
bun run test
|
||||
```
|
||||
|
||||
If the change is user-facing, it also needs a changeset — see [Releases](#releases).
|
||||
|
||||
## Architecture
|
||||
|
||||
**Sofa** is a self-hosted movie & TV tracking app built as a Turborepo monorepo.
|
||||
@@ -56,6 +59,7 @@ couch-potato/
|
||||
│ ├── logger/ # @sofa/logger — Pino-based structured logging (JIT)
|
||||
│ ├── test/ # @sofa/test — Vitest setup + in-memory DB test helpers
|
||||
│ └── tmdb/ # @sofa/tmdb — TMDB API client + image URL helper (JIT)
|
||||
├── .changeset/ # Changesets config + pending changesets (see Releases)
|
||||
├── .oxlintrc.json
|
||||
├── .oxfmtrc.json
|
||||
├── Dockerfile
|
||||
@@ -155,6 +159,26 @@ Optional:
|
||||
|
||||
Single container, single process. Hono serves API + SPA on port 3000. API routes (`/rpc/*`, `/api/*`) mounted first; unmatched routes fall back to `index.html`.
|
||||
|
||||
### Releases
|
||||
|
||||
Versions and changelogs are managed with [Changesets](https://changesets.dev). **Every PR with a user-facing change needs a changeset**: a Markdown file in `.changeset/` naming the affected packages and their semver bump. Run `bun run changeset`, or write one by hand with any unique filename:
|
||||
|
||||
```md
|
||||
---
|
||||
"@sofa/web": minor
|
||||
---
|
||||
|
||||
Add a "Rewatch" button to title pages
|
||||
```
|
||||
|
||||
- **The summary is user-facing** — it lands verbatim in `CHANGELOG.md` and the GitHub release notes, so write it for people running Sofa. Skip the changeset for changes users won't notice (refactors, tests, CI, internal docs).
|
||||
- **Server release** — `@sofa/server`, `@sofa/web`, and every `packages/*` library are a fixed group sharing one version: the Docker image tag and the `vX.Y.Z` GitHub release. Name the packages you changed; any bump moves the whole group.
|
||||
- **`@sofa/native`** is left out of Changesets (`ignore` in the config): its version is the App Store / Play Store version, bumped by hand for store submissions. Never name it in a changeset — Changesets rejects a changeset that mixes it with other packages and silently skips one that names only it.
|
||||
- **`@sofa/public-api`** is versioned on its own and deploys continuously; a changeset is optional.
|
||||
- **Bump types** — `patch` for fixes, `minor` for features and for breaking changes while on 0.x. Never use `major` unless asked: it would release 1.0.0.
|
||||
|
||||
`.github/workflows/release.yml` automates the rest. While changesets are pending, it keeps a "chore(release): version packages" PR up to date (`bun run version-packages` bumps versions, writes changelogs, and syncs `bun.lock` and the OpenAPI spec version). Merging that PR makes `bun run release` tag `vX.Y.Z`, publish the GitHub release, and dispatch the Docker workflow for the tag. Mobile builds still go through the EAS workflows.
|
||||
|
||||
## Browser Automation
|
||||
|
||||
Use `agent-browser` for web automation (`agent-browser --help` for all commands). Auth credentials: `demo@sofa.watch` / `password`.
|
||||
@@ -153,6 +153,7 @@ Useful commands:
|
||||
- `bun run lint`
|
||||
- `bun run format`
|
||||
- `bun run check-types`
|
||||
- `bun run changeset` (describe a user-facing change for the next release's changelog)
|
||||
- `cd packages/db && bun run db:generate`
|
||||
- `cd packages/db && bun run db:migrate`
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
"": {
|
||||
"name": "sofa-monorepo",
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "1.0.1",
|
||||
"@changesets/cli": "3.0.3",
|
||||
"@lingui/cli": "catalog:",
|
||||
"@lingui/conf": "catalog:",
|
||||
"@lingui/format-po": "catalog:",
|
||||
@@ -564,6 +566,44 @@
|
||||
|
||||
"@blazediff/core": ["@blazediff/core@1.10.0", "", {}, "sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ=="],
|
||||
|
||||
"@changesets/apply-release-plan": ["@changesets/apply-release-plan@8.1.1", "", { "dependencies": { "@changesets/config": "^4.0.1", "@changesets/format": "^0.1.2", "@changesets/git": "^4.0.1", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "import-meta-resolve": "^4.2.0", "jsonc-parser": "^3.3.1", "semver": "^7.8.1" } }, "sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q=="],
|
||||
|
||||
"@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@7.0.0", "", { "dependencies": { "@changesets/errors": "^1.0.0", "@changesets/get-dependents-graph": "^3.0.0", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "semver": "^7.8.1" } }, "sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg=="],
|
||||
|
||||
"@changesets/changelog-git": ["@changesets/changelog-git@1.0.0", "", { "dependencies": { "@changesets/types": "^7.0.0" } }, "sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA=="],
|
||||
|
||||
"@changesets/changelog-github": ["@changesets/changelog-github@1.0.1", "", { "dependencies": { "@changesets/get-github-info": "^1.0.1", "@changesets/types": "^7.0.0" } }, "sha512-QUcrV7878yHlWPXXPA6gqSxNKwtVHCd1K22L7ZAoJw2yGy8K4QvjwOStD75+1Q9KD9ZSUgGS94HuYdd7HOVtpA=="],
|
||||
|
||||
"@changesets/cli": ["@changesets/cli@3.0.3", "", { "dependencies": { "@changesets/apply-release-plan": "^8.1.1", "@changesets/assemble-release-plan": "^7.0.0", "@changesets/changelog-git": "^1.0.0", "@changesets/config": "^4.0.1", "@changesets/errors": "^1.0.0", "@changesets/get-dependents-graph": "^3.0.0", "@changesets/git": "^4.0.1", "@changesets/pre": "^3.0.0", "@changesets/read": "^1.0.1", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "@changesets/write": "^1.0.1", "@clack/prompts": "^1.7.0", "@manypkg/get-packages": "^3.1.0", "@pnpm/deps.graph-sequencer": "^1100.0.1", "cac": "^7.0.0", "import-meta-resolve": "^4.2.0", "launch-editor": "^2.14.1", "package-manager-detector": "^1.6.0", "semver": "^7.8.1", "tinyexec": "^1.3.1" }, "bin": { "changeset": "bin.js" } }, "sha512-L1i67bMfkK6iC/kDejuNO4pFgTRcWIObsD6Qof2woP1I4M8tj17DGlcpRaTzTv1DYSP5EMwWdEtR8X0Pj5Jg3Q=="],
|
||||
|
||||
"@changesets/config": ["@changesets/config@4.0.1", "", { "dependencies": { "@changesets/get-dependents-graph": "^3.0.0", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "@manypkg/get-packages": "^3.1.0", "picomatch": "^4.0.7" } }, "sha512-PxpxSQLQSLDjceAgRq+4FWlU+HflDIw03mmPVipxoQx9xrXFUjmqTy3SE+n/2t+DqPdpPiLWVr5NRiLZvjyLwg=="],
|
||||
|
||||
"@changesets/errors": ["@changesets/errors@1.0.0", "", {}, "sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g=="],
|
||||
|
||||
"@changesets/format": ["@changesets/format@0.1.2", "", { "dependencies": { "package-manager-detector": "^1.8.0", "tinyexec": "^1.3.0" } }, "sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA=="],
|
||||
|
||||
"@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@3.0.0", "", { "dependencies": { "@changesets/types": "^7.0.0", "semver": "^7.8.1" } }, "sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg=="],
|
||||
|
||||
"@changesets/get-github-info": ["@changesets/get-github-info@1.0.1", "", { "dependencies": { "dataloader": "^2.2.3" } }, "sha512-ifLQCky/ZtDgZ4xCiUMjnLZSJ8xk52iIzMJbBBPlZWjr2CiTNTaTDddIVWKicrUAdWuLWL7PUw2fNa1yPwLpIg=="],
|
||||
|
||||
"@changesets/git": ["@changesets/git@4.0.1", "", { "dependencies": { "@changesets/errors": "^1.0.0", "@changesets/types": "^7.0.0", "@manypkg/get-packages": "^3.1.0", "picomatch": "^4.0.4", "tinyexec": "^1.3.0" } }, "sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA=="],
|
||||
|
||||
"@changesets/parse": ["@changesets/parse@1.0.0", "", { "dependencies": { "@changesets/types": "^7.0.0", "yaml": "^2.9.0" } }, "sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg=="],
|
||||
|
||||
"@changesets/pre": ["@changesets/pre@3.0.0", "", { "dependencies": { "@changesets/errors": "^1.0.0", "@changesets/types": "^7.0.0", "@manypkg/get-packages": "^3.1.0" } }, "sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q=="],
|
||||
|
||||
"@changesets/read": ["@changesets/read@1.0.1", "", { "dependencies": { "@changesets/git": "^4.0.1", "@changesets/parse": "^1.0.0", "@changesets/types": "^7.0.0" } }, "sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ=="],
|
||||
|
||||
"@changesets/should-skip-package": ["@changesets/should-skip-package@1.0.0", "", { "dependencies": { "@changesets/types": "^7.0.0" } }, "sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg=="],
|
||||
|
||||
"@changesets/types": ["@changesets/types@7.0.0", "", {}, "sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w=="],
|
||||
|
||||
"@changesets/write": ["@changesets/write@1.0.1", "", { "dependencies": { "@changesets/format": "^0.1.1", "@changesets/types": "^7.0.0", "human-id": "^4.2.0" } }, "sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA=="],
|
||||
|
||||
"@clack/core": ["@clack/core@1.5.1", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@1.8.1", "", { "dependencies": { "@clack/core": "1.5.1", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ=="],
|
||||
|
||||
"@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
|
||||
|
||||
"@corvu/utils": ["@corvu/utils@0.4.2", "", { "dependencies": { "@floating-ui/dom": "^1.6.11" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-Ox2kYyxy7NoXdKWdHeDEjZxClwzO4SKM8plAaVwmAJPxHMqA0rLOoAsa+hBDwRLpctf+ZRnAd/ykguuJidnaTA=="],
|
||||
@@ -906,6 +946,12 @@
|
||||
|
||||
"@lingui/vite-plugin": ["@lingui/vite-plugin@6.9.0", "", { "dependencies": { "@lingui/cli": "6.9.0", "@lingui/conf": "6.9.0", "@lingui/native-tools": "^0.1.5" }, "peerDependencies": { "@babel/core": "^7.29.0 || ^8.0.0", "@lingui/babel-plugin-lingui-macro": "^5 || ^6", "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "rolldown": "^1.0.0-rc.5", "vite": "^6.3.0 || ^7 || ^8" }, "optionalPeers": ["@babel/core", "@lingui/babel-plugin-lingui-macro", "@rolldown/plugin-babel", "rolldown"] }, "sha512-CrOjXhhxV+qpbSt1EVqO8+LD37K4M/87w5Xajic0m4K8opcS+B2h+4H4a/ZThu3LnqPkL50ZlOPDYLQpuRwY+Q=="],
|
||||
|
||||
"@manypkg/find-root": ["@manypkg/find-root@3.1.0", "", { "dependencies": { "@manypkg/tools": "^2.1.0" } }, "sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA=="],
|
||||
|
||||
"@manypkg/get-packages": ["@manypkg/get-packages@3.1.0", "", { "dependencies": { "@manypkg/find-root": "^3.1.0", "@manypkg/tools": "^2.1.0" } }, "sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg=="],
|
||||
|
||||
"@manypkg/tools": ["@manypkg/tools@2.1.2", "", { "dependencies": { "jju": "^1.4.0", "tinyglobby": "^0.2.13", "yaml": "^2.9.0" } }, "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ=="],
|
||||
|
||||
"@messageformat/date-skeleton": ["@messageformat/date-skeleton@1.1.0", "", {}, "sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A=="],
|
||||
|
||||
"@messageformat/parser": ["@messageformat/parser@5.1.1", "", { "dependencies": { "moo": "^0.5.1" } }, "sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg=="],
|
||||
@@ -1072,6 +1118,8 @@
|
||||
|
||||
"@player.style/sutro": ["@player.style/sutro@0.2.1", "", { "peerDependencies": { "media-chrome": ">=1.0.0" } }, "sha512-vWtFAdu4z3+1HcDPAriX+41q+bYSOgM898XO4TxEgoNyeaBq/Aoz2cRgWFpsWaK368+n7uWCbO5n2vAszN5fhw=="],
|
||||
|
||||
"@pnpm/deps.graph-sequencer": ["@pnpm/deps.graph-sequencer@1100.0.1", "", {}, "sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A=="],
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||
|
||||
"@posthog/core": ["@posthog/core@1.55.3", "", { "dependencies": { "@posthog/types": "^1.413.0" } }, "sha512-XnmG9jo7WlxTeN4oCNWH4ESLQHR6ZPZ+mcq+8JxU62k5i1BAe3BSrybnmXHjqKp7e3rUvLK2bOzGZGSlfHvlNA=="],
|
||||
@@ -1688,6 +1736,8 @@
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"cac": ["cac@7.0.0", "", {}, "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
@@ -1790,6 +1840,8 @@
|
||||
|
||||
"culori": ["culori@4.0.2", "", {}, "sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw=="],
|
||||
|
||||
"dataloader": ["dataloader@2.2.3", "", {}, "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA=="],
|
||||
|
||||
"dateformat": ["dateformat@4.6.3", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.21", "", {}, "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA=="],
|
||||
@@ -2024,8 +2076,14 @@
|
||||
|
||||
"fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="],
|
||||
|
||||
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
|
||||
|
||||
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.3", "", {}, "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg=="],
|
||||
|
||||
"fast-wrap-ansi": ["fast-wrap-ansi@0.2.2", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q=="],
|
||||
|
||||
"fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
|
||||
|
||||
"fb-dotslash": ["fb-dotslash@0.5.8", "", { "bin": { "dotslash": "bin/dotslash" } }, "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA=="],
|
||||
@@ -2130,6 +2188,8 @@
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"human-id": ["human-id@4.2.2", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-Z+8unD6em/MJwyrkWQ2M3wygRGI1+MztbutiNwk1DCuEPXOZRWGjtcMPZYP1exY9iNZMhd7ZOSMQSo9WYtiQRw=="],
|
||||
|
||||
"human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
|
||||
@@ -2144,6 +2204,8 @@
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
|
||||
"import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
|
||||
|
||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
@@ -2206,6 +2268,8 @@
|
||||
|
||||
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
|
||||
|
||||
"jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="],
|
||||
|
||||
"jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
|
||||
|
||||
"jotai": ["jotai@3.0.1", "", { "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0" }, "optionalPeers": ["@types/react", "react"] }, "sha512-6tS7wY9otdiNp26Wqb/1ds3OuVJVoV+tJlN4hpLrYmfFf/t9bauVt+zKBNSpS5qSl5h6GieVQ+OknVraZX+udQ=="],
|
||||
@@ -2238,6 +2302,8 @@
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
|
||||
|
||||
"jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
@@ -2458,6 +2524,8 @@
|
||||
|
||||
"p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="],
|
||||
|
||||
"package-manager-detector": ["package-manager-detector@1.9.0", "", {}, "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA=="],
|
||||
|
||||
"pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||
|
||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||
|
||||
+6
-1
@@ -60,9 +60,14 @@
|
||||
"i18n:extract": "lingui extract",
|
||||
"i18n:claude": "bun packages/i18n/scripts/claude.ts",
|
||||
"sync:providers": "bun scripts/sync-tmdb-providers.ts",
|
||||
"smoke:backup": "bun scripts/backup-restore-smoke.ts"
|
||||
"smoke:backup": "bun scripts/backup-restore-smoke.ts",
|
||||
"changeset": "changeset",
|
||||
"version-packages": "bun scripts/version-packages.ts",
|
||||
"release": "bun scripts/release.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "1.0.1",
|
||||
"@changesets/cli": "3.0.3",
|
||||
"@lingui/cli": "catalog:",
|
||||
"@lingui/conf": "catalog:",
|
||||
"@lingui/format-po": "catalog:",
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Tags and publishes a GitHub release for the current server version unless that tag already
|
||||
* exists, then dispatches the Docker workflow for it. The Release workflow runs this on every push
|
||||
* to main; it's a no-op until a "Version Packages" PR merges and changes the version.
|
||||
*
|
||||
* Release notes are merged from the CHANGELOG.md of every package in the fixed version group, so a
|
||||
* changeset counts no matter which of those packages it names.
|
||||
*
|
||||
* Usage: bun run release [--dry-run]
|
||||
*/
|
||||
|
||||
import path from "node:path";
|
||||
|
||||
import { $ } from "bun";
|
||||
|
||||
import changesetConfig from "../.changeset/config.json" with { type: "json" };
|
||||
|
||||
const CHANGE_TYPES = ["Major Changes", "Minor Changes", "Patch Changes"];
|
||||
// The whole group moves in lockstep, so its dependency bumps are noise: "- Updated dependencies
|
||||
// [abc1234]:" then " - @sofa/api@1.2.3" lines (the default changelog generator leaves out the first
|
||||
// line when only the fixed group caused the bump)
|
||||
const DEPENDENCY_ENTRY = /^- (?:Updated dependencies|@sofa\/[\w-]+@\d)/;
|
||||
|
||||
const dryRun = process.argv.includes("--dry-run");
|
||||
|
||||
const { version } = await Bun.file("apps/server/package.json").json();
|
||||
const tag = `v${version}`;
|
||||
|
||||
const packageDirs = new Map<string, string>();
|
||||
for await (const file of new Bun.Glob("{apps,packages}/*/package.json").scan()) {
|
||||
const { name } = await Bun.file(file).json();
|
||||
packageDirs.set(name, path.dirname(file));
|
||||
}
|
||||
|
||||
// "### Minor Changes" → its entries, deduplicated across changelogs (a changeset naming several
|
||||
// packages in the group writes the same entry to each of them)
|
||||
const entriesByType = new Map<string, Set<string>>();
|
||||
for (const name of changesetConfig.fixed[0]) {
|
||||
const changelog = Bun.file(`${packageDirs.get(name)}/CHANGELOG.md`);
|
||||
if (!(await changelog.exists())) continue;
|
||||
|
||||
const lines = (await changelog.text()).split("\n");
|
||||
const start = lines.indexOf(`## ${version}`);
|
||||
if (start === -1) continue;
|
||||
const end = lines.findIndex((line, i) => i > start && line.startsWith("## "));
|
||||
|
||||
let entries: Set<string> | undefined;
|
||||
let entry: string[] = [];
|
||||
const flush = () => {
|
||||
const text = entry.join("\n").trimEnd();
|
||||
if (text && !DEPENDENCY_ENTRY.test(text)) entries?.add(text);
|
||||
entry = [];
|
||||
};
|
||||
for (const line of lines.slice(start + 1, end === -1 ? undefined : end)) {
|
||||
if (line.startsWith("### ")) {
|
||||
flush();
|
||||
const type = line.slice(4).trim();
|
||||
entries = entriesByType.get(type) ?? new Set();
|
||||
entriesByType.set(type, entries);
|
||||
} else if (line.startsWith("- ")) {
|
||||
flush();
|
||||
entry.push(line);
|
||||
} else if (entry.length > 0) {
|
||||
entry.push(line);
|
||||
}
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
const notes = CHANGE_TYPES.filter((type) => entriesByType.get(type)?.size)
|
||||
.map((type) => `### ${type}\n\n${[...(entriesByType.get(type) ?? [])].join("\n")}`)
|
||||
.join("\n\n");
|
||||
|
||||
if (dryRun) {
|
||||
console.log(`${tag}\n\n${notes || "(no changelog entries; GitHub would generate notes)"}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// --exit-code: 0 when the tag exists, 2 when it doesn't
|
||||
const remoteTag = await $`git ls-remote --exit-code --tags origin refs/tags/${tag}`
|
||||
.nothrow()
|
||||
.quiet();
|
||||
if (remoteTag.exitCode === 0) {
|
||||
console.log(`${tag} has already been released.`);
|
||||
process.exit(0);
|
||||
}
|
||||
if (remoteTag.exitCode !== 2) {
|
||||
throw new Error(`git ls-remote failed: ${remoteTag.stderr.toString()}`);
|
||||
}
|
||||
|
||||
const target = (await $`git rev-parse HEAD`.text()).trim();
|
||||
const args = ["release", "create", tag, "--title", tag, "--target", target];
|
||||
if (version.includes("-")) args.push("--prerelease");
|
||||
args.push(...(notes ? ["--notes", notes] : ["--generate-notes"]));
|
||||
await $`gh ${args}`;
|
||||
console.log(`Released ${tag}.`);
|
||||
|
||||
// Tags created with the workflow's GITHUB_TOKEN don't trigger other workflows, so start the
|
||||
// Docker build for this tag explicitly.
|
||||
await $`gh workflow run docker.yml --ref ${tag}`;
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Applies pending changesets (version bumps + changelogs), then syncs the other files that record
|
||||
* the version. The Release workflow runs this to build the "Version Packages" PR.
|
||||
*
|
||||
* Usage: bun run version-packages (needs GITHUB_TOKEN for @changesets/changelog-github)
|
||||
*/
|
||||
|
||||
import { $ } from "bun";
|
||||
|
||||
await $`changeset version`;
|
||||
|
||||
const { version } = await Bun.file("apps/server/package.json").json();
|
||||
|
||||
const specPath = "docs/public/openapi.json";
|
||||
const spec = await Bun.file(specPath).json();
|
||||
spec.info.version = version;
|
||||
await Bun.write(specPath, JSON.stringify(spec, null, 2));
|
||||
|
||||
// bun.lock records every workspace's version; sync it now rather than in someone's next PR
|
||||
await $`bun install --lockfile-only`;
|
||||
Reference in new issue
Block a user