Move CI to Gitea Actions; this project is hosted on Gitea, not GitHub

Per explicit direction: this repository is not going on GitHub. Moved
.github/workflows/ci.yml to .gitea/workflows/ci.yml (Gitea Actions'
convention) and updated every doc that referenced the old path or assumed
GitHub-specific features:

- .gitea/workflows/ci.yml: added a header comment on the two things that
  are genuinely Gitea-specific and instance-dependent, not just a renamed
  file -- `runs-on: ubuntu-latest` must match a label the actual
  registered Gitea runner advertises (there is no GitHub-hosted-runner
  equivalent, this is self-hosted), and `actions/checkout@v4` resolves
  against whatever action source that runner is configured with.
- CONTRIBUTING.md: corrected a claim that no longer holds -- it previously
  said CI "runs on a normal, unrestricted GitHub Actions VM where TSan is
  expected to work"; since this is actually a self-hosted Gitea runner
  whose environment isn't controlled by this repo, that assumption isn't
  something this repo can vouch for, so the text now says so rather than
  carrying the old (GitHub-shaped) assumption forward silently.
- SECURITY.md: removed a claim this project can't back up (that "private
  security advisories" are available once hosted -- that's a GitHub
  feature this repo never had access to); reporting is by direct email to
  the maintainer only.
- README.md: fixed a real gap while in here -- the "Security" section
  never actually linked to SECURITY.md despite it existing since the
  previous commit.
- CLAUDE.md, CHANGELOG.md: updated path references; CLAUDE.md's
  self-evaluation section (a historical record of an audit finding) keeps
  the old .github path where it describes what was literally true at that
  time, with a note explaining the rename, rather than rewriting history.

