diff --git a/.github/workflows/awesome-lists.yml b/.github/workflows/awesome-lists.yml index f495aeb..621f63b 100644 --- a/.github/workflows/awesome-lists.yml +++ b/.github/workflows/awesome-lists.yml @@ -26,7 +26,7 @@ jobs: suggest: name: Open the list pull requests if: ${{ github.server_url != 'https://github.com' && github.repository == 'codician/diffinery' }} - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} environment: github-mirror timeout-minutes: 10 steps: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4320b9b..05e6d3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,8 +4,10 @@ name: CI # workflow (e2e.yml), so deploys don't wait for it too. # # Every job of this repository's workflows runs on the label in the repository variable -# DIFFINERY_RUNS_ON, else ubuntu-latest (a hosted runner on Diffinery too). The GitHub copy -# sets `diffinery` to run on diffinery.com's GitHub runners; forks need nothing. +# DIFFINERY_RUNS_ON, else on `diffinery` (diffinery.com's GitHub runners) in +# codician-team/diffinery, else ubuntu-latest (a hosted runner on Diffinery too). GitHub passes +# no variables to pull requests from forks, so without that middle default they would run on +# GitHub's own runners. Other copies of the repository need nothing. on: push: @@ -24,7 +26,7 @@ jobs: # tests together took longer than either does alone. typecheck: name: Typecheck and bundle - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 20 steps: - uses: actions/checkout@v4 @@ -44,7 +46,7 @@ jobs: test: name: Unit tests (${{ matrix.shard }}/2) - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 20 # Two halves of every package's test files, side by side: CI is the first step of a deploy. strategy: diff --git a/.github/workflows/d1-replication.yml b/.github/workflows/d1-replication.yml index c922ba9..35ed13a 100644 --- a/.github/workflows/d1-replication.yml +++ b/.github/workflows/d1-replication.yml @@ -18,7 +18,7 @@ permissions: jobs: check: name: Check sessions on a test database - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} # The Cloudflare secrets are the production environment's. environment: production timeout-minutes: 20 @@ -81,7 +81,7 @@ jobs: name: Turn on read replication in production needs: check if: ${{ inputs.enable_production }} - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} environment: name: production url: https://${{ vars.DIFFINERY_DOMAIN || 'diffinery.com' }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8b909fc..a6c43e6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,8 +10,9 @@ name: Deploy # DIFFINERY_GITHUB_APP_ID, DIFFINERY_GITHUB_APP_SLUG, DIFFINERY_GITHUB_APP_CLIENT_ID, # DIFFINERY_GITHUB_APP_URL the GitHub App of GitHub runners (docs/self-hosting/configuration.md) # DIFFINERY_RUNS_ON the runner label of every job in this repository's workflows (default -# ubuntu-latest). `diffinery` runs them on a Diffinery instance's GitHub runners; -# set it back to ubuntu-latest to deploy while those can't start. +# ubuntu-latest, and `diffinery` in codician-team/diffinery, whose fork pull +# requests get no variables). `diffinery` runs them on a Diffinery instance's +# GitHub runners; set it back to ubuntu-latest to deploy while those can't start. # # It deploys a main commit only once CI (typecheck, unit tests and the Worker bundle) has # passed on it, so it doesn't run those itself. End-to-end tests run alongside the deploy. @@ -42,7 +43,7 @@ jobs: # codician/diffinery, an older copy of this repository: GitHub deploys diffinery.com, # and that copy lacks the GitHub App's variables a deploy needs. if: ${{ !(github.server_url == 'https://diffinery.com' && github.repository == 'codician/diffinery') && (github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push')) }} - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 5 outputs: deploy: ${{ steps.tip.outputs.deploy }} @@ -72,7 +73,7 @@ jobs: name: Deploy to Cloudflare needs: gate if: ${{ needs.gate.outputs.deploy == 'true' }} - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 45 environment: name: production diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 839c459..43ea19d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -19,7 +19,7 @@ jobs: # Only the last shard needs a browser (passkeys). e2e: name: End-to-end (${{ matrix.name }}) - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 20 strategy: fail-fast: false @@ -88,11 +88,11 @@ jobs: # Docker as the hosted runner image runs it, with this change's docker wrapper, daemon config # and agent (its registry relay). It needs a Diffinery hosted runner, for the real dockerd: on - # Diffinery, or on GitHub with DIFFINERY_RUNS_ON=diffinery. On GitHub's own runners it fails. + # Diffinery, or on GitHub on the `diffinery` label. On GitHub's own runners it fails. # A daemon an earlier step started is stopped first, so the first uses below start it afresh. runner-docker: name: End-to-end (runner Docker) - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 20 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/github-community.yml b/.github/workflows/github-community.yml index 823225e..1d88430 100644 --- a/.github/workflows/github-community.yml +++ b/.github/workflows/github-community.yml @@ -19,7 +19,7 @@ jobs: setup: name: Labels, welcome discussion and roadmap issue if: ${{ github.server_url != 'https://github.com' && github.repository == 'codician/diffinery' }} - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} environment: github-mirror timeout-minutes: 5 steps: diff --git a/.github/workflows/live-test.yml b/.github/workflows/live-test.yml index cebd166..fc7d3de 100644 --- a/.github/workflows/live-test.yml +++ b/.github/workflows/live-test.yml @@ -31,7 +31,7 @@ jobs: name: Containers, registry, sandboxes, webhooks, schedules, LFS and pull requests # Runs on the host that deploys production (see the gate in deploy.yml). if: ${{ github.event_name == 'workflow_dispatch' || (github.server_url == 'https://diffinery.com' && github.event.workflow_run.conclusion == 'success') }} - runs-on: ${{ vars.DIFFINERY_RUNS_ON || 'ubuntu-latest' }} + runs-on: ${{ vars.DIFFINERY_RUNS_ON || (github.repository == 'codician-team/diffinery' && 'diffinery') || 'ubuntu-latest' }} timeout-minutes: 45 environment: production env: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0047ccc..509dc96 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,6 +57,12 @@ Pull requests must pass CI (typecheck, the Worker bundle and the unit tests) and end-to-end shards before they merge. Tests must not depend on the speed of the machine they run on. +CI runs on Diffinery's own runners for GitHub (`runs-on: diffinery`), pull requests from +forks included: in this repository the workflows default to that label when the +repository variable `DIFFINERY_RUNS_ON`, which GitHub doesn't pass to forks, is missing. +The "End-to-end (runner Docker)" job needs those runners and fails on GitHub's own. A +maintainer may have to approve the workflows of your first pull request before they run. + ## Conventions - **TypeScript everywhere**, in strict mode, sharing code through the workspace packages in diff --git a/apps/diffinery/docs/actions/github-runners.md b/apps/diffinery/docs/actions/github-runners.md index a1be05e..f29a61e 100644 --- a/apps/diffinery/docs/actions/github-runners.md +++ b/apps/diffinery/docs/actions/github-runners.md @@ -70,7 +70,15 @@ To move every job at once, and to be able to move them back without a commit, re runs-on: ${{ vars.RUNS_ON || 'ubuntu-latest' }} ``` -Then set the variable to `diffinery` (`gh variable set RUNS_ON --body diffinery`). Setting it back to `ubuntu-latest` returns the jobs to GitHub-hosted runners, for example to keep a deploy working while Diffinery can't start runners. Forks without the variable keep running on GitHub. Diffinery's own repository does this with `DIFFINERY_RUNS_ON`. +Then set the variable to `diffinery` (`gh variable set RUNS_ON --body diffinery`). Setting it back to `ubuntu-latest` returns the jobs to GitHub-hosted runners, for example to keep a deploy working while Diffinery can't start runners. Forks without the variable keep running on GitHub. + +GitHub passes no repository variables to workflows of pull requests from forks, so those jobs run on `ubuntu-latest` too. To run them on Diffinery as well, default to `diffinery` in your own repository, and keep `ubuntu-latest` for copies of it: + +```yaml + runs-on: ${{ vars.RUNS_ON || (github.repository == 'my-org/my-repo' && 'diffinery') || 'ubuntu-latest' }} +``` + +Diffinery's own repository does this with `DIFFINERY_RUNS_ON`. Read [Security](#security) first: anyone who opens a pull request can then use your minutes. ## The machine diff --git a/apps/diffinery/docs/self-hosting/deploy.md b/apps/diffinery/docs/self-hosting/deploy.md index fa40ed8..ea160a1 100644 --- a/apps/diffinery/docs/self-hosting/deploy.md +++ b/apps/diffinery/docs/self-hosting/deploy.md @@ -111,7 +111,7 @@ In your copy's repository settings, add: | `MAIL_DOMAIN` | Variable, optional | The domain email is sent from, when it isn't `DIFFINERY_DOMAIN`. | | `ACCESS_TEAM_DOMAIN` | Variable, optional | Your Zero Trust team domain, for example `example.cloudflareaccess.com`. Needed only when the token can't read it. | | `DIFFINERY_GITHUB_APP_ID`, `DIFFINERY_GITHUB_APP_SLUG`, `DIFFINERY_GITHUB_APP_CLIENT_ID`, `DIFFINERY_GITHUB_APP_URL` | Variables, optional | The GitHub App of [GitHub runners](/docs/self-hosting/configuration#github-runners). The workflow passes them to render-config as `GITHUB_APP_ID` and the rest. | -| `DIFFINERY_RUNS_ON` | Variable, optional | The `runs-on` label of every job in the repository's workflows. Without it, jobs run on `ubuntu-latest`. Set it to `diffinery` to run them on a Diffinery instance's [GitHub runners](/docs/actions/github-runners) once that instance's GitHub App is installed on the repository. Set it back to `ubuntu-latest` to deploy while those runners can't start, for example while the instance itself is down. | +| `DIFFINERY_RUNS_ON` | Variable, optional | The `runs-on` label of every job in the repository's workflows. Without it, jobs run on `ubuntu-latest` (in `codician-team/diffinery` itself, on `diffinery`, so pull requests from forks, which get no variables, run there too). Set it to `diffinery` to run them on a Diffinery instance's [GitHub runners](/docs/actions/github-runners) once that instance's GitHub App is installed on the repository. Set it back to `ubuntu-latest` to deploy while those runners can't start, for example while the instance itself is down. | The Deploy job runs in an environment named `production`, so you can also store the secrets as environment secrets there and add required reviewers to that environment. diff --git a/apps/diffinery/test/unit/contact.test.ts b/apps/diffinery/test/unit/contact.test.ts index d79919b..b2dce48 100644 --- a/apps/diffinery/test/unit/contact.test.ts +++ b/apps/diffinery/test/unit/contact.test.ts @@ -157,10 +157,11 @@ describe("sending without signing in", () => { await submitMessage(env, audit, input("ada@example.net", { message: "Second message, about another." }), null, "203.0.113.9"); expect(rows(DB)).toHaveLength(2); await confirmMessage(env, audit, tokenOf(queued[1])); - expect(rows(DB).map((r) => [r.message, r.state])).toEqual([ - ["First message, about one thing.", "unconfirmed"], - ["Second message, about another.", "new"], - ]); + // Both can have the same created_at millisecond, and then the random id orders them. + expect(Object.fromEntries(rows(DB).map((r) => [r.message, r.state]))).toEqual({ + "First message, about one thing.": "unconfirmed", + "Second message, about another.": "new", + }); }); it("limits one network, one address and the whole form per day, quietly past the last two", async () => {