Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 13 additions & 21 deletions .github/workflows/chrome-web-store.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,20 @@ jobs:
environment: ${{ inputs.channel }}
env:
BACKEND_PUBLIC_URL: ${{ vars.BACKEND_PUBLIC_URL || secrets.BACKEND_PUBLIC_URL }}
CWS_CLIENT_ID: ${{ secrets.CWS_CLIENT_ID }}
CWS_CLIENT_SECRET: ${{ secrets.CWS_CLIENT_SECRET }}
CWS_EXTENSION_ID: ${{ vars.CWS_EXTENSION_ID || secrets.CWS_EXTENSION_ID }}
CWS_PUBLISHER_ID: ${{ vars.CWS_PUBLISHER_ID || secrets.CWS_PUBLISHER_ID }}
CWS_REFRESH_TOKEN: ${{ secrets.CWS_REFRESH_TOKEN }}
steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Authenticate Chrome Web Store service account
id: auth
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.CWS_SERVICE_ACCOUNT_JSON }}
token_format: access_token
access_token_scopes: https://www.googleapis.com/auth/chromewebstore

- name: Set up Node
uses: actions/setup-node@v4
with:
Expand Down Expand Up @@ -70,36 +75,23 @@ jobs:
(cd apps/extension/dist && zip -qr "../../../artifacts/${artifact_name}.zip" .)
echo "zip_path=artifacts/${artifact_name}.zip" >> "$GITHUB_OUTPUT"

- name: Request Chrome Web Store access token
id: token
- name: Validate Chrome Web Store configuration
run: |
: "${CWS_PUBLISHER_ID:?CWS_PUBLISHER_ID is required}"
: "${CWS_EXTENSION_ID:?CWS_EXTENSION_ID is required}"
: "${CWS_CLIENT_ID:?CWS_CLIENT_ID is required}"
: "${CWS_CLIENT_SECRET:?CWS_CLIENT_SECRET is required}"
: "${CWS_REFRESH_TOKEN:?CWS_REFRESH_TOKEN is required}"
curl -fsS "https://oauth2.googleapis.com/token" \
-d "client_id=${CWS_CLIENT_ID}" \
-d "client_secret=${CWS_CLIENT_SECRET}" \
-d "refresh_token=${CWS_REFRESH_TOKEN}" \
-d "grant_type=refresh_token" \
-o token.json
access_token="$(node -e "const fs=require('node:fs'); console.log(JSON.parse(fs.readFileSync('token.json', 'utf8')).access_token)")"
echo "::add-mask::${access_token}"
echo "access_token=${access_token}" >> "$GITHUB_OUTPUT"

- name: Check current store version
run: |
curl -fsS \
-H "Authorization: Bearer ${{ steps.token.outputs.access_token }}" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
"https://chromewebstore.googleapis.com/v2/publishers/${CWS_PUBLISHER_ID}/items/${CWS_EXTENSION_ID}:fetchStatus" \
-o cws-status.json
node scripts/compare-extension-version.mjs apps/extension/dist/manifest.json cws-status.json

- name: Upload package
run: |
curl -fsS \
-H "Authorization: Bearer ${{ steps.token.outputs.access_token }}" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-H "Content-Type: application/zip" \
-X POST \
--upload-file "${{ steps.package.outputs.zip_path }}" \
Expand All @@ -110,7 +102,7 @@ jobs:
for attempt in $(seq 1 20); do
sleep 5
curl -fsS \
-H "Authorization: Bearer ${{ steps.token.outputs.access_token }}" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
"https://chromewebstore.googleapis.com/v2/publishers/${CWS_PUBLISHER_ID}/items/${CWS_EXTENSION_ID}:fetchStatus" \
-o cws-status-after-upload.json
upload_state="$(node -e "const fs=require('node:fs'); const status=JSON.parse(fs.readFileSync('cws-status-after-upload.json', 'utf8')); console.log(status.lastAsyncUploadState || 'SUCCESS');")"
Expand All @@ -129,7 +121,7 @@ jobs:
run: |
node -e "const fs=require('node:fs'); fs.writeFileSync('publish-body.json', JSON.stringify({ publishType: process.env.PUBLISH_TYPE }));"
curl -fsS \
-H "Authorization: Bearer ${{ steps.token.outputs.access_token }}" \
-H "Authorization: Bearer ${{ steps.auth.outputs.access_token }}" \
-H "Content-Type: application/json" \
-X POST \
-d @publish-body.json \
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ jobs:
- name: Install just
run: cargo install just --locked

- name: Install pnpm
run: npm install -g pnpm@11.3.0

- name: Install JS dependencies
run: npx pnpm@latest install --frozen-lockfile
run: pnpm install --frozen-lockfile