Verified: clean make all + make test, all 6 binaries pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqJpkdJ6Njnt1pw3CbghzB
This commit is contained in:
2026-09-14 11:03:20 +00:00
co-authored by Claude Sonnet 5
parent a9143308f5
commit 229475247d
6 changed files with 46 additions and 20 deletions
@@ -1,3 +1,15 @@
# Gitea Actions workflow (not GitHub Actions — this project is hosted on
# Gitea, see CONTRIBUTING.md). Gitea Actions' YAML is GitHub-Actions-
# compatible, but two things here are Gitea-specific and need to match
# your actual instance, not just this repo:
# - `runs-on: ubuntu-latest` must match a label your registered Gitea
# runner (act_runner) actually advertises; there is no GitHub-hosted-
# runner equivalent, this is entirely self-hosted.
# - `uses: actions/checkout@v4` resolves against whatever action source
# your instance's runner is configured with (by default, Gitea Actions
# fetches actions from github.com unless DEFAULT_ACTIONS_URL is
# overridden); use `https://gitea.com/actions/checkout@v4` instead if
# your runner is configured to disallow GitHub as an action source.
name: CI
on:
+11 -4
View File
@@ -47,10 +47,17 @@ pushed to any remote — there is no public release yet, only a local one.
- **`backend_pack_new` was declared in `include/packfs.h` but never
implemented** — any program calling it failed at link time. Implemented
as a standalone, read-only `pack` `Backend`.
- `.github/workflows/ci.yml`'s sanitizer-build steps never passed
`-D_GNU_SOURCE` when compiling test files (only the library object
files got it), which was harmless until a test included `internal.h`
(needed for `pthread_rwlock_t`) and became a link failure.
- The CI config's (`.gitea/workflows/ci.yml`) sanitizer-build steps never
passed `-D_GNU_SOURCE` when compiling test files (only the library
object files got it), which was harmless until a test included
`internal.h` (needed for `pthread_rwlock_t`) and became a link failure.
### Changed
- CI moved from a GitHub-Actions-convention config (`.github/workflows/`)
to Gitea Actions (`.gitea/workflows/`) — this project is hosted on
Gitea, not GitHub, and was never actually hosted on GitHub; only the
scaffolding's starting convention changed. `SECURITY.md`, `CLAUDE.md`,
`CONTRIBUTING.md`, and `README.md` updated to match.
### Documented
- **`vfs.c`'s mount table (`MountSnapshot`) is O(n²) in mount count, the
+3 -3
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Repository status
This repository contains a working v0 implementation of the `concept.md` specification: `include/packfs.h` (public API), `src/*.c` (implementation), `tests/test_*.c` (test suite), and open-source project scaffolding (`README.md`, `LICENSE`, `CONTRIBUTING.md`, `CHANGELOG.md`, `SECURITY.md`, `packfs.pc.in`, `.github/workflows/ci.yml`), alongside the frozen `concept.md` and this file. Every file under `src/` and `include/` carries an `SPDX-License-Identifier: MIT` tag; `include/packfs.h`'s `PACKFS_VERSION_*` macros are the single source of truth for the project's version — `pfs_version()` (runtime) and `packfs.pc` (generated by `make install`) are both derived from them, never maintained separately.
This repository contains a working v0 implementation of the `concept.md` specification: `include/packfs.h` (public API), `src/*.c` (implementation), `tests/test_*.c` (test suite), and open-source project scaffolding (`README.md`, `LICENSE`, `CONTRIBUTING.md`, `CHANGELOG.md`, `SECURITY.md`, `packfs.pc.in`, `.gitea/workflows/ci.yml` — this project is hosted on Gitea, not GitHub), alongside the frozen `concept.md` and this file. Every file under `src/` and `include/` carries an `SPDX-License-Identifier: MIT` tag; `include/packfs.h`'s `PACKFS_VERSION_*` macros are the single source of truth for the project's version — `pfs_version()` (runtime) and `packfs.pc` (generated by `make install`) are both derived from them, never maintained separately.
## Build, test, and lint commands
@@ -17,7 +17,7 @@ make install PREFIX=/some/prefix
A single test: `make build/test_mem && ./build/test_mem` (substitute any `test_*` basename). There is no separate lint step — the build itself uses `-Wall -Wextra -Wpedantic`, and a warning introduced by a change is a build failure, not something to leave in place.
Sanitizer builds are not wired into `make test` (they need per-file compilation with sanitizer flags plus `-D_GNU_SOURCE -Iinclude -Isrc`); see `.github/workflows/ci.yml` for the exact invocation, which CI runs on every push. **Any change to `upper.c`, `overlay.c`, or `vfs.c` must be verified under `-fsanitize=address,undefined` and `-fsanitize=thread` before being considered done** — this is not a formality: exactly this process caught a real use-after-free in the snapshot-reclamation logic during initial development (see the `reclaim_gate` note below), which the plain build and even repeated plain test runs never surfaced.
Sanitizer builds are not wired into `make test` (they need per-file compilation with sanitizer flags plus `-D_GNU_SOURCE -Iinclude -Isrc`); see `.gitea/workflows/ci.yml` (Gitea Actions) for the exact invocation, which CI runs on every push. **Any change to `upper.c`, `overlay.c`, or `vfs.c` must be verified under `-fsanitize=address,undefined` and `-fsanitize=thread` before being considered done** — this is not a formality: exactly this process caught a real use-after-free in the snapshot-reclamation logic during initial development (see the `reclaim_gate` note below), which the plain build and even repeated plain test runs never surfaced.
**If your environment cannot run ThreadSanitizer at all, say so rather than skipping it silently.** TSan needs `personality(ADDR_NO_RANDOMIZE)` to disable ASLR for itself; some sandboxes block that syscall outright, in which case *every* TSan build fails identically (`FATAL: ThreadSanitizer: unexpected memory mapping`), including a trivial unrelated pthread program — that is the confirming test, not a PackFS-specific symptom. In that situation, ASan/UBSan are still run and still matter (they are what actually caught the `reclaim_gate` bug above), but they do not perform TSan's happens-before race analysis and are not a substitute for it; a change is TSan-verified only once it has passed on a machine or CI run that can actually execute it, not merely because ASan/UBSan passed.
@@ -97,7 +97,7 @@ These are hard requirements stated in the spec, not stylistic suggestions — an
## Self-evaluation
**Methodology.** This file was checked against the repository's actual state (`make test` passing, including two new tests, `test_pack_write_perf` and the dedup-verification block added to `test_pack_overlay`; `include/`, `src/`, `tests/`, `bench/` present and matching the description below; confirmed by directory listing and a live build), against `concept.md` as frozen (for factual consistency of the constraints and architecture summarized above), and against the documentation standard stated in this file. This revision followed a deliberate audit for other instances of the file index's O(n²) shape elsewhere in the codebase — prompted by a request to leave no caveats undocumented — which found two more real issues, not zero: `pack_write`'s compaction dedup (O(n²), plus a latent hash-collision correctness bug, both now fixed) and the mount table (O(n²), confirmed and deliberately left unfixed, with the reasoning recorded). The audit also found and fixed a CI gap the new tests exposed — `.github/workflows/ci.yml`'s sanitizer-build steps never passed `-D_GNU_SOURCE` when compiling test files, which was harmless while no test included `internal.h` and became a real link failure once two did (`internal.h` needs it for `pthread_rwlock_t`) — and documented a sandbox flake (ASan/UBSan's own `DEADLYSIGNAL` startup race, distinct from and in addition to the already-documented TSan limitation) observed directly during this audit's own sanitizer runs, in `CONTRIBUTING.md` and cross-referenced here. Every finding below was confirmed empirically (direct `pack_write` timing, `bench/bench.c`'s new mount-scaling category, repeated sanitizer runs with `timeout`), not asserted.
**Methodology.** This file was checked against the repository's actual state (`make test` passing, including two new tests, `test_pack_write_perf` and the dedup-verification block added to `test_pack_overlay`; `include/`, `src/`, `tests/`, `bench/` present and matching the description below; confirmed by directory listing and a live build), against `concept.md` as frozen (for factual consistency of the constraints and architecture summarized above), and against the documentation standard stated in this file. This revision followed a deliberate audit for other instances of the file index's O(n²) shape elsewhere in the codebase — prompted by a request to leave no caveats undocumented — which found two more real issues, not zero: `pack_write`'s compaction dedup (O(n²), plus a latent hash-collision correctness bug, both now fixed) and the mount table (O(n²), confirmed and deliberately left unfixed, with the reasoning recorded). The audit also found and fixed a CI gap the new tests exposed — the CI config's (now `.gitea/workflows/ci.yml`; at the time of this finding it lived at `.github/workflows/ci.yml`, before this project's scaffolding was set up for Gitea hosting instead of the GitHub-Actions convention it started from — this repository was never actually hosted on GitHub) sanitizer-build steps never passed `-D_GNU_SOURCE` when compiling test files, which was harmless while no test included `internal.h` and became a real link failure once two did (`internal.h` needs it for `pthread_rwlock_t`) — and documented a sandbox flake (ASan/UBSan's own `DEADLYSIGNAL` startup race, distinct from and in addition to the already-documented TSan limitation) observed directly during this audit's own sanitizer runs, in `CONTRIBUTING.md` and cross-referenced here. Every finding below was confirmed empirically (direct `pack_write` timing, `bench/bench.c`'s new mount-scaling category, repeated sanitizer runs with `timeout`), not asserted.
| Category | Grade | Notes |
|---|---|---|
+10 -5
View File
@@ -19,7 +19,7 @@ project is written in.
```sh
make test # must pass before any PR
cc ... -fsanitize=address,undefined # ASan/UBSan: see .github/workflows/ci.yml for exact flags
cc ... -fsanitize=address,undefined # ASan/UBSan: see .gitea/workflows/ci.yml for exact flags
cc ... -fsanitize=thread # TSan, for anything touching src/upper.c, src/overlay.c, or src/vfs.c
```
@@ -41,10 +41,15 @@ it or claiming verification that didn't happen — ASan/UBSan still catch
real bugs (they found and fixed a genuine heap-use-after-free during this
project's development, see the `reclaim_gate` note in `internal.h`) but do
not do TSan's happens-before race analysis, so they are not a substitute
for it. CI (`.github/workflows/ci.yml`) runs on a normal, unrestricted
GitHub Actions VM where TSan is expected to work; treat a change as
TSan-verified only once it has actually passed there or on a local machine
that can run it, not merely because ASan/UBSan passed.
for it. CI (`.gitea/workflows/ci.yml`, Gitea Actions — this project is
hosted on Gitea, not GitHub) runs on this project's own self-hosted
runner; whether that runner can execute TSan depends on that runner's own
environment, which is not controlled by this repository the way a
hosted-runner VM would be — do not assume CI's TSan step passing means
what it would on an unrestricted machine without having actually checked
what environment the registered runner provides. Treat a change as
TSan-verified only once it has actually passed on a machine confirmed able
to run it, not merely because a CI step reported success.
**A second, separate environment quirk, also observed directly rather than
assumed:** in the same kind of sandboxed environment, an ASan/UBSan-built
+4 -2
View File
@@ -260,7 +260,7 @@ never mutates a buffer address a reader might be reading (Section 5.7):
growth always allocates a new buffer and publishes it, never reallocates in
place. `tests/test_concurrency.c` exercises this under concurrent reader and
writer threads, and the suite is regularly run under ThreadSanitizer and
AddressSanitizer (see `.github/workflows/ci.yml`).
AddressSanitizer (see `.gitea/workflows/ci.yml`).
## Security
@@ -274,7 +274,9 @@ silently assumed equivalent. Pack files are treated as untrusted input unless
they came from this process's own compaction: every on-disk offset is
bounds-checked and the pack's checksum is verified before any of it is
trusted (Section 7). See `tests/test_dir.c` for a containment regression test
and `tests/test_pack_overlay.c` for a corrupted-pack rejection test.
and `tests/test_pack_overlay.c` for a corrupted-pack rejection test. See
[`SECURITY.md`](SECURITY.md) for exactly what is and isn't claimed as a
security boundary, and how to report a vulnerability.
## Contributing
+6 -6
View File
@@ -61,10 +61,10 @@ known limitations, not silent gaps:
## Reporting a vulnerability
Please do not open a public GitHub issue for a suspected security
vulnerability. Once this repository has a hosting remote and private
security advisories are available there, prefer that. Until then, or as an
alternative, report privately to the maintainer:
This project is hosted on Gitea, not GitHub (see `CONTRIBUTING.md`).
Please do not open a public issue on the issue tracker for a suspected
security vulnerability — report it privately, by email, to the
maintainer:
**retoor** — `retoor@molodetz.nl`
@@ -82,5 +82,5 @@ to pass under `-fsanitize=address,undefined` and `-fsanitize=thread`
before being considered done (`CLAUDE.md`) — this process has already
caught and fixed a real heap-use-after-free in the snapshot-reclamation
logic during development (see the `reclaim_gate` note in `src/internal.h`).
`.github/workflows/ci.yml` runs the full test suite plus both sanitizer
passes on every push.
`.gitea/workflows/ci.yml` (Gitea Actions) runs the full test suite plus
both sanitizer passes on every push.