Compare commits
2
Commits
v0.1.2
..
00e544125b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00e544125b | ||
|
|
95440b37ea |
@@ -1,13 +1,18 @@
|
||||
name: CI
|
||||
|
||||
# The server runs Gitea 1.27.2 (docs/development/security.md, "What this server honours"):
|
||||
# IMPORTANT — the currently inventoried server is Gitea 1.24.2, which *ignores* several keys used
|
||||
# below. Per https://docs.gitea.com/1.24/usage/actions/comparison, its unsupported workflow syntax
|
||||
# includes `permissions` / `jobs.<job_id>.permissions`, `concurrency`, and
|
||||
# `jobs.<job_id>.timeout-minutes`. They are declared here deliberately, so the workflow is already
|
||||
# correct on a supported release and on GitHub Actions, but on this server they are inert:
|
||||
#
|
||||
# * `concurrency:` is honoured: a newer push to a pull request cancels the run it supersedes, so a
|
||||
# cancelled run's own cleanup is never guaranteed (see the CI stack sweep in each stack job).
|
||||
# * `permissions:` is supported by 1.27 with Gitea's own scopes, but what the job token can do on
|
||||
# this server is not yet proven, so nothing here relies on it being read-only.
|
||||
# * `timeout-minutes:` is no longer listed as unsupported, but is not verified here either;
|
||||
# teardown relies on the stack sweep and the runner's own reaper.
|
||||
# * `permissions:` does NOT restrict the in-job `GITEA_TOKEN`. Job-token permission controls,
|
||||
# repository/owner defaults, and server-side maximum-permission clamping arrive in the 1.26
|
||||
# feature line (https://docs.gitea.com/1.26/usage/actions/token-permissions). Until the server
|
||||
# is upgraded and configured, the token's authority is whatever the server grants — not what
|
||||
# this file requests.
|
||||
# * `concurrency:` and `timeout-minutes:` are NOT bounded-execution guarantees here. Cancellation,
|
||||
# timeout, and teardown must be enforced by the trusted runner provisioner/reaper.
|
||||
#
|
||||
# `persist-credentials: false` below is unaffected by any of this and is enforced by the action
|
||||
# itself. See docs/development/security.md.
|
||||
|
||||
@@ -218,10 +218,9 @@ undeployed state, and the launch checklist in a single place. **CI is accepted a
|
||||
trusted-code-only boundary**: the self-hosted Gitea runner executes only code controlled and
|
||||
reviewed by trusted repository collaborators, and unknown or untrusted pull requests must not
|
||||
execute on it automatically. What remains is operational and cannot be proven from this repository —
|
||||
configuring and proving job-token authority server-side (the server now runs Gitea 1.27.2, which
|
||||
honours `concurrency` and documents `permissions`, but neither token limits nor `timeout-minutes` are
|
||||
verified on it yet), enforcing timeout and teardown in a trusted provisioner/reaper, and proving
|
||||
disposable runner isolation. Those are
|
||||
upgrading Gitea past 1.24.2 (which *ignores* the workflow's `permissions`, `concurrency`, and
|
||||
`timeout-minutes` declarations), clamping job-token authority server-side, enforcing timeout and
|
||||
teardown in a trusted provisioner/reaper, and proving disposable runner isolation. Those are
|
||||
prerequisites for **executing untrusted contributions**, alongside the deployment checklist that
|
||||
gates a **public launch**; moving future public contribution workflows to a service with isolated
|
||||
hosted runners is an acceptable alternative. **Neither set of requirements blocks Phase 6** — Surface
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/api",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"files": [
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/core",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"files": [
|
||||
|
||||
+2
-2
@@ -384,10 +384,10 @@ finds its files:
|
||||
```sh
|
||||
docker build \
|
||||
--file deploy/Dockerfile \
|
||||
--build-arg VERSION=0.1.2 \
|
||||
--build-arg VERSION=0.1.1 \
|
||||
--build-arg REVISION="$(git rev-parse HEAD)" \
|
||||
--build-arg SOURCE=https://git.example.org/your-org/ekklesia \
|
||||
--tag ekklesia:0.1.2 \
|
||||
--tag ekklesia:0.1.1 \
|
||||
.
|
||||
```
|
||||
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
# 0047: Every module proves itself inside Ekklesia, with one harness the platform publishes
|
||||
|
||||
- Status: Proposed
|
||||
- Date: 2026-10-05
|
||||
- Supersedes: none
|
||||
- Related: [ADR 0016](0016-module-database-access.md),
|
||||
[ADR 0021](0021-modules-as-hostable-products.md),
|
||||
[ADR 0028](0028-module-operational-lifecycle.md),
|
||||
[ADR 0030](0030-host-provided-module-object-storage.md),
|
||||
[ADR 0033](0033-display-kiosk-host-contract.md),
|
||||
[ADR 0036](0036-managed-device-identity-and-pairing.md),
|
||||
[ADR 0041](0041-module-browser-contributions.md),
|
||||
[ADR 0043](0043-display-offline-code-fallback.md),
|
||||
[ADR 0044](0044-self-hosted-reference-deployment.md),
|
||||
[ADR 0045](0045-split-dashboard-and-api-origins.md)
|
||||
|
||||
## Context
|
||||
|
||||
Modules are independently versioned products (ADR 0021). They live in their own repositories, and
|
||||
none of them can be built, run, or rendered without Ekklesia. The first production module, Signage,
|
||||
reached a church with three defects that only show when it runs **inside** Ekklesia, in a real
|
||||
browser:
|
||||
|
||||
- every heartbeat its display sent was refused with a 400, because the display and the server
|
||||
disagreed about `null`;
|
||||
- a display that failed one request at start-up never recovered without a reload;
|
||||
- its media cache leaked one blob per slide per refresh, until the browser refused the display's own
|
||||
requests.
|
||||
|
||||
Signage had unit tests, browser tests against a stubbed host, and no CI. Every test passed, because
|
||||
each half was tested against its own idea of the other, and nothing ever ran the module in the
|
||||
platform it ships into.
|
||||
|
||||
The platform's own deployment smoke (ADR 0044, ADR 0045) does run a module in a real browser, but
|
||||
only the repository's template, and only far enough to mount it. The instance repository runs the
|
||||
smoke against its real modules, but only at release time, after a module has already been tagged.
|
||||
Each module repository that wants more than that has to assemble the platform, build an image,
|
||||
start a deployment, and drive a browser itself. Signage is about to do exactly that by hand. The
|
||||
second module, Classroom, would copy it, and the copies would drift from the platform they test.
|
||||
|
||||
## Decision
|
||||
|
||||
### The platform publishes a versioned module harness
|
||||
|
||||
Each platform release publishes one OCI image, `ekklesia-module-harness:<platform version>`. It is
|
||||
built from the platform at that release tag by the platform's own release CI, and pushed to the
|
||||
organization's registry. A version is never overwritten. Modules reference it **by digest**.
|
||||
|
||||
The harness contains the platform source at that version: its `deploy/` directory, its Dockerfile,
|
||||
its lockfile, and its smoke scripts. It also contains the pinned Node.js and pnpm, the browser
|
||||
driver, and the pinned headless Chromium image reference. Given a module checkout, it:
|
||||
|
||||
1. **assembles** the module into the platform at `modules/<id>`, exactly as an instance does (ADR
|
||||
0044), and installs with `--frozen-lockfile`;
|
||||
2. runs the platform's own gates against that set: `pnpm boundaries`, `modules:validate`,
|
||||
`migrations:validate`, and the build;
|
||||
3. **builds** the deployment image from the assembled workspace with the platform's Dockerfile;
|
||||
4. starts the **reference deployment** in the split topology (ADR 0045), with a disposable
|
||||
PostgreSQL and object store, Caddy's internal CA, and freshly generated secrets;
|
||||
5. plans, then **explicitly applies**, migrations against that fresh database. This is the only
|
||||
database the harness ever migrates;
|
||||
6. creates the initial owner, grants the module's declared permissions to a test person, and
|
||||
**mounts every UI surface the manifest declares**, in headless Chromium. That covers:
|
||||
- the primary UI in the Dashboard;
|
||||
- the display, paired as a device with no human session (ADR 0036);
|
||||
- each browser contribution, whenever the module that hosts it is present (ADR 0041);
|
||||
7. runs a short **display soak**, and the **no-dead-ends** injections below;
|
||||
8. tears everything down: containers, networks, volumes, and the work directory.
|
||||
|
||||
It **fails** on any of the following:
|
||||
|
||||
- a `console.error` or uncaught exception on any page;
|
||||
- an unhandled promise rejection;
|
||||
- any 4xx or 5xx response that the harness did not itself expect. Expected ones are listed in the
|
||||
harness, for example a display's descriptor before it is paired;
|
||||
- a page that never mounts;
|
||||
- a failed or refused request (CORS, blocked, network error);
|
||||
- a soak leak.
|
||||
|
||||
Each failure line names the surface, the request path (never its query), and the step.
|
||||
|
||||
### The display soak and the no-dead-ends checks are generic
|
||||
|
||||
These checks come from what Signage got wrong, and they apply to any module with a display.
|
||||
|
||||
- **Soak**, two minutes by default. The display stays mounted with no remount and no offline
|
||||
indicator. Its realtime streams stay healthy. Live object URLs stay bounded. JavaScript heap
|
||||
growth, measured over DevTools, stays within a fixed ratio.
|
||||
- **No dead ends.** One at a time, the harness refuses the first request to each module route the
|
||||
display calls at start-up, and the first realtime connection. Within a bounded time and with no
|
||||
reload, the display must make that request again and succeed. A display has nobody to press
|
||||
Retry.
|
||||
|
||||
Module-specific behaviour, such as Signage's heartbeat being recorded, is not guessed by the
|
||||
harness. A module declares it (below).
|
||||
|
||||
### A module is tested against both ends of its compatibility range
|
||||
|
||||
A module's CI runs the harness twice: against the **lowest** and the **newest** published platform
|
||||
version within its manifest's `compatibility.platform` range. Testing every version in between adds
|
||||
cost and finds little. Testing only the newest misses the floor the manifest promises to work on.
|
||||
|
||||
The two versions, and their harness digests, are pinned in the module's repository. The harness
|
||||
refuses to run if:
|
||||
|
||||
- a pinned version lies outside the module's range;
|
||||
- the lowest pinned version is not the range's floor.
|
||||
|
||||
Moving to a newer platform is a deliberate, reviewed pin change, as an instance's is (ADR 0044). It
|
||||
is never a moving tag.
|
||||
|
||||
A frozen install of the module assembled into each of those two platforms needs a lockfile per
|
||||
pinned version. The module keeps them (`ci/lock/<platform version>.yaml`), and the harness provides
|
||||
the one command that regenerates them inside its own pinned Node.js. A stale lockfile fails the
|
||||
frozen install, which is the signal to regenerate it in a reviewed change.
|
||||
|
||||
### Modules declare extra smoke steps, beside their manifest rather than in it
|
||||
|
||||
A module may add steps of its own in `ekklesia.harness.json`, next to `ekklesia.module.json`. Steps
|
||||
are declarative first:
|
||||
|
||||
- open a surface;
|
||||
- wait for a test id;
|
||||
- call one of the module's routes as a named role, expecting a status;
|
||||
- soak for N minutes;
|
||||
- inject a failure into a named route;
|
||||
- check a value in the module's own table, through its own route.
|
||||
|
||||
For what a declaration cannot express, a step may name a driver script in the module's repository.
|
||||
The harness runs that script with the same DevTools client and the same failure rules.
|
||||
|
||||
**This is deliberately not a manifest field.** Manifest versions are strict, hard cutovers that every
|
||||
host validates (ADR 0033). A test step is not a runtime contract, and a host must never read one.
|
||||
Putting steps in the manifest would force a manifest version bump on every module for something
|
||||
that has no meaning outside CI.
|
||||
|
||||
### Later: the harness checks what a module actually touches against what it declares
|
||||
|
||||
In a later phase, the harness records what the module does while it runs:
|
||||
|
||||
- the tables and schemas it reads and writes;
|
||||
- the buckets it uses;
|
||||
- its outbound network connections, with the egress network blocked and every attempt logged.
|
||||
|
||||
It compares them with what the manifest declares (ADR 0016, ADR 0030). A module that touches what it
|
||||
did not declare fails. This is the module-trust check. The egress half needs a manifest declaration
|
||||
of outbound destinations that does not exist yet, and is a separate decision. This ADR fixes only
|
||||
the harness's shape, so that the recording phases can be added without changing how modules call
|
||||
it.
|
||||
|
||||
### The template's workflow stays short, and calls the harness by digest
|
||||
|
||||
`templates/module` ships a workflow that:
|
||||
|
||||
- checks out the module with `persist-credentials: false` and pinned actions;
|
||||
- runs the module's own unit tests, typecheck, and lint;
|
||||
- sweeps stale CI stacks from the runner, then runs the harness, once per pinned platform version,
|
||||
with one `docker run` of the harness image **by digest**, and finally, always, removes this job's
|
||||
own stacks. The module directory is mounted read-only, and the runner's Docker is used exactly
|
||||
as the platform's smoke uses it.
|
||||
|
||||
It references no secret, needs no registry credential (the harness image is public within the
|
||||
organization), and uses no `pull_request_target`. The trusted-code-only rule for self-hosted runners
|
||||
applies to module repositories exactly as it does to the platform (see `docs/development/security.md`).
|
||||
|
||||
**Why `docker run`, and not a reusable workflow or a composite action.** The server runs Gitea
|
||||
1.27.2. (This proposal first assumed 1.24.2; the reasons below hold on the actual version.)
|
||||
|
||||
- Its runner can call a reusable workflow from another repository by cloning that repository, and
|
||||
since 1.26 that includes private repositories (go-gitea/gitea#32562). That support is still thinly
|
||||
documented, and has known problems in practice.
|
||||
- A reusable workflow would make the harness's behaviour depend on how this server and runner
|
||||
version expand it. An image runs the same everywhere it runs.
|
||||
- Composite actions from another repository work through absolute `uses:` URLs. That would still
|
||||
wrap the same `docker run`, adding a second pinned reference and nothing else.
|
||||
|
||||
An image digest is the one reference that is equally immutable, equally reviewable, and works
|
||||
unchanged on any Gitea release and on GitHub Actions. A composite action can be added later as a
|
||||
convenience over the same image, without changing what a module pins.
|
||||
|
||||
### Nothing the harness starts outlives the job that started it
|
||||
|
||||
The harness starts Docker stacks on a shared runner, and a runner can kill a job outright: a
|
||||
cancelled or superseded job (`concurrency` cancels superseded runs on this server), or the runner's
|
||||
own time limit. A script's own cleanup trap does not run then. The platform's CI leaked a whole
|
||||
migration stack that way, and it ran until someone removed it by hand. So the harness follows the
|
||||
platform's rule (`scripts/ci-sweep.sh`, `docs/development/security.md`):
|
||||
|
||||
- everything it starts is named and labeled as a CI stack of its run, attempt, and job key;
|
||||
- it removes its own stacks in a trap;
|
||||
- the template workflow runs the harness's **stale sweep** before the harness starts, and its
|
||||
**job sweep** in a final `if: always()` step, so a killed run's stacks are removed by that run's
|
||||
last step, or failing that by the next job on the runner.
|
||||
|
||||
The sweep ships inside the harness image, so a module never copies it.
|
||||
|
||||
### Publishing follows the instance's release pattern
|
||||
|
||||
The harness image is pushed only by a `publish` job that runs on `v*` tags, after the platform's CI
|
||||
has built and tested that exact image (checked by image ID). That job:
|
||||
|
||||
- uses a write-only package token, the only secret in the platform's workflow;
|
||||
- refuses to overwrite an existing version;
|
||||
- prints the digest.
|
||||
|
||||
Pull requests and branch builds build the harness and run it against the template, but never push.
|
||||
The registry is reached exactly as the instance's publish job reaches it.
|
||||
|
||||
### Adoption
|
||||
|
||||
- **Signage** uses an interim CI job until the first harness is published. That job assembles
|
||||
Signage into a pinned platform commit and runs the platform's display-device smoke scenarios.
|
||||
Signage switches to the harness as soon as one is published.
|
||||
- **Classroom** follows.
|
||||
- **The template** carries the workflow from the first harness release on, so every new module has
|
||||
it from its first commit.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A module cannot reach a release without having run inside Ekklesia, in a real browser, at both
|
||||
ends of the platform range it claims.
|
||||
- The defects that reached the first production Signage deployment fail a module's CI instead:
|
||||
refused requests, dead ends, leaks, and console errors.
|
||||
- The platform owns one harness, versioned with itself. Modules do not copy deployment scripts that
|
||||
then drift.
|
||||
- Every module's CI needs Docker on its runner, and takes minutes rather than seconds. The harness
|
||||
runs as separate jobs from the unit tests, so a quick failure still reports quickly.
|
||||
- The platform's release process gains one published artifact, one credential, and one more failure
|
||||
mode at release time.
|
||||
- A module now carries a lockfile for each pinned platform version. The harness makes regenerating
|
||||
them one command, and a stale one fails visibly.
|
||||
|
||||
## Alternatives
|
||||
|
||||
- **Each module keeps its own copy of the smoke scripts.** Rejected. That is today's situation in
|
||||
embryo, and it drifts.
|
||||
- **A reusable workflow in the platform repository.** Rejected for now, for the reasons above: thin
|
||||
support in practice, and a harness whose behaviour depends on whichever runner and server version
|
||||
happens to expand it.
|
||||
- **Publish the harness as an npm package.** Rejected. The harness needs Docker, a browser, and the
|
||||
platform's deployment files anyway, so a package would ship a fraction of it and assume the rest.
|
||||
- **Test only the newest platform version.** Rejected. A module's range floor would go untested
|
||||
until an installation that has not upgraded meets it.
|
||||
- **Put smoke steps in the manifest.** Rejected. Manifest versions are hard cutovers with runtime
|
||||
meaning, and test steps have neither.
|
||||
- **Run the harness in the platform's CI against every known module.** Rejected. The platform would
|
||||
need every module's source and would fail on modules it does not own. The direction is module
|
||||
tests platform, not the reverse.
|
||||
|
||||
## Deferred
|
||||
|
||||
- The data-access and egress recording phases, and the manifest declaration egress needs.
|
||||
- A composite action over the harness image.
|
||||
- Running the harness against a module's declared dependencies at their own pinned versions, for
|
||||
contributions hosted by another module.
|
||||
- Visual regression of mounted surfaces.
|
||||
@@ -55,4 +55,3 @@ ADRs capture durable choices that affect more than one package or phase.
|
||||
- [0044: Ekklesia ships one self-hosted reference deployment, composed from pinned sources](0044-self-hosted-reference-deployment.md) — Accepted
|
||||
- [0045: The Dashboard and the API may live on separate origins](0045-split-dashboard-and-api-origins.md) — Accepted
|
||||
- [0046: Ekklesia's branding is never forced on an installation](0046-installation-owned-branding.md) — Proposed
|
||||
- [0047: Every module proves itself inside Ekklesia, with one harness the platform publishes](0047-module-ci-harness.md) — Proposed
|
||||
|
||||
@@ -693,10 +693,8 @@ public Dashboard login must be assessed with the actual edge/proxy controls befo
|
||||
|
||||
## Continuous integration boundary
|
||||
|
||||
CI runs on self-hosted Gitea Actions runners. The server reports **Gitea 1.27.2** (`GET
|
||||
/api/v1/version`, checked 2026-10-06; this document previously said 1.24.2, which was stale). The
|
||||
server's actual version, not the documentation of an earlier one, determines which parts of the
|
||||
workflow are real guarantees and which are not.
|
||||
CI runs on self-hosted Gitea Actions runners. The inventoried server is **Gitea 1.24.2**, and that
|
||||
version determines which parts of the workflow are real guarantees and which are not.
|
||||
|
||||
### The current operating rule: trusted code only
|
||||
|
||||
@@ -729,26 +727,24 @@ so a later step in the same job cannot read or reuse it from there.
|
||||
It does **not** remove the short-lived `GITEA_TOKEN` from the job environment, and it does **not**
|
||||
reduce that token's server-side authority. A step that wants the token can still read it.
|
||||
|
||||
### What this server honours, and what is not yet verified
|
||||
### What Gitea 1.24 ignores — so the branch cannot claim it
|
||||
|
||||
Each item separates what Gitea documents for 1.27
|
||||
(<https://docs.gitea.com/1.27/usage/actions/comparison>) from what has been observed on this server:
|
||||
Per the official comparison for this version
|
||||
(<https://docs.gitea.com/1.24/usage/actions/comparison>), Gitea 1.24's unsupported workflow syntax
|
||||
includes `permissions` and `jobs.<job_id>.permissions`, `concurrency`, and
|
||||
`jobs.<job_id>.timeout-minutes`. On this server those declarations are inert. Therefore:
|
||||
|
||||
- **`concurrency` is honoured.** Gitea supports it since 1.26 (go-gitea/gitea#32751), and on this
|
||||
server a newer push to a pull request cancels the run it supersedes. Observed: Ekklesia run 133 was
|
||||
cancelled when run 134 started for the same pull request, and run 135 when run 137 did. A
|
||||
superseded run's own cleanup is therefore never guaranteed to run, which is one reason for the
|
||||
stack sweep below.
|
||||
- **`permissions` is supported by 1.27, with Gitea's own scopes, but is not verified here.** The
|
||||
server-side token-permission settings are not visible through the API, and nothing has yet proven
|
||||
what the job token can and cannot do on this server. Until that proof exists (requirement 3
|
||||
below), this repository still does not claim the `GITEA_TOKEN` is read-only.
|
||||
- **`timeout-minutes` is no longer listed as unsupported for 1.27, but is not verified here.**
|
||||
Teardown does not rely on it: the stack sweep and a trusted reaper do.
|
||||
- **Still ignored:** `jobs.<job_id>.environment`, and any `runs-on` other than a single label.
|
||||
- **The workflow's `permissions: contents: read` does not restrict the job token.** This branch
|
||||
cannot claim the `GITEA_TOKEN` is read-only, because nothing in workflow YAML makes it so on
|
||||
1.24. Job-token permission controls, repository/owner defaults, and server-side
|
||||
maximum-permission clamping arrive in the 1.26 feature line
|
||||
(<https://docs.gitea.com/1.26/usage/actions/token-permissions>).
|
||||
- **`concurrency` and `timeout-minutes` are not bounded-execution guarantees here.** Cancellation,
|
||||
timeout, and teardown must be enforced by a trusted provisioner and reaper outside the workflow.
|
||||
|
||||
The declarations stay in the workflow either way: they state the intent, and are correct on GitHub
|
||||
Actions as well.
|
||||
The declarations are kept deliberately: they are already correct for a supported Gitea release and
|
||||
for GitHub Actions, and keeping them means the upgrade does not require re-deriving the intent. They
|
||||
are documented as ignored, not removed.
|
||||
|
||||
### Disposable stacks never outlive their job
|
||||
|
||||
@@ -797,17 +793,16 @@ they are not conditions on merging trusted, reviewed work.
|
||||
None of them can be satisfied by editing this repository; each requires access to the Gitea server
|
||||
and runner hosts, and each must be proven there.
|
||||
|
||||
1. ~~Upgrade Gitea to a reviewed supported release with job-token permission controls — at least
|
||||
the 1.26 feature line.~~ **Done:** the server runs 1.27.2. Items 2 and 3 now configure and prove
|
||||
what it makes possible.
|
||||
1. Upgrade Gitea to a reviewed supported release with job-token permission controls — at least the
|
||||
1.26 feature line. Select the actual release during infrastructure work, after backup and
|
||||
compatibility review; do not pin a version from this document.
|
||||
2. Configure repository and owner defaults, and the server-side maximum permission, so workflow
|
||||
code cannot request more than read-only repository access.
|
||||
3. Prove against the real server that the job's `GITEA_TOKEN` cannot push code, create or update
|
||||
releases, packages, issues, or pull requests, change Actions configuration, or reach another
|
||||
private repository.
|
||||
4. Enforce job timeout and teardown in the trusted provisioner/reaper. `concurrency` is honoured
|
||||
by the server; `timeout-minutes` is not yet verified, and the in-workflow stack sweep only
|
||||
complements a reaper.
|
||||
4. Enforce job timeout, cancellation, concurrency, and teardown in the trusted
|
||||
provisioner/reaper rather than relying on the ignored workflow keys above.
|
||||
5. Complete a disposable-guest runner isolation proof: one throwaway guest per job, torn down by a
|
||||
trusted reaper outside workflow YAML, holding no deployment credential, registration token, or
|
||||
reusable key.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ekklesia",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Open, modular church technology platform",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/contracts",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/design-tokens",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/module-sdk",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/module-ui-build",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"bin",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/module-ui-host",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/module-ui-sdk",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/typescript-config",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"files": [
|
||||
"base.json",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/ui",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
+3
-4
@@ -6,10 +6,9 @@
|
||||
# bash scripts/ci-sweep.sh --job this job's own stacks, at any age
|
||||
#
|
||||
# Every CI script already removes its own stack in an EXIT trap, but a trap never runs when the runner
|
||||
# kills the job container: a cancelled or superseded job (the server honours `concurrency`), or
|
||||
# the runner's own time limit. So each workflow job that starts stacks ends with `--job` in an
|
||||
# `if: always()` step, and begins with `--stale`, which catches whatever a killed job left before
|
||||
# it could clean up.
|
||||
# kills the job container: a cancelled job, or the runner's own time limit (Gitea 1.24 ignores
|
||||
# `timeout-minutes`). So each workflow job that starts stacks ends with `--job` in an `if: always()`
|
||||
# step, and begins with `--stale`, which catches whatever a killed job left before it could clean up.
|
||||
#
|
||||
# What a stack is, and how it is found, is defined in scripts/lib/ci-project.sh: a Compose project
|
||||
# named with the `ekklesia-ci-` prefix (Compose labels everything it creates with that name), plus
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ekklesia/shell",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"files": [
|
||||
|
||||
Reference in New Issue
Block a user