- name: Run repo checks
run: just check
8 changes: 2 additions & 6 deletions .release-inputs.example.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ BETA_BACKEND_PUBLIC_URL=https://beta.example.com
BETA_CADDY_EMAIL=admin@example.com
BETA_CWS_PUBLISHER_ID=publisher-id
BETA_CWS_EXTENSION_ID=extension-id
BETA_CWS_CLIENT_ID=client-id
BETA_CWS_CLIENT_SECRET=client-secret
BETA_CWS_REFRESH_TOKEN=refresh-token
BETA_CWS_SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"project-id","private_key_id":"key-id","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n","client_email":"chrome-web-store@project-id.iam.gserviceaccount.com","client_id":"client-id","token_uri":"https://oauth2.googleapis.com/token"}'
BETA_VPS_HOST=beta-vps.example.com
BETA_VPS_USER=deploy
BETA_VPS_SSH_KEY="-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
Expand All @@ -16,9 +14,7 @@ PRODUCTION_BACKEND_PUBLIC_URL=https://watch.example.com
PRODUCTION_CADDY_EMAIL=admin@example.com
PRODUCTION_CWS_PUBLISHER_ID=publisher-id
PRODUCTION_CWS_EXTENSION_ID=extension-id
PRODUCTION_CWS_CLIENT_ID=client-id
PRODUCTION_CWS_CLIENT_SECRET=client-secret
PRODUCTION_CWS_REFRESH_TOKEN=refresh-token
PRODUCTION_CWS_SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"project-id","private_key_id":"key-id","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n","client_email":"chrome-web-store@project-id.iam.gserviceaccount.com","client_id":"client-id","token_uri":"https://oauth2.googleapis.com/token"}'
PRODUCTION_VPS_HOST=production-vps.example.com
PRODUCTION_VPS_USER=deploy
PRODUCTION_VPS_SSH_KEY="-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----"
2 changes: 1 addition & 1 deletion docs/chrome-store.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Selected release strategy:

- Alpha and beta use one private Chrome Web Store item limited to trusted testers.
- Beta uses one private Chrome Web Store item limited to trusted testers.
- A separate beta item is only needed if beta must run for a long time in parallel with production.
- Production starts as an unlisted item for the first public release window, then can move to public distribution after the support and review process is stable.

Expand Down
11 changes: 5 additions & 6 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,7 @@ Environment secrets:
- `VPS_SSH_KEY`
- `BACKEND_PUBLIC_URL` if not stored as an environment variable
- `CADDY_EMAIL` if not stored as an environment variable
- `CWS_CLIENT_ID`
- `CWS_CLIENT_SECRET`
- `CWS_REFRESH_TOKEN`
- `CWS_SERVICE_ACCOUNT_JSON`

Environment variables:

Expand All @@ -171,13 +169,14 @@ gh variable set CWS_EXTENSION_ID --env beta --body extension-id
gh secret set VPS_HOST --env beta --body vps.example.com
gh secret set VPS_USER --env beta --body deploy
gh secret set VPS_SSH_KEY --env beta < ~/.ssh/videotogether_deploy
gh secret set CWS_CLIENT_ID --env beta --body client-id
gh secret set CWS_CLIENT_SECRET --env beta --body client-secret
gh secret set CWS_REFRESH_TOKEN --env beta --body refresh-token
gh secret set CWS_SERVICE_ACCOUNT_JSON --env beta --body "$(jq -c . service-account.json)"
```

Repeat for `production` with its production backend URL, VPS target, and Chrome Web Store item ID.

The Chrome Web Store service account must also be granted access in the Chrome
Web Store Developer Dashboard under the publisher account settings.

You can also set the same inputs from local environment variables or a dotenv-style file:

```bash
Expand Down
41 changes: 31 additions & 10 deletions docs/pre-release-goals.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,24 @@ VideoTogether Lite backend and Chrome extension. The target release path is:
- Docs: wording updated so the repo describes the current implementation rather
than its fork-era history.

## Current Status

- [x] Beta VPS is provisioned.
- [x] Docker is installed on the beta VPS.
- [x] Cloudflare DNS points the beta backend domain to the VPS.
- [x] GitHub Actions `beta` environment inputs are configured.
- [x] Chrome Web Store beta submission has been sent for review.
- [ ] Chrome Web Store beta review is approved.
- [x] Production environment setup is postponed until beta is healthy.

## Release Principles

- Keep the backend to one running replica until room/session state moves out of
process memory.
- Treat the production backend URL and Chrome Web Store item IDs as
environment-specific release inputs.
- Treat beta as the active release target.
- Keep production backend URL, production Chrome Web Store item IDs, production
tagged deploys, and public production release work deferred until beta is
deployed and tested.
- Keep secrets in GitHub Actions environments, not checked-in files.
- Make beta testing explicit: separate backend host, tester-only store
distribution, and clear feedback instructions.
Expand Down Expand Up @@ -69,34 +81,35 @@ VideoTogether Lite backend and Chrome extension. The target release path is:
- Run `docker compose up -d`.
- Verify `/healthz` through the public Caddy URL.

- [x] Configure GitHub Actions environments.
- [x] Configure active GitHub Actions environments.
- `beta`
- `production`
- `production` remains deferred for now.

- [x] Define required secrets and variables per environment.
- [x] Define required beta secrets and variables.
- `VPS_HOST`
- `VPS_USER`
- `VPS_SSH_KEY`
- `BACKEND_PUBLIC_URL`
- `CWS_PUBLISHER_ID`
- `CWS_EXTENSION_ID`
- Chrome Web Store OAuth/client credentials
- Chrome Web Store service-account credentials
- any registry credentials not covered by `GITHUB_TOKEN`

- [x] Gate production deploys.
- [x] Keep production deploy gates ready for later.
- Use required reviewers for the `production` environment.
- Add deployment concurrency so two production deploys cannot race.
- Do not require production environment values for the beta unblock.

## Extension Release Channels

- [x] Decide Chrome Web Store item strategy.
- For short beta testing: use one private item with trusted testers.
- For long-running beta in parallel with production: use a separate beta item.
- For production: decide public vs unlisted.
- Production distribution stays deferred until beta is stable.

- [x] Add channel-specific build support.
- `beta` builds point at the beta backend URL.
- `production` builds point at the production backend URL.
- `production` build wiring exists, but production values are postponed.
- Artifact names include channel, manifest version, and git SHA.

- [x] Add extension package workflow.
Expand Down Expand Up @@ -209,14 +222,19 @@ VideoTogether Lite backend and Chrome extension. The target release path is:
- [ ] `just check` passes locally and in GitHub Actions.
- [x] Backend Docker image runs locally.
- [x] Caddy reverse proxy handles HTTP and WebSocket traffic.
- [x] Beta VPS is provisioned.
- [x] Docker is installed on the beta VPS.
- [x] Cloudflare DNS points the beta backend domain to the VPS.
- [x] GitHub Actions `beta` environment inputs are configured.
- [ ] Beta backend is deployed from GitHub Actions.
- [ ] Beta extension ZIP is built by GitHub Actions.
- [x] Beta Chrome Web Store submission is pending review.
- [ ] Beta Chrome Web Store item is private and limited to trusted testers.
- [ ] Create/join/update/leave room flow passes through the public beta URL.
- [x] YouTube, Bilibili, and basic HTML video smoke tests pass.
- [ ] Privacy and tester docs are published or linked from the store listing.

## Verification Before Production
## Deferred Production

- [ ] Production backend is deployed from a tagged image.
- [ ] Production extension build uses the production backend URL.
Expand All @@ -225,6 +243,9 @@ VideoTogether Lite backend and Chrome extension. The target release path is:
- [x] Rollback path is tested for backend and extension package.
- [x] Monitoring/log review process is documented.

Production work resumes only after beta backend deployment, Chrome Web Store
beta approval, and public beta smoke tests pass.

## References

- Chrome Web Store publishing:
Expand Down
2 changes: 1 addition & 1 deletion docs/release-checklist.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Release Checklist

Use this file for beta and production releases. The backend URL, Chrome Web Store item IDs, and OAuth credentials are environment-specific values.
Use this file for beta and production releases. The backend URL, Chrome Web Store item IDs, and Chrome Web Store service-account credentials are environment-specific values.

## Workflow Runner

Expand Down
2 changes: 1 addition & 1 deletion docs/zh-cn/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ VideoTogether Lite 会在邀请码房间中同步你主动选择并共享的视
3. 启用开发者模式。
4. 加载已解压的 `apps/extension/dist`。

Alpha 或 beta 测试时,请使用维护者提供的私有 Chrome Web Store 测试链接安装。
Beta 测试时,请使用维护者提供的私有 Chrome Web Store 测试链接安装。

## 创建房间

Expand Down
4 changes: 1 addition & 3 deletions scripts/audit-release-readiness.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ const requiredWorkflowFiles = [
const requiredEnvironmentInputs = [
"BACKEND_PUBLIC_URL",
"CADDY_EMAIL",
"CWS_CLIENT_ID",
"CWS_CLIENT_SECRET",
"CWS_EXTENSION_ID",
"CWS_PUBLISHER_ID",
"CWS_REFRESH_TOKEN",
"CWS_SERVICE_ACCOUNT_JSON",
"VPS_HOST",
"VPS_SSH_KEY",
"VPS_USER"
Expand Down
Loading
Loading