chore: prepare repo for public release

This commit is contained in:
xixu-me committed 2026-04-11 16:20:17 +08:00
1 parent 8b7c333a5f
commit f7cbed956d
14 files changed
+377 -13067

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
.git
.github
.wrangler
coverage
dist
dist-oci
node_modules
test
worker-configuration.d.ts
.dev.vars
.dev.vars.*
.env
.env.local
.env.*.local
.DS_Store
*.log
+15
View File
@@ -43,3 +43,18 @@ updates:
- "patch" - "patch"
patterns: patterns:
- "*" - "*"
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "10:00"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 2
labels:
- "dependencies"
- "docker"
commit-message:
prefix: "build"
include: "scope"
+2 -8
View File
@@ -24,10 +24,7 @@ jobs:
github.event.pull_request.draft == false && github.event.pull_request.draft == false &&
github.event.pull_request.base.ref == 'main' && github.event.pull_request.base.ref == 'main' &&
contains(github.event.pull_request.labels.*.name, 'dependencies') && contains(github.event.pull_request.labels.*.name, 'dependencies') &&
( github.event.pull_request.user.login == 'dependabot[bot]'
github.event.pull_request.head.repo.full_name == github.repository ||
github.event.pull_request.user.login == 'dependabot[bot]'
)
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 5
@@ -43,10 +40,7 @@ jobs:
github.event.action == 'unlabeled' && github.event.action == 'unlabeled' &&
github.event.label.name == 'dependencies' && github.event.label.name == 'dependencies' &&
github.event.pull_request.base.ref == 'main' && github.event.pull_request.base.ref == 'main' &&
( github.event.pull_request.user.login == 'dependabot[bot]'
github.event.pull_request.head.repo.full_name == github.repository ||
github.event.pull_request.user.login == 'dependabot[bot]'
)
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 5
+4 -32
View File
@@ -2,41 +2,13 @@ name: CI
on: on:
pull_request: pull_request:
push: merge_group:
branches:
- main
permissions: permissions:
contents: read contents: read
jobs: jobs:
validate: validate:
runs-on: ubuntu-latest permissions:
timeout-minutes: 15 contents: read
uses: ./.github/workflows/validate-reusable.yml
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.12
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Generate Worker types
run: bun run cf-typegen
- name: Type check
run: bun run typecheck
- name: Run Worker tests
run: bun run test
- name: Check generated Worker types
run: bun run cf-typegen:check
- name: Verify Wrangler dry-run build
run: bunx wrangler deploy --dry-run
-45
View File
@@ -1,45 +0,0 @@
name: Deploy
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: production-deploy
cancel-in-progress: false
jobs:
deploy:
if: ${{ secrets.CLOUDFLARE_API_TOKEN != '' && secrets.CLOUDFLARE_ACCOUNT_ID != '' }}
runs-on: ubuntu-latest
timeout-minutes: 20
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.12
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Generate Worker types
run: bun run cf-typegen
- name: Validate before deploy
run: bun run check
- name: Deploy Worker
run: bun run deploy
+115
View File
@@ -0,0 +1,115 @@
name: Release
on:
push:
branches:
- main
workflow_dispatch:
inputs:
deploy_worker:
description: Deploy the Worker to production
required: false
default: false
type: boolean
publish_oci:
description: Publish the OCI image to GHCR
required: false
default: false
type: boolean
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
validate:
permissions:
contents: read
uses: ./.github/workflows/validate-reusable.yml
deploy-worker:
needs:
- validate
if: >
needs.validate.result == 'success' &&
secrets.CLOUDFLARE_API_TOKEN != '' &&
secrets.CLOUDFLARE_ACCOUNT_ID != '' &&
(
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
(github.event_name == 'workflow_dispatch' && inputs.deploy_worker && github.ref == 'refs/heads/main')
)
runs-on: ubuntu-latest
timeout-minutes: 20
environment: production
permissions:
contents: read
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.12
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Generate Worker types
run: bun run cf-typegen
- name: Deploy Worker
run: bun run deploy
publish-oci:
needs:
- validate
if: >
needs.validate.result == 'success' &&
(
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
(
github.event_name == 'workflow_dispatch' &&
inputs.publish_oci &&
github.ref == 'refs/heads/main'
)
)
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
packages: write
env:
IMAGE_NAME: ghcr.io/xixu-me/paseo-relay
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f
- name: Log in to GHCR
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and publish OCI image
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8
with:
context: .
file: ./Dockerfile
push: true
provenance: true
sbom: true
platforms: linux/amd64,linux/arm64
tags: ${{ env.IMAGE_NAME }}:main
+111
View File
@@ -0,0 +1,111 @@
name: Validate Reusable
on:
workflow_call:
jobs:
validate-worker:
name: validate-worker
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.12
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Generate Worker types
run: bun run cf-typegen
- name: Type check
run: bun run typecheck
- name: Run Worker tests
run: bun run test
- name: Check generated Worker types
run: bun run cf-typegen:check
- name: Verify Wrangler dry-run build
run: bunx wrangler deploy --dry-run
validate-oci:
name: validate-oci
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.3.12
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Build OCI image
run: docker build -t paseo-relay:ci .
- name: Smoke test OCI image
shell: bash
run: |
set -euo pipefail
cleanup() {
status=$?
if [[ $status -ne 0 ]]; then
docker logs paseo-relay-ci || true
fi
docker rm -f paseo-relay-ci >/dev/null 2>&1 || true
exit $status
}
trap cleanup EXIT
docker run -d --name paseo-relay-ci -p 8080:8080 paseo-relay:ci >/dev/null
for attempt in {1..20}; do
status="$(curl --silent --show-error --output health.json --write-out '%{http_code}' http://127.0.0.1:8080/health || true)"
if [[ "$status" == "200" ]]; then
break
fi
sleep 1
done
test "${status:-}" = "200"
grep -q '"status":"ok"' health.json
python3 - <<'PY'
import socket
request = (
"GET /ws?role=server&serverId=smoke&v=2 HTTP/1.1\r\n"
"Host: 127.0.0.1:8080\r\n"
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
)
with socket.create_connection(("127.0.0.1", 8080), timeout=5) as conn:
conn.sendall(request.encode("ascii"))
response = conn.recv(4096).decode("latin1", "replace")
print(response)
if not response.startswith("HTTP/1.1 101"):
raise SystemExit("expected HTTP/1.1 101 Switching Protocols")
PY
+4
View File
@@ -2,7 +2,11 @@ node_modules/
.wrangler/ .wrangler/
coverage/ coverage/
dist/ dist/
dist-oci/
worker-configuration.d.ts worker-configuration.d.ts
.dev.vars .dev.vars
.dev.vars.*
.env .env
.env.local
.env.*.local
.DS_Store .DS_Store
+37
View File
@@ -0,0 +1,37 @@
FROM oven/bun:1.3.12-alpine AS builder
WORKDIR /app
COPY package.json bun.lock wrangler.jsonc tsconfig.json ./
COPY src ./src
RUN bun install --frozen-lockfile
RUN bunx wrangler deploy --dry-run --outdir=dist-oci
FROM node:25-slim AS runtime
ARG TARGETARCH
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/* && \
case "${TARGETARCH}" in \
amd64) WORKERD_PKG="@cloudflare/workerd-linux-64" ;; \
arm64) WORKERD_PKG="@cloudflare/workerd-linux-arm64" ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \
esac && \
npm install -g "${WORKERD_PKG}" && \
ln -s "/usr/local/lib/node_modules/${WORKERD_PKG}/bin/workerd" /usr/local/bin/workerd
WORKDIR /worker
COPY --from=builder /app/dist-oci ./dist-oci
COPY config.capnp ./config.capnp
RUN mkdir -p /var/lib/paseo-relay/do && \
chown -R node:node /worker /var/lib/paseo-relay
VOLUME ["/var/lib/paseo-relay/do"]
EXPOSE 8080
USER node
CMD ["workerd", "serve", "config.capnp", "--directory-path", "relay-storage=/var/lib/paseo-relay/do"]
+51
View File
@@ -0,0 +1,51 @@
using Workerd = import "/workerd/workerd.capnp";
const config :Workerd.Config = (
services = [
(
name = "main",
worker = .worker,
),
(
name = "relay-storage",
disk = (
writable = true,
allowDotfiles = true,
),
),
],
sockets = [
(
name = "http",
address = "*:8080",
service = "main",
http = (),
),
],
);
const worker :Workerd.Worker = (
modules = [
(
name = "worker",
esModule = embed "dist-oci/index.js",
),
],
compatibilityDate = "2026-04-11",
durableObjectNamespaces = [
(
className = "RelayDurableObject",
uniqueKey = "paseo-relay-oci-relaydurableobject-v1",
enableSql = true,
),
],
bindings = [
(
name = "RELAY",
durableObjectNamespace = "RelayDurableObject",
),
],
durableObjectStorage = (
localDisk = "relay-storage",
),
);
+11 -1
View File
@@ -1,8 +1,15 @@
{ {
"name": "paseo-relay", "name": "paseo-relay",
"private": true, "private": true,
"description": "Self-hosted Paseo relay.",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"keywords": [
"paseo",
"relay",
"cloudflare-workers",
"durable-objects"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/xixu-me/paseo-relay.git" "url": "https://github.com/xixu-me/paseo-relay.git"
@@ -19,7 +26,10 @@
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"cf-typegen": "bunx wrangler types", "cf-typegen": "bunx wrangler types",
"cf-typegen:check": "bunx wrangler types --check" "cf-typegen:check": "bunx wrangler types --check",
"oci:bundle": "bunx wrangler deploy --dry-run --outdir=dist-oci",
"oci:build": "bun run oci:bundle && docker build -t paseo-relay:latest .",
"oci:run": "docker run --rm -p 8080:8080 -v paseo-relay-data:/var/lib/paseo-relay/do paseo-relay:latest"
}, },
"dependencies": { "dependencies": {
"@getpaseo/relay": "^0.1.52" "@getpaseo/relay": "^0.1.52"
+11
View File
@@ -50,4 +50,15 @@ describe("paseo relay worker", () => {
expect(response.status).toBe(101); expect(response.status).toBe(101);
expect(response.webSocket).toBeDefined(); expect(response.webSocket).toBeDefined();
}); });
it("upgrades a valid websocket relay v2 control request", async () => {
const response = await SELF.fetch("http://example.com/ws?role=server&serverId=daemon-1&v=2", {
headers: {
Upgrade: "websocket",
},
});
expect(response.status).toBe(101);
expect(response.webSocket).toBeDefined();
});
}); });
-12979
View File
File diff suppressed because it is too large. Load diff
-2
View File
@@ -27,12 +27,10 @@
"logs": { "logs": {
"enabled": true, "enabled": true,
"head_sampling_rate": 1, "head_sampling_rate": 1,
"persist": true,
"invocation_logs": true, "invocation_logs": true,
}, },
"traces": { "traces": {
"enabled": true, "enabled": true,
"persist": true,
"head_sampling_rate": 1, "head_sampling_rate": 1,
}, },
}, },