Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24a75ee8d5 | |||
| e534dfcff6 | |||
| f77f3b7f11 | |||
| cdbeebf5e7 | |||
| e311bb1066 | |||
| 2aadba34e2 | |||
| 7f2979d7ff | |||
| f8053537a9 | |||
| 550c1d9f44 | |||
| 55594fd632 | |||
| b797aefb28 | |||
| e8050f9dfd | |||
| 0fd4cfe83d | |||
| 9d80f47625 | |||
| ef13a18b4c | |||
| 26bdad6d1b | |||
| b9d13a4814 | |||
| ac58ade9da | |||
| cb369d3be3 | |||
| b38558cdcf | |||
| ca88d13535 | |||
| 577274b382 | |||
| 25bf426ae5 | |||
| 55d9a62c33 | |||
| bf0095dcd2 | |||
| c4aa88d677 | |||
| 16b6f1ed21 | |||
| a9d70c58b1 | |||
| 0e20e87361 | |||
| 2af7732077 | |||
| 7d58ed23a4 | |||
| cf54f1c94f | |||
| ef014a9116 | |||
| bbf81d6ac5 | |||
| d5101ea3fe | |||
| 3dda8347e4 | |||
| 949f0d2153 | |||
| 45c8bc03e8 | |||
| c462cee781 | |||
| 80c05b582e | |||
| d95501ed01 | |||
| 65101ed034 | |||
| d971b72ba4 | |||
| 44972326e9 | |||
| df96ba5c89 | |||
| e3e8c6f5e3 | |||
| 4e2bb71ed5 | |||
| 7599dbd981 | |||
| 21684819ec | |||
| 3c4fcfc3ed | |||
| 0af380cd9a | |||
| 9a4b7eaa11 | |||
| 0d46839c04 | |||
| 4b5e5eec2d | |||
| cc57704fe0 | |||
| cfa685cf91 | |||
| 8e628dad9c | |||
| ce394ed7b8 | |||
| cb45365285 | |||
| 7da15f25ff | |||
| 80fb557b73 | |||
| bf0191b68e | |||
| b319fe443b | |||
| 547d804a81 | |||
| f81e3807a3 | |||
| 5fe57dc74c | |||
| 4a50fe3f31 | |||
| 2828abdfd4 | |||
| 1bc3afe912 | |||
| 486dee4b30 | |||
| 67098eb3b7 | |||
| 42380de028 | |||
| 20740d8735 | |||
| 49fd58b363 |
@@ -0,0 +1,2 @@
|
|||||||
|
result
|
||||||
|
result-*
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
when:
|
|
||||||
- event: tag
|
|
||||||
ref: "refs/tags/v*"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
image: git.oleks.space/oleks/nix-ci:latest
|
|
||||||
environment:
|
|
||||||
GITEA_CLONE_TOKEN:
|
|
||||||
from_secret: gitea_clone_token
|
|
||||||
commands:
|
|
||||||
- nixos-ci-entrypoint true
|
|
||||||
- nix build .#xonsh --no-link --print-out-paths
|
|
||||||
- nix build .#hello-world --no-link --print-out-paths
|
|
||||||
backend_options:
|
|
||||||
kubernetes:
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/hostname: howard2404
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# Build flake-hub packages for all architectures and push to attic
|
|
||||||
|
|
||||||
clone:
|
|
||||||
- name: clone
|
|
||||||
image: woodpeckerci/plugin-git
|
|
||||||
environment:
|
|
||||||
CI_NETRC_MACHINE: git.oleks.space
|
|
||||||
CI_NETRC_USERNAME: oleks
|
|
||||||
CI_NETRC_PASSWORD:
|
|
||||||
from_secret: gitea_clone_token
|
|
||||||
|
|
||||||
when:
|
|
||||||
- event: push
|
|
||||||
branch: main
|
|
||||||
|
|
||||||
# Build packages for each architecture in parallel
|
|
||||||
steps:
|
|
||||||
- name: build-x86_64-linux
|
|
||||||
image: git.oleks.space/oleks/nix-ci:latest
|
|
||||||
depends_on: []
|
|
||||||
environment:
|
|
||||||
ATTIC_TOKEN:
|
|
||||||
from_secret: attic_token
|
|
||||||
GITEA_CLONE_TOKEN:
|
|
||||||
from_secret: gitea_clone_token
|
|
||||||
backend_options:
|
|
||||||
kubernetes:
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
commands:
|
|
||||||
- sh ci/setup.sh
|
|
||||||
- sh ci/build.sh x86_64-linux
|
|
||||||
|
|
||||||
- name: build-aarch64-linux
|
|
||||||
image: git.oleks.space/oleks/nix-ci:latest
|
|
||||||
depends_on: []
|
|
||||||
environment:
|
|
||||||
ATTIC_TOKEN:
|
|
||||||
from_secret: attic_token
|
|
||||||
GITEA_CLONE_TOKEN:
|
|
||||||
from_secret: gitea_clone_token
|
|
||||||
backend_options:
|
|
||||||
kubernetes:
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/arch: arm64
|
|
||||||
commands:
|
|
||||||
- sh ci/setup.sh
|
|
||||||
- sh ci/build.sh aarch64-linux
|
|
||||||
|
|
||||||
- name: build-s390x-linux
|
|
||||||
image: git.oleks.space/oleks/nix-ci:latest
|
|
||||||
depends_on: []
|
|
||||||
environment:
|
|
||||||
ATTIC_TOKEN:
|
|
||||||
from_secret: attic_token
|
|
||||||
GITEA_CLONE_TOKEN:
|
|
||||||
from_secret: gitea_clone_token
|
|
||||||
backend_options:
|
|
||||||
kubernetes:
|
|
||||||
nodeSelector:
|
|
||||||
kubernetes.io/arch: amd64
|
|
||||||
commands:
|
|
||||||
- sh ci/setup.sh
|
|
||||||
- sh ci/build.sh s390x-linux
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Build flake-hub packages for x86_64-linux and push to attic.
|
||||||
|
# Separate workflow per arch — sharing one PVC across arches makes
|
||||||
|
# the second pod permanently Unschedulable (PV node affinity binds
|
||||||
|
# to the first arch's node).
|
||||||
|
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: main
|
||||||
|
|
||||||
|
clone:
|
||||||
|
- name: clone
|
||||||
|
image: woodpeckerci/plugin-git
|
||||||
|
environment:
|
||||||
|
CI_NETRC_MACHINE: git.oleks.space
|
||||||
|
CI_NETRC_USERNAME: oleks
|
||||||
|
CI_NETRC_PASSWORD:
|
||||||
|
from_secret: gitea_clone_token
|
||||||
|
PLUGIN_TAGS: "false"
|
||||||
|
PLUGIN_DEPTH: "1"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build-amd64
|
||||||
|
image: git.oleks.space/oleks/nix-ci:latest
|
||||||
|
environment:
|
||||||
|
ATTIC_TOKEN:
|
||||||
|
from_secret: attic_token
|
||||||
|
GITEA_CLONE_TOKEN:
|
||||||
|
from_secret: gitea_clone_token
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/arch: amd64
|
||||||
|
labels:
|
||||||
|
commit-tag: "${CI_COMMIT_TAG}"
|
||||||
|
commit-branch: "${CI_COMMIT_BRANCH}"
|
||||||
|
pipeline-number: "${CI_PIPELINE_NUMBER}"
|
||||||
|
commands:
|
||||||
|
- echo "▸ arch=$(uname -m)"
|
||||||
|
- sh ci/setup.sh
|
||||||
|
# Same front door as a local `nix run .#publish -- --publish`. The app
|
||||||
|
# is parity-lib's mkAtticClosurePublish (attic-closure archetype); CI and
|
||||||
|
# local share one audited impl. PUBLISH=1 makes it actually push (local
|
||||||
|
# runs dry-run).
|
||||||
|
- PUBLISH=1 nix run .#publish
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Build flake-hub packages for aarch64-linux and push to attic.
|
||||||
|
# Separate workflow per arch — see amd64.yaml for rationale.
|
||||||
|
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: main
|
||||||
|
|
||||||
|
clone:
|
||||||
|
- name: clone
|
||||||
|
image: woodpeckerci/plugin-git
|
||||||
|
environment:
|
||||||
|
CI_NETRC_MACHINE: git.oleks.space
|
||||||
|
CI_NETRC_USERNAME: oleks
|
||||||
|
CI_NETRC_PASSWORD:
|
||||||
|
from_secret: gitea_clone_token
|
||||||
|
PLUGIN_TAGS: "false"
|
||||||
|
PLUGIN_DEPTH: "1"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build-arm64
|
||||||
|
image: git.oleks.space/oleks/nix-ci:latest
|
||||||
|
environment:
|
||||||
|
ATTIC_TOKEN:
|
||||||
|
from_secret: attic_token
|
||||||
|
GITEA_CLONE_TOKEN:
|
||||||
|
from_secret: gitea_clone_token
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/arch: arm64
|
||||||
|
labels:
|
||||||
|
commit-tag: "${CI_COMMIT_TAG}"
|
||||||
|
commit-branch: "${CI_COMMIT_BRANCH}"
|
||||||
|
pipeline-number: "${CI_PIPELINE_NUMBER}"
|
||||||
|
commands:
|
||||||
|
- echo "▸ arch=$(uname -m)"
|
||||||
|
- sh ci/setup.sh
|
||||||
|
# NODE-BOUND LEG (emmett#44, cluster#192): aarch64-linux can't be built on
|
||||||
|
# emmett (linux/amd64) and these native packages have no cross path, so this
|
||||||
|
# leg must run on an aarch64 node (the arch nodeSelector above). Same
|
||||||
|
# parity-lib attic-closure front door as amd64, only the arch app differs.
|
||||||
|
- PUBLISH=1 nix run .#publish-aarch64-linux
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# flake-hub
|
||||||
|
|
||||||
|
Personal Nix flake hub — custom packages and overlays
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Personal Nix flake hub for publishing custom packages and overlays.
|
||||||
|
Provides packages for x86_64-linux and aarch64-linux.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#<package-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Antigravity
|
||||||
|
|
||||||
|
`google-antigravity` (FHS-wrapped) and `google-antigravity-no-fhs` are
|
||||||
|
re-exposed from
|
||||||
|
[jacopone/antigravity-nix](https://github.com/jacopone/antigravity-nix)
|
||||||
|
(MIT). Upstream auto-updates daily via its own GitHub Actions; we just bump
|
||||||
|
the flake input and CI caches the build to attic.
|
||||||
|
|
||||||
|
When the in-app "new version available" banner appears (or you just want to
|
||||||
|
refresh the pin):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just antigravity-update # nix flake update antigravity-nix
|
||||||
|
git commit -am "antigravity: bump"
|
||||||
|
git push # CI rebuilds and pushes to attic
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, on the consumer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/servers/emmett
|
||||||
|
nix flake update oleks-nixpkgs
|
||||||
|
nix run .#deploy
|
||||||
|
```
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Build all flake-hub packages and push to attic
|
|
||||||
set -e
|
|
||||||
|
|
||||||
ARCH="$1"
|
|
||||||
ATTIC_CACHE="attic-infra-cache-k3s-1"
|
|
||||||
ATTIC_SERVER="https://nix-cache-upload.oleks.space"
|
|
||||||
|
|
||||||
echo "=== Building flake-hub packages for ${ARCH} ==="
|
|
||||||
|
|
||||||
# Setup attic
|
|
||||||
attic=$(nix build --inputs-from . nixpkgs#attic-client --print-out-paths --no-link)/bin/attic
|
|
||||||
"${attic}" login ci "${ATTIC_SERVER}" "${ATTIC_TOKEN}"
|
|
||||||
|
|
||||||
echo "Building packages..."
|
|
||||||
out=$(nix build ".#packages.${ARCH}.hello-world" --print-build-logs --print-out-paths --no-link)
|
|
||||||
"${attic}" push "${ATTIC_CACHE}" "${out}"
|
|
||||||
|
|
||||||
out=$(nix build ".#packages.${ARCH}.xonsh" --print-build-logs --print-out-paths --no-link)
|
|
||||||
"${attic}" push "${ATTIC_CACHE}" "${out}"
|
|
||||||
|
|
||||||
echo "✅ Build completed for ${ARCH}"
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Bootstrap nix environment for CI (runs inside nixos/nix:latest)
|
# Bootstrap nix environment for CI (runs inside nixos/nix:latest)
|
||||||
set -e
|
set -ex
|
||||||
|
|
||||||
# Direct to armer public IP — bypass Cloudflare upload size limits
|
# Direct to armer public IP — bypass Cloudflare upload size limits
|
||||||
# Hairpin NAT on armer handles redirect for pods running on armer itself
|
# Hairpin NAT on armer handles redirect for pods running on armer itself
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
# Publishing `gitea-local-fork`
|
||||||
|
|
||||||
|
End-to-end flow for taking a change in Oleks's local gitea fork
|
||||||
|
(`/home/oleks/projects/gitea`, branch `oleks/main`) all the way to a
|
||||||
|
binary in the `attic-infra-cache-k3s-1` cache, ready for any consumer
|
||||||
|
to fetch instead of recompiling Go 1.26.3 locally.
|
||||||
|
|
||||||
|
## Step-by-step
|
||||||
|
|
||||||
|
### 1. Develop in the gitea fork
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/gitea
|
||||||
|
# work on oleks/main (the fork integration tip)
|
||||||
|
git commit -m "..."
|
||||||
|
git push oleks oleks/main:main
|
||||||
|
```
|
||||||
|
|
||||||
|
Lands on `git.oleks.space/oleks/gitea` at `refs/heads/main`.
|
||||||
|
|
||||||
|
This step alone does **not** trigger any CI on flake-hub — the binding
|
||||||
|
contract is the flake-hub pin commit in step 2.
|
||||||
|
|
||||||
|
### 2. Bump the pin in flake-hub
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/nix-customs/flake-hub
|
||||||
|
just gitea-update
|
||||||
|
```
|
||||||
|
|
||||||
|
Under the hood, `scripts/update-gitea-local-fork.sh`:
|
||||||
|
|
||||||
|
1. **Wipes stale unstable tags** — `git push oleks :refs/tags/v*-unstable-*`
|
||||||
|
so `nix-update` sees only clean semver tags (`v1.26.0` etc.) when
|
||||||
|
constructing the new version.
|
||||||
|
2. **Bumps the pin** —
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix run nixpkgs#nix-update -- \
|
||||||
|
--flake gitea-local-fork \
|
||||||
|
--version=branch=main \
|
||||||
|
--build
|
||||||
|
```
|
||||||
|
|
||||||
|
Updates `rev`, `vendorHash`, `pnpmDeps` hash, and `version` (formatted
|
||||||
|
as `<latest-semver-tag>-unstable-<commit-date>`).
|
||||||
|
3. **Tags the gitea fork** at the new rev with `v<version>` and pushes
|
||||||
|
the tag. **This is the load-bearing step:** the tag makes the rev
|
||||||
|
reachable forever, surviving any future rebase / force-push of
|
||||||
|
`oleks/main`.
|
||||||
|
4. **Commits + pushes flake-hub** — the bumped `packages/gitea-local-fork.nix`
|
||||||
|
lands on `git.oleks.space/oleks/flake-hub` `main`.
|
||||||
|
|
||||||
|
### 3. Woodpecker picks up the flake-hub push
|
||||||
|
|
||||||
|
Pipeline auto-triggers from `.woodpecker/amd64.yaml` and `.woodpecker/arm64.yaml`
|
||||||
|
running in parallel. Each workflow:
|
||||||
|
|
||||||
|
1. Clones `oleks/flake-hub` using the `gitea_clone_token` secret.
|
||||||
|
2. Runs `ci/setup.sh` — configures `/etc/hosts` to pin `armer`
|
||||||
|
directly (bypassing Cloudflare upload-size limits via hairpin NAT)
|
||||||
|
and writes the trusted substituters / public keys to `/etc/nix/nix.conf`.
|
||||||
|
3. Runs `ci/build.py <arch>` — for each package (including
|
||||||
|
`gitea-local-fork` on `x86_64-linux` and `aarch64-linux`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#packages.<arch>.gitea-local-fork --print-build-logs ...
|
||||||
|
attic push attic-infra-cache-k3s-1 <closure>
|
||||||
|
```
|
||||||
|
|
||||||
|
Authenticates the push with `ATTIC_TOKEN` against
|
||||||
|
`https://nix-cache-upload.oleks.space`.
|
||||||
|
|
||||||
|
### 4. Closure lives in attic
|
||||||
|
|
||||||
|
After both arch workflows succeed, the closure is queryable from:
|
||||||
|
|
||||||
|
- **Cache URL:** `https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1`
|
||||||
|
- **Public key:** `attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=`
|
||||||
|
|
||||||
|
### 5. Consume from cache
|
||||||
|
|
||||||
|
Anyone (or any host) can pull the binary instead of building:
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD013 -->
|
||||||
|
```bash
|
||||||
|
just gitea-run
|
||||||
|
# or, equivalently:
|
||||||
|
nix run \
|
||||||
|
--extra-substituters "https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1" \
|
||||||
|
--extra-trusted-public-keys "attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=" \
|
||||||
|
git+https://git.oleks.space/oleks/flake-hub#gitea-local-fork
|
||||||
|
```
|
||||||
|
<!-- markdownlint-enable MD013 -->
|
||||||
|
|
||||||
|
## Diagram
|
||||||
|
|
||||||
|
```text
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 1. ~/projects/gitea (branch oleks/main) │
|
||||||
|
│ ─ git push oleks oleks/main:main ─► oleks/gitea refs/heads/main │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 2. ~/projects/nix-customs/flake-hub │
|
||||||
|
│ ─ just gitea-update │
|
||||||
|
│ a. wipe v*-unstable-* tags on fork │
|
||||||
|
│ b. nix-update → bump rev/hashes/version │
|
||||||
|
│ c. tag fork at new rev: v<version> ─► push │
|
||||||
|
│ d. commit + push flake-hub origin/main │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 3. ci.oleks.space pipeline auto-fires │
|
||||||
|
│ amd64 + arm64 (parallel): │
|
||||||
|
│ clone → ci/setup.sh → ci/build.py <arch> │
|
||||||
|
│ ─ nix build .#packages.<arch>.gitea-local-fork │
|
||||||
|
│ ─ attic push attic-infra-cache-k3s-1 <closure> │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 4. attic-infra-cache-k3s-1 │
|
||||||
|
│ URL: https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1 │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ 5. Consumer: just gitea-run / nix run …#gitea-local-fork │
|
||||||
|
│ ─ closure pulled from cache, no recompile │
|
||||||
|
└────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
Both are Woodpecker repo secrets on `oleks/flake-hub` (never in code):
|
||||||
|
|
||||||
|
- `gitea_clone_token` — clone flake-hub source; netrc for `fetchgit` of
|
||||||
|
the fork.
|
||||||
|
- `attic_token` — authenticate `attic push` against
|
||||||
|
`nix-cache-upload.oleks.space`.
|
||||||
|
|
||||||
|
## Why the dance with tags in step 2c
|
||||||
|
|
||||||
|
The fork's `oleks/main` branch is rebaseable. `fetchgit` in the
|
||||||
|
derivation resolves by `rev`, so the rev must remain reachable from
|
||||||
|
*some* ref on the remote. Tags are immutable. The
|
||||||
|
`v<version>` tag created at update time becomes the load-bearing
|
||||||
|
reference: even if `oleks/main` later gets force-pushed and the rev
|
||||||
|
falls off the branch, the tag keeps it alive (and Gitea won't GC it).
|
||||||
|
|
||||||
|
## Failure points to watch
|
||||||
|
|
||||||
|
- **Step 2c — tag push rejected.** Check `oleks` remote auth in your
|
||||||
|
shell; the pre-push hook also runs lint.
|
||||||
|
- **Step 3 build — first cold build ≈10–15 min/arch, later ≈1–2 min.**
|
||||||
|
Normal; Go 1.26.3 compiles from source.
|
||||||
|
- **Step 3 attic — push hangs or 413.** `ci/setup.sh` pins armer in
|
||||||
|
`/etc/hosts` to bypass the Cloudflare 100 MB upload cap; confirm the
|
||||||
|
pin survived.
|
||||||
|
- **Step 5 consume — cache miss, recompile locally.** Check the closure
|
||||||
|
landed (`attic info` or browse the cache URL) and that the public key
|
||||||
|
matches.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
- Derivation: `packages/gitea-local-fork.nix`
|
||||||
|
- Flake attr: `flake.nix` → `packages.<system>.gitea-local-fork`
|
||||||
|
- Update script: `scripts/update-gitea-local-fork.sh` *(untracked, local-only)*
|
||||||
|
- Pipelines: `.woodpecker/amd64.yaml`, `.woodpecker/arm64.yaml`
|
||||||
|
- Build driver: `ci/build.py`
|
||||||
|
- Justfile recipes: `just gitea-build`, `just gitea-update`, `just gitea-run`
|
||||||
Generated
+517
-9
@@ -1,5 +1,26 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"antigravity-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780561919,
|
||||||
|
"narHash": "sha256-joj1ZN1q3dBtaPuYXvvFuXJzKt1r8g5FwsO9wgIOhkI=",
|
||||||
|
"owner": "jacopone",
|
||||||
|
"repo": "antigravity-nix",
|
||||||
|
"rev": "dafe38f5bf01ff5e4e105b18d2be9e3d754b6345",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "jacopone",
|
||||||
|
"repo": "antigravity-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
@@ -18,6 +39,159 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_3": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_4": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_4"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_5": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_5"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_6": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_6"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fleet": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"nixpkgs-armer": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-bim": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-ci": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-emmett": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-howard": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-mermaid": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-mermaid-gpu": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-micron": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-projects": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780368078,
|
||||||
|
"narHash": "sha256-tLzA5XveUF4PfuKNz3KuhmVhuME3PX5zvtFa17hhQPU=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "1626405d46ff3595b91c9e2d3ed9399f67c18b83",
|
||||||
|
"revCount": 15,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fleet-pins": {
|
"fleet-pins": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
@@ -45,6 +219,10 @@
|
|||||||
"fleet-pins",
|
"fleet-pins",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
|
"nixpkgs-mermaid-gpu": [
|
||||||
|
"fleet-pins",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
"nixpkgs-micron": [
|
"nixpkgs-micron": [
|
||||||
"fleet-pins",
|
"fleet-pins",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
@@ -55,11 +233,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772635961,
|
"lastModified": 1780368078,
|
||||||
"narHash": "sha256-4uQsEqeDCbZp4g5Qk4lb7PDBYcFykHr57FynIc4m/a0=",
|
"narHash": "sha256-tLzA5XveUF4PfuKNz3KuhmVhuME3PX5zvtFa17hhQPU=",
|
||||||
"ref": "main",
|
"ref": "main",
|
||||||
"rev": "513059f6a1b4cea423e600a142a1e6fe8b8e3e4f",
|
"rev": "1626405d46ff3595b91c9e2d3ed9399f67c18b83",
|
||||||
"revCount": 7,
|
"revCount": 15,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.oleks.space/oleks/fleet-pins"
|
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||||
},
|
},
|
||||||
@@ -69,30 +247,262 @@
|
|||||||
"url": "https://git.oleks.space/oleks/fleet-pins"
|
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fleet_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_3",
|
||||||
|
"nixpkgs-armer": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-bim": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-ci": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-emmett": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-howard": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-mermaid": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-mermaid-gpu": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-micron": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-projects": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780368078,
|
||||||
|
"narHash": "sha256-tLzA5XveUF4PfuKNz3KuhmVhuME3PX5zvtFa17hhQPU=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "1626405d46ff3595b91c9e2d3ed9399f67c18b83",
|
||||||
|
"revCount": 15,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/fleet-pins"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hyprspace": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1764820995,
|
||||||
|
"narHash": "sha256-IMa4mvkF0w7OAy+yEzPFYs2an332K30lf5qfUOAS9Cw=",
|
||||||
|
"owner": "KZDKM",
|
||||||
|
"repo": "Hyprspace",
|
||||||
|
"rev": "0467be86b18cfc324fab04afbd40fe9ef80f7fa9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "KZDKM",
|
||||||
|
"repo": "Hyprspace",
|
||||||
|
"rev": "0467be86b18cfc324fab04afbd40fe9ef80f7fa9",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mcp-chrome": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_3",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1779817034,
|
||||||
|
"narHash": "sha256-uRiqIPmTGeTCeoQtCz+CuC2NXNEv8xoP696HgYrA8fU=",
|
||||||
|
"ref": "main",
|
||||||
|
"rev": "8b5ec6cedfed0d46eff8ee4f6bf3912fd33acc22",
|
||||||
|
"revCount": 210,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/mcp-chrome"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"ref": "main",
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/mcp-chrome"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nix-deps": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_4",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1778863463,
|
||||||
|
"narHash": "sha256-TynOVZBZHiBZAjZbW2sQfaVEh5UXi4UgZ3X0pcu4nYE=",
|
||||||
|
"owner": "manelinux",
|
||||||
|
"repo": "nix-deps",
|
||||||
|
"rev": "5fdd0979c6254969eeb34ff49da2fa73c02972f3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "manelinux",
|
||||||
|
"repo": "nix-deps",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771848320,
|
"lastModified": 1777268161,
|
||||||
"narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=",
|
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2fc6539b481e1d2569f25f8799236694180c0993",
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2fc6539b481e1d2569f25f8799236694180c0993",
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777268161,
|
||||||
|
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777268161,
|
||||||
|
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parity": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_5",
|
||||||
|
"fleet": "fleet",
|
||||||
|
"nixpkgs": [
|
||||||
|
"parity",
|
||||||
|
"fleet",
|
||||||
|
"nixpkgs-ci"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780602315,
|
||||||
|
"narHash": "sha256-msVp9pBMP+I/n0+GGADG1XQembM3F19fUIh79rxqkgs=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "089bd03264d848f8391d9d53d79da0fd97ed1857",
|
||||||
|
"revCount": 13,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/parity-lib"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/parity-lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parity_2": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_6",
|
||||||
|
"fleet": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"fleet"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"woodpecker-peek",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780472546,
|
||||||
|
"narHash": "sha256-cx+821qtyNZNe9t3ab8qImmeg/rxVzPpZIS45SflrI0=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "d265a79ddb84b297364e6cc3638c9f6b5dc583d7",
|
||||||
|
"revCount": 10,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/parity-lib"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/parity-lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"antigravity-nix": "antigravity-nix",
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
"fleet-pins": "fleet-pins",
|
"fleet-pins": "fleet-pins",
|
||||||
|
"hyprspace": "hyprspace",
|
||||||
|
"mcp-chrome": "mcp-chrome",
|
||||||
|
"nix-deps": "nix-deps",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"fleet-pins",
|
"fleet-pins",
|
||||||
"nixpkgs-projects"
|
"nixpkgs-projects"
|
||||||
|
],
|
||||||
|
"parity": "parity",
|
||||||
|
"stalewood": "stalewood",
|
||||||
|
"woodpecker-peek": "woodpecker-peek"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stalewood": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1779473107,
|
||||||
|
"narHash": "sha256-PjX2So2b4xPuXZCkgTb0eXnV9wIELQ7D0EPlDWD0NBw=",
|
||||||
|
"owner": "retif",
|
||||||
|
"repo": "stalewood",
|
||||||
|
"rev": "415f3de6c23f662dabceaf0cba540a289c609174",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "retif",
|
||||||
|
"repo": "stalewood",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
@@ -109,6 +519,104 @@
|
|||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_4": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_5": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_6": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"woodpecker-peek": {
|
||||||
|
"inputs": {
|
||||||
|
"fleet": "fleet_2",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"parity": "parity_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780743249,
|
||||||
|
"narHash": "sha256-qVrrS69mzq3Fo8+pFtFukDogAl+2qQl5ib/7DJi1oRw=",
|
||||||
|
"ref": "main",
|
||||||
|
"rev": "8db46bec6c1dfed1c411df6b139d60c87d739696",
|
||||||
|
"revCount": 31,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/woodpecker-peek"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"ref": "main",
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.oleks.space/oleks/woodpecker-peek"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|||||||
@@ -5,6 +5,57 @@
|
|||||||
fleet-pins.url = "git+https://git.oleks.space/oleks/fleet-pins?ref=main";
|
fleet-pins.url = "git+https://git.oleks.space/oleks/fleet-pins?ref=main";
|
||||||
nixpkgs.follows = "fleet-pins/nixpkgs-projects";
|
nixpkgs.follows = "fleet-pins/nixpkgs-projects";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
|
# Shared per-archetype parity publish-app builders (cluster#104, emmett#44).
|
||||||
|
# flake-hub is an ATTIC-CLOSURE repo: it builds package closures and warms
|
||||||
|
# the Attic cache with them — there is NO registry artifact.
|
||||||
|
# mkAtticClosurePublish makes that archetype explicit to pipeline-doctor.
|
||||||
|
parity.url = "git+https://git.oleks.space/oleks/parity-lib";
|
||||||
|
|
||||||
|
# Hyprspace source; no flake.nix in the repo so we consume it as raw src.
|
||||||
|
# Pin tracks the last v0.52-compatible commit of Hyprspace.
|
||||||
|
hyprspace = {
|
||||||
|
url = "github:KZDKM/Hyprspace/0467be86b18cfc324fab04afbd40fe9ef80f7fa9";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Google Antigravity packaging. Upstream auto-updates daily; we re-expose
|
||||||
|
# the overlay and build into our attic cache so emmett pulls from there.
|
||||||
|
antigravity-nix = {
|
||||||
|
url = "github:jacopone/antigravity-nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
# nix-deps: "see the real cost of installing packages on NixOS".
|
||||||
|
# Re-exposed through our overlay so CI builds it into attic.
|
||||||
|
nix-deps = {
|
||||||
|
url = "github:manelinux/nix-deps";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
# stalewood — find/reap merged git worktrees. Ships its own flake;
|
||||||
|
# re-expose its package (mirrors the nix-deps pattern).
|
||||||
|
stalewood = {
|
||||||
|
url = "github:retif/stalewood";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
# woodpecker-peek — tray app for Woodpecker CI (on git.oleks.space).
|
||||||
|
# Re-exposed so flake-hub CI warms attic and emmett pulls cached.
|
||||||
|
woodpecker-peek = {
|
||||||
|
url = "git+https://git.oleks.space/oleks/woodpecker-peek?ref=main";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
# mcp-chrome — Chrome MCP server + extension (English-localized fork of
|
||||||
|
# hangwin/mcp-chrome). Re-exposes the from-source wasm-simd worker (proven
|
||||||
|
# green, ~22 s) and the full chrome-mcp-extension build. The extension
|
||||||
|
# target is KNOWN-BROKEN under nix-daemon at this pin (see oleks/mcp-chrome
|
||||||
|
# issue #1 close comment); expect attic to miss it until that's resolved.
|
||||||
|
mcp-chrome = {
|
||||||
|
url = "git+https://git.oleks.space/oleks/mcp-chrome?ref=main";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@@ -13,6 +64,13 @@
|
|||||||
nixpkgs,
|
nixpkgs,
|
||||||
fleet-pins,
|
fleet-pins,
|
||||||
flake-utils,
|
flake-utils,
|
||||||
|
parity,
|
||||||
|
hyprspace,
|
||||||
|
antigravity-nix,
|
||||||
|
nix-deps,
|
||||||
|
stalewood,
|
||||||
|
woodpecker-peek,
|
||||||
|
mcp-chrome,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@@ -27,15 +85,171 @@
|
|||||||
"s390x-linux" = "s390x-linux";
|
"s390x-linux" = "s390x-linux";
|
||||||
};
|
};
|
||||||
|
|
||||||
mkPackages = pkgs: {
|
mkPackages =
|
||||||
hello-world = pkgs.callPackage ./packages/hello-world.nix { };
|
pkgs:
|
||||||
xonsh = pkgs.callPackage ./packages/xonsh.nix {
|
let
|
||||||
xonsh-unwrapped = import ./packages/xonsh-unwrapped.nix {
|
sys = pkgs.stdenv.hostPlatform.system;
|
||||||
inherit (pkgs) lib python3Packages fetchFromGitHub;
|
# Antigravity ships a Google-provided x86_64/aarch64 Linux binary.
|
||||||
|
# Skip it for cross targets (e.g. s390x) where it can't run anyway.
|
||||||
|
supportsAntigravity = sys == "x86_64-linux" || sys == "aarch64-linux";
|
||||||
|
xontribs = import ./packages/xontribs.nix {
|
||||||
|
inherit (pkgs) python3Packages fetchurl;
|
||||||
};
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
hello-world = pkgs.callPackage ./packages/hello-world.nix { };
|
||||||
|
geesefs = pkgs.callPackage ./packages/geesefs.nix { };
|
||||||
|
metamcp = pkgs.callPackage ./packages/metamcp.nix { };
|
||||||
|
xonsh = pkgs.callPackage ./packages/xonsh.nix {
|
||||||
|
xonsh-unwrapped = import ./packages/xonsh-unwrapped.nix {
|
||||||
|
inherit (pkgs) lib python3Packages fetchFromGitHub;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# Native-only packages — skip on s390x cross (gitea's pnpm step and
|
||||||
|
# cgo+sqlite link don't cross-compile cleanly).
|
||||||
|
// nixpkgs.lib.optionalAttrs (sys == "x86_64-linux" || sys == "aarch64-linux") {
|
||||||
|
gitea-local-fork =
|
||||||
|
let
|
||||||
|
# Our fork's go.mod requires Go 1.26.3; nixpkgs at this pin has
|
||||||
|
# only 1.26.0 (and unstable has 1.26.2). Bump the package's src.
|
||||||
|
go = pkgs.go_1_26.overrideAttrs (_: rec {
|
||||||
|
version = "1.26.3";
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
url = "https://go.dev/dl/go${version}.src.tar.gz";
|
||||||
|
hash = "sha256-HGRoddCqh5kTMYTtV895/yS97+jIggRwYCqdPW2Rkrg=";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
in
|
||||||
|
pkgs.callPackage ./packages/gitea-local-fork.nix {
|
||||||
|
buildGoModule = pkgs.buildGoModule.override { inherit go; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# Xontribs: pass into `programs.xonsh.extraPackages` or
|
||||||
|
# `pkgs.xonsh.override { extraPackages = ps: [...]; }`.
|
||||||
|
// xontribs
|
||||||
|
# Antigravity: re-expose jacopone/antigravity-nix's outputs so emmett
|
||||||
|
# consumes a single flake-hub input and our CI builds into attic.
|
||||||
|
// nixpkgs.lib.optionalAttrs supportsAntigravity {
|
||||||
|
inherit (antigravity-nix.packages.${sys})
|
||||||
|
google-antigravity
|
||||||
|
google-antigravity-no-fhs
|
||||||
|
google-antigravity-ide
|
||||||
|
google-antigravity-ide-no-fhs
|
||||||
|
google-antigravity-cli
|
||||||
|
;
|
||||||
|
# nix-deps' flake only outputs eachDefaultSystem (no s390x), so
|
||||||
|
# gate it on the same native x86_64/aarch64 condition.
|
||||||
|
nix-deps = nix-deps.packages.${sys}.default;
|
||||||
|
# stalewood — re-exposed from its own flake. No s390x output,
|
||||||
|
# so it rides the same native-only gate.
|
||||||
|
stalewood = stalewood.packages.${sys}.default;
|
||||||
|
# woodpecker-peek — same pattern; consumers (emmett) read the
|
||||||
|
# attic-cached binary via flake-hub's overlay, then set
|
||||||
|
# services.woodpecker-peek.package = pkgs.woodpecker-peek;.
|
||||||
|
woodpecker-peek = woodpecker-peek.packages.${sys}.default;
|
||||||
|
# mcp-chrome — Rust→wasm worker (proven green) plus the full
|
||||||
|
# chrome-mcp-extension build. The latter is KNOWN-BROKEN under
|
||||||
|
# nix-daemon at this pin; flake-hub CI will miss the cache on it
|
||||||
|
# until upstream resolves that. wasm-simd alone is what consumers
|
||||||
|
# actually pull cached today (commits 9534234, b276465 in
|
||||||
|
# oleks/mcp-chrome sync the built wasm back into the tree).
|
||||||
|
mcp-chrome-wasm-simd = mcp-chrome.packages.${sys}.wasm-simd;
|
||||||
|
mcp-chrome-extension = mcp-chrome.packages.${sys}.chrome-mcp-extension;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Overlay providing Hyprspace. Requires `pkgs.hyprland` to be present
|
||||||
|
# (consumer applies the Hyprland flake's overlay first). Kept out of
|
||||||
|
# `mkPackages` because standalone `nix build .#hyprspace` has no
|
||||||
|
# Hyprland in scope.
|
||||||
|
hyprspaceOverlay = final: _prev: {
|
||||||
|
hyprspace = final.callPackage ./packages/hyprspace.nix {
|
||||||
|
src = hyprspace;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Rustc bootstrap: symlink_file panics with "File exists" during
|
||||||
|
# s390x cross-compilation. Multiple call sites use t!(symlink_file(...)).
|
||||||
|
# Patch the symlink_file method body to remove an existing dest first.
|
||||||
|
# Exported as overlays.s390xRustcSymlink so nixos-ci consumes this single
|
||||||
|
# definition (and the patch travels with it) instead of duplicating it.
|
||||||
|
s390xRustcSymlinkOverlay =
|
||||||
|
final: prev:
|
||||||
|
let
|
||||||
|
patchedRustcUnwrapped = prev.rustc-unwrapped.overrideAttrs (old: {
|
||||||
|
patches = (old.patches or [ ]) ++ [
|
||||||
|
./patches/rustc-symlink-file-eexist.patch
|
||||||
|
];
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
rustc-unwrapped = patchedRustcUnwrapped;
|
||||||
|
rustc = prev.rustc.override {
|
||||||
|
rustc-unwrapped = patchedRustcUnwrapped;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Overlays needed for s390x cross-compilation of attic-client
|
||||||
|
s390xOverlays = [
|
||||||
|
# OpenSSL s390x assembly uses z10 instructions (cijne) that the
|
||||||
|
# nix-bootstrapped assembler doesn't recognize
|
||||||
|
(final: prev: {
|
||||||
|
openssl = prev.openssl.overrideAttrs (old: {
|
||||||
|
configureFlags = (old.configureFlags or [ ]) ++ [ "no-asm" ];
|
||||||
|
});
|
||||||
|
})
|
||||||
|
# musl doesn't support s390x long double (IBM double-double format:
|
||||||
|
# LDBL_MANT_DIG=106, sizeof=16). Make musl appear unavailable so
|
||||||
|
# busybox-sandbox-shell uses glibc static instead.
|
||||||
|
(final: prev: {
|
||||||
|
busybox-sandbox-shell = prev.busybox-sandbox-shell.override {
|
||||||
|
musl = prev.musl // {
|
||||||
|
meta = prev.musl.meta // {
|
||||||
|
platforms = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
# libarchive tests fail in k8s pod sandboxes
|
||||||
|
(final: prev: {
|
||||||
|
libarchive = prev.libarchive.overrideAttrs (_: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
# nix 2.28 is a direct dependency of attic-client — disable tests
|
||||||
|
# and fix missing RPATH for boost/zstd/libarchive
|
||||||
|
(final: prev: {
|
||||||
|
nixVersions = prev.nixVersions // {
|
||||||
|
nix_2_28 = prev.nixVersions.nix_2_28.overrideAttrs (old: {
|
||||||
|
doCheck = false;
|
||||||
|
doInstallCheck = false;
|
||||||
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ final.patchelf ];
|
||||||
|
postFixup = (old.postFixup or "") + ''
|
||||||
|
for rpath in ${final.boost}/lib ${final.zstd.out}/lib ${final.libarchive.out}/lib; do
|
||||||
|
patchelf --add-rpath "$rpath" $out/bin/nix
|
||||||
|
for lib in $out/lib/lib*.so; do
|
||||||
|
[ -f "$lib" ] && patchelf --add-rpath "$rpath" "$lib"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})
|
||||||
|
# LLVM test failures in CI pod environment — override libllvm
|
||||||
|
# (not llvm) so it propagates through rustc bootstrap chain
|
||||||
|
(final: prev: {
|
||||||
|
llvmPackages = prev.llvmPackages // {
|
||||||
|
libllvm = prev.llvmPackages.libllvm.overrideAttrs (old: {
|
||||||
|
doCheck = false;
|
||||||
|
doInstallCheck = false;
|
||||||
|
nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ prev.gitMinimal ];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})
|
||||||
|
# Rustc symlink_file "File exists" fix (defined + exported above).
|
||||||
|
s390xRustcSymlinkOverlay
|
||||||
|
];
|
||||||
|
|
||||||
# Native builds
|
# Native builds
|
||||||
native = flake-utils.lib.eachSystem buildSystems (
|
native = flake-utils.lib.eachSystem buildSystems (
|
||||||
system:
|
system:
|
||||||
@@ -48,7 +262,7 @@
|
|||||||
default = packages.hello-world;
|
default = packages.hello-world;
|
||||||
};
|
};
|
||||||
|
|
||||||
overlays.default = final: prev: mkPackages final;
|
formatter = pkgs.nixfmt-rfc-style;
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
name = "oleks-hub-shell";
|
name = "oleks-hub-shell";
|
||||||
@@ -69,6 +283,11 @@
|
|||||||
builtins.map (
|
builtins.map (
|
||||||
target:
|
target:
|
||||||
let
|
let
|
||||||
|
targetOverlays =
|
||||||
|
{
|
||||||
|
"s390x-linux" = s390xOverlays;
|
||||||
|
}
|
||||||
|
.${target} or [ ];
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
crossSystem.config =
|
crossSystem.config =
|
||||||
@@ -78,14 +297,33 @@
|
|||||||
}
|
}
|
||||||
.${target}
|
.${target}
|
||||||
}.config;
|
}.config;
|
||||||
|
overlays = targetOverlays;
|
||||||
};
|
};
|
||||||
packages = mkPackages pkgs;
|
packages = mkPackages pkgs;
|
||||||
|
crossOnlyPackages =
|
||||||
|
{
|
||||||
|
"s390x-linux" = {
|
||||||
|
inherit (pkgs)
|
||||||
|
attic-client
|
||||||
|
rustc
|
||||||
|
cargo
|
||||||
|
rustfmt
|
||||||
|
sccache
|
||||||
|
mold
|
||||||
|
;
|
||||||
|
nix = pkgs.nixVersions.nix_2_28;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
.${target} or { };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = target;
|
name = target;
|
||||||
value = packages // {
|
value =
|
||||||
default = packages.hello-world;
|
packages
|
||||||
};
|
// crossOnlyPackages
|
||||||
|
// {
|
||||||
|
default = packages.hello-world;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
) (builtins.attrNames crossTargets)
|
) (builtins.attrNames crossTargets)
|
||||||
);
|
);
|
||||||
@@ -93,5 +331,99 @@
|
|||||||
native
|
native
|
||||||
// {
|
// {
|
||||||
packages = (native.packages or { }) // cross;
|
packages = (native.packages or { }) // cross;
|
||||||
|
overlays = {
|
||||||
|
default = final: _prev: mkPackages final;
|
||||||
|
gcc15-fixes = import ./overlays/gcc15-fixes.nix;
|
||||||
|
hyprspace = hyprspaceOverlay;
|
||||||
|
# Single home for the s390x rustc symlink_file patch overlay so
|
||||||
|
# consumers (nixos-ci) don't re-declare it + the patch file.
|
||||||
|
s390xRustcSymlink = s390xRustcSymlinkOverlay;
|
||||||
|
};
|
||||||
|
|
||||||
|
# `nix run .#<app>` — local-parity entrypoints (emmett#44, cluster#192,
|
||||||
|
# ATTIC-CLOSURE archetype, cluster#104). The stage/publish/push-staged
|
||||||
|
# apps come straight from parity-lib's mkAtticClosurePublish, so CI and a
|
||||||
|
# local run share one audited implementation and cannot drift. There is NO
|
||||||
|
# registry artifact — these apps build the package closures and warm the
|
||||||
|
# Attic cache with them.
|
||||||
|
#
|
||||||
|
# TWO HALVES (emmett#44): STAGE `nix build`s every package in the arch's
|
||||||
|
# list into the local /nix store (cluster-independent, runs on emmett);
|
||||||
|
# PUBLISH additionally `attic push`es each closure to the cache that lives
|
||||||
|
# next to the cluster. Local runs DRY-RUN (stage + show the pushes) unless
|
||||||
|
# `--publish`/PUBLISH=1.
|
||||||
|
#
|
||||||
|
# nix run .#stage-x86_64-linux stage amd64 closures, no publish
|
||||||
|
# nix run .#publish stage amd64 then push if PUBLISH=1
|
||||||
|
# nix run .#publish -- --publish actually push to attic
|
||||||
|
# nix run .#push-staged replay already-staged amd64 paths
|
||||||
|
#
|
||||||
|
# arm64 leg: aarch64-linux cannot be built on emmett (linux/amd64) and the
|
||||||
|
# native packages have no cross path, so it MUST run on an aarch64 node
|
||||||
|
# (.woodpecker/arm64.yaml runs `PUBLISH=1 nix run .#publish-aarch64-linux`).
|
||||||
|
#
|
||||||
|
# The package set per arch mirrors flake-hub's CI warm list (was
|
||||||
|
# ci/publish.py packages_for): the always-on core plus arch-conditional
|
||||||
|
# extras. Cross-only/known-broken targets stay out (see comments).
|
||||||
|
apps =
|
||||||
|
let
|
||||||
|
# The Attic cache token lives at `pass infra/attic/ci_token` and the
|
||||||
|
# cache is reached via the armer hairpin endpoint — preserve both so
|
||||||
|
# the push is byte-for-byte the pre-parity behaviour.
|
||||||
|
atticEndpoint = "https://nix-cache-upload.oleks.space";
|
||||||
|
atticPass = "infra/attic/ci_token";
|
||||||
|
|
||||||
|
# Package names to warm into Attic for a given native arch.
|
||||||
|
# Native arches only (amd64/arm64); mcp-chrome-extension stays OUT
|
||||||
|
# (known-broken under nix-daemon at this pin, see oleks/mcp-chrome #1).
|
||||||
|
packageNamesFor =
|
||||||
|
arch:
|
||||||
|
[
|
||||||
|
"hello-world"
|
||||||
|
"geesefs"
|
||||||
|
"xonsh"
|
||||||
|
]
|
||||||
|
++ nixpkgs.lib.optionals (arch == "x86_64-linux" || arch == "aarch64-linux") [
|
||||||
|
"woodpecker-peek"
|
||||||
|
"mcp-chrome-wasm-simd"
|
||||||
|
"gitea-local-fork"
|
||||||
|
"google-antigravity"
|
||||||
|
"google-antigravity-no-fhs"
|
||||||
|
"google-antigravity-ide"
|
||||||
|
"google-antigravity-ide-no-fhs"
|
||||||
|
"google-antigravity-cli"
|
||||||
|
];
|
||||||
|
|
||||||
|
drvsFor = arch: map (n: self.packages.${arch}.${n}) (packageNamesFor arch);
|
||||||
|
|
||||||
|
buildersFor = arch: parity.lib.mkParityBuilders (import nixpkgs { system = arch; });
|
||||||
|
|
||||||
|
atticAppsFor =
|
||||||
|
arch:
|
||||||
|
(buildersFor arch).mkAtticClosurePublish {
|
||||||
|
drvs = drvsFor arch;
|
||||||
|
inherit arch;
|
||||||
|
endpoint = atticEndpoint;
|
||||||
|
passEntry = atticPass;
|
||||||
|
};
|
||||||
|
|
||||||
|
amd64Apps = atticAppsFor "x86_64-linux";
|
||||||
|
arm64Apps = atticAppsFor "aarch64-linux";
|
||||||
|
in
|
||||||
|
nixpkgs.lib.genAttrs buildSystems (system:
|
||||||
|
# amd64 is the emmett-buildable arch: expose its stage/publish/
|
||||||
|
# push-staged plus the top-level `publish`. Also surface the arm64
|
||||||
|
# stage/publish under their arch-suffixed names for the node-bound
|
||||||
|
# CI leg (.woodpecker/arm64.yaml).
|
||||||
|
{
|
||||||
|
inherit (amd64Apps)
|
||||||
|
"stage-x86_64-linux"
|
||||||
|
"publish-x86_64-linux"
|
||||||
|
"publish"
|
||||||
|
"push-staged"
|
||||||
|
;
|
||||||
|
"stage-aarch64-linux" = arm64Apps."stage-aarch64-linux";
|
||||||
|
"publish-aarch64-linux" = arm64Apps."publish-aarch64-linux";
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
default:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
# ── Antigravity ────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Run antigravity (FHS-wrapped) from the attic cache
|
||||||
|
antigravity-run:
|
||||||
|
nix run \
|
||||||
|
--extra-substituters "https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1" \
|
||||||
|
--extra-trusted-public-keys "attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=" \
|
||||||
|
git+https://git.oleks.space/oleks/flake-hub#google-antigravity
|
||||||
|
|
||||||
|
# Run antigravity (non-FHS variant) from the attic cache
|
||||||
|
antigravity-run-no-fhs:
|
||||||
|
nix run \
|
||||||
|
--extra-substituters "https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1" \
|
||||||
|
--extra-trusted-public-keys "attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=" \
|
||||||
|
git+https://git.oleks.space/oleks/flake-hub#google-antigravity-no-fhs
|
||||||
|
|
||||||
|
# Build antigravity (FHS-wrapped)
|
||||||
|
antigravity-build:
|
||||||
|
nix build .#google-antigravity --print-build-logs
|
||||||
|
|
||||||
|
# Build antigravity (non-FHS variant)
|
||||||
|
antigravity-build-no-fhs:
|
||||||
|
nix build .#google-antigravity-no-fhs --print-build-logs
|
||||||
|
|
||||||
|
# Pull the latest jacopone/antigravity-nix pin
|
||||||
|
antigravity-update:
|
||||||
|
nix flake update antigravity-nix
|
||||||
|
|
||||||
|
# ── gitea-local-fork ───────────────────────────────────────
|
||||||
|
# Builds Oleks's local gitea fork (/home/oleks/projects/gitea, branch oleks/main).
|
||||||
|
|
||||||
|
# Build the gitea fork (compiles Go 1.26.3 first time; ~5-8 min cold)
|
||||||
|
gitea-build:
|
||||||
|
nix build .#gitea-local-fork --print-build-logs
|
||||||
|
|
||||||
|
# Refresh gitea-local-fork: bump rev/hashes/version, retag the gitea fork, push both repos
|
||||||
|
gitea-update:
|
||||||
|
nix run nixpkgs#nix-update -- \
|
||||||
|
--flake gitea-local-fork \
|
||||||
|
--use-update-script
|
||||||
|
|
||||||
|
# Run gitea from the attic-cached build (no local recompile)
|
||||||
|
gitea-run:
|
||||||
|
nix run \
|
||||||
|
--extra-substituters "https://nix-cache-custom.oleks.space/attic-infra-cache-k3s-1" \
|
||||||
|
--extra-trusted-public-keys "attic-infra-cache-k3s-1:qYSNK3DmttQXCFqn1t50qoWGtQNPRFWq9mgQjD05DeU=" \
|
||||||
|
git+https://git.oleks.space/oleks/flake-hub#gitea-local-fork -- --help
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Narrow fixes for packages that fail under the fleet's pinned nixpkgs
|
||||||
|
# with GCC 15 / current upstream breakage. Remove entries as upstream fixes
|
||||||
|
# land so consumers can drop them on the next pin bump.
|
||||||
|
final: prev: {
|
||||||
|
# hotdoc's bundled cmark declares `project(cmark VERSION 0.28.3)` without
|
||||||
|
# LANGUAGES, which makes CMake probe CXX; GCC 15 fails the probe. Adding
|
||||||
|
# LANGUAGES C skips the CXX check.
|
||||||
|
hotdoc = prev.hotdoc.overrideAttrs (old: {
|
||||||
|
postPatch = (old.postPatch or "") + ''
|
||||||
|
substituteInPlace cmark/CMakeLists.txt \
|
||||||
|
--replace-fail "project(cmark VERSION 0.28.3)" "project(cmark VERSION 0.28.3 LANGUAGES C)"
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
# kitty's bundled base64 lib has incompatible pointer types that GCC 15
|
||||||
|
# promotes from warning to error.
|
||||||
|
kitty = prev.kitty.overrideAttrs (old: {
|
||||||
|
env = (old.env or { }) // {
|
||||||
|
NIX_CFLAGS_COMPILE = (old.env.NIX_CFLAGS_COMPILE or "") + " -Wno-error=incompatible-pointer-types";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
# Skip D-Bus-dependent checks that fail under remote-builder sandboxes.
|
||||||
|
libsecret = prev.libsecret.overrideAttrs (_: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
xdg-desktop-portal = prev.xdg-desktop-portal.overrideAttrs (_: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
# afdko has two pre-existing test failures that block
|
||||||
|
# nototools → noto-fonts-color-emoji.
|
||||||
|
pythonPackagesExtensions = (prev.pythonPackagesExtensions or [ ]) ++ [
|
||||||
|
(pyFinal: pyPrev: {
|
||||||
|
afdko = pyPrev.afdko.overrideAttrs (old: {
|
||||||
|
disabledTests = (old.disabledTests or [ ]) ++ [
|
||||||
|
"test_non_varying_glyphs_bug356"
|
||||||
|
"test_ufo_contentsplist_parsing"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildGoModule,
|
||||||
|
fetchFromGitHub,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "geesefs";
|
||||||
|
version = "0.43.5";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "yandex-cloud";
|
||||||
|
repo = "geesefs";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-cfeL7fnxS+UFUlRVLiO09GHuEOvkiH5PkKcoH+jNRhY=";
|
||||||
|
};
|
||||||
|
|
||||||
|
proxyVendor = true;
|
||||||
|
vendorHash = "sha256-p+shpYrPxYLXpW6A4a/5qM90KH+pcMCqZOPoYTE77f0=";
|
||||||
|
|
||||||
|
subPackages = [ "." ];
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "FUSE FS implementation over S3";
|
||||||
|
homepage = "https://github.com/yandex-cloud/geesefs";
|
||||||
|
license = [ lib.licenses.mit ];
|
||||||
|
platforms = lib.platforms.unix;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildGoModule,
|
||||||
|
fetchgit,
|
||||||
|
makeWrapper,
|
||||||
|
git,
|
||||||
|
bash,
|
||||||
|
coreutils,
|
||||||
|
gzip,
|
||||||
|
nodejs,
|
||||||
|
openssh,
|
||||||
|
fetchPnpmDeps,
|
||||||
|
pnpmConfigHook,
|
||||||
|
pnpm,
|
||||||
|
stdenv,
|
||||||
|
sqliteSupport ? true,
|
||||||
|
}:
|
||||||
|
|
||||||
|
# Custom Gitea built from Oleks's local fork.
|
||||||
|
#
|
||||||
|
# Fork repo (local working copy): /home/oleks/projects/gitea
|
||||||
|
# Fork remote: https://git.oleks.space/oleks/gitea.git
|
||||||
|
# Tracked branch: oleks/main (the fork's integration tip;
|
||||||
|
# formerly `feat/projects-api`, renamed
|
||||||
|
# 2026-05-13. Carries upstream/main +
|
||||||
|
# PR #37518 Projects REST API + a few
|
||||||
|
# fork-local commits.)
|
||||||
|
# Pin discipline: nix-update bumps `rev` and tags the fork
|
||||||
|
# at `v<version>` (see passthru.updateScript
|
||||||
|
# below) so the rev stays reachable even
|
||||||
|
# after future force-pushes / rebases.
|
||||||
|
#
|
||||||
|
# Mirrors the structure of nixpkgs' gitea derivation (frontend pnpm sub-drv
|
||||||
|
# + buildGoModule with go:embed of public/) but with our fork as src.
|
||||||
|
|
||||||
|
let
|
||||||
|
pname = "gitea-local-fork";
|
||||||
|
version = "1.26.0-unstable-2026-05-17.1";
|
||||||
|
|
||||||
|
src = fetchgit {
|
||||||
|
url = "https://git.oleks.space/oleks/gitea.git";
|
||||||
|
rev = "c45ea82fd1408bdb7170308080e0eb0ffdea85b2";
|
||||||
|
hash = "sha256-AZKXcweNi6JfEgpfuj+agBNLWrQ50fTpxUalNv3XC9A=";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Gitea's package.json requires pnpm >= 11; nixpkgs ships pnpm 10. The
|
||||||
|
# lockfile is v9 (forward-compatible), so strip the engine constraint and
|
||||||
|
# the packageManager preference before any pnpm tool sees the source.
|
||||||
|
# We only patch the source the frontend sees; the Go build doesn't care.
|
||||||
|
frontendSrc = stdenv.mkDerivation {
|
||||||
|
pname = "${pname}-frontend-src";
|
||||||
|
inherit version src;
|
||||||
|
dontConfigure = true;
|
||||||
|
dontBuild = true;
|
||||||
|
installPhase = ''
|
||||||
|
cp -R . $out
|
||||||
|
chmod -R +w $out
|
||||||
|
${nodejs}/bin/node -e '
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = process.argv[1];
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(path, "utf8"));
|
||||||
|
if (pkg.engines) delete pkg.engines.pnpm;
|
||||||
|
delete pkg.packageManager;
|
||||||
|
fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n");
|
||||||
|
' "$out/package.json"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend = stdenv.mkDerivation (finalAttrs: {
|
||||||
|
pname = "${pname}-frontend";
|
||||||
|
inherit version;
|
||||||
|
src = frontendSrc;
|
||||||
|
|
||||||
|
pnpmDeps = fetchPnpmDeps {
|
||||||
|
inherit (finalAttrs) pname version src;
|
||||||
|
fetcherVersion = 3;
|
||||||
|
hash = "sha256-TisyRChMRFm5GgdW2wXPHS7NIPu04SSd97oVEgKJ2dI=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
nodejs
|
||||||
|
pnpmConfigHook
|
||||||
|
pnpm
|
||||||
|
];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
make frontend
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -R public $out/
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
in
|
||||||
|
buildGoModule {
|
||||||
|
inherit pname version src;
|
||||||
|
|
||||||
|
proxyVendor = true;
|
||||||
|
vendorHash = "sha256-TxLmmeMinazGmIx94iQvpsncWKXb+42ddZcxbwayVgk=";
|
||||||
|
|
||||||
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"data"
|
||||||
|
];
|
||||||
|
|
||||||
|
subPackages = [ "." ];
|
||||||
|
|
||||||
|
nativeBuildInputs = [ makeWrapper ];
|
||||||
|
|
||||||
|
tags = lib.optionals sqliteSupport [
|
||||||
|
"sqlite"
|
||||||
|
"sqlite_unlock_notify"
|
||||||
|
];
|
||||||
|
|
||||||
|
ldflags = [
|
||||||
|
"-s"
|
||||||
|
"-w"
|
||||||
|
"-X main.Version=${version}"
|
||||||
|
"-X 'main.Tags=${
|
||||||
|
lib.concatStringsSep " " (
|
||||||
|
lib.optionals sqliteSupport [
|
||||||
|
"sqlite"
|
||||||
|
"sqlite_unlock_notify"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}'"
|
||||||
|
];
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mkdir $data
|
||||||
|
ln -s ${frontend}/public $data/public
|
||||||
|
cp -R ./{templates,options} $data
|
||||||
|
mkdir -p $out
|
||||||
|
cp -R ./options/locale $out/locale
|
||||||
|
|
||||||
|
wrapProgram $out/bin/gitea \
|
||||||
|
--prefix PATH : ${
|
||||||
|
lib.makeBinPath [
|
||||||
|
bash
|
||||||
|
coreutils
|
||||||
|
git
|
||||||
|
gzip
|
||||||
|
openssh
|
||||||
|
]
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
# Expose hashes nix-update needs to find when iterating.
|
||||||
|
inherit (frontend) pnpmDeps;
|
||||||
|
|
||||||
|
# Custom update flow: refresh hashes + retag the gitea fork + push.
|
||||||
|
# Invoke with `nix-update --use-update-script gitea-local-fork` or
|
||||||
|
# `just gitea-update` (which passes `--use-update-script`).
|
||||||
|
updateScript = ../scripts/update-gitea-local-fork.sh;
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Gitea fork with Projects REST API (upstream PR #37518)";
|
||||||
|
homepage = "https://git.oleks.space/oleks/gitea";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
mainProgram = "gitea";
|
||||||
|
platforms = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Rebuilds the Hyprspace plugin against the consumer's Hyprland. Requires
|
||||||
|
# `pkgs.hyprland` to already be in scope (consumers apply the upstream
|
||||||
|
# Hyprland flake overlay first). `src` comes from the hyprspace flake
|
||||||
|
# input pinned in ../flake.nix.
|
||||||
|
{
|
||||||
|
gcc14Stdenv,
|
||||||
|
hyprland,
|
||||||
|
src,
|
||||||
|
}:
|
||||||
|
|
||||||
|
gcc14Stdenv.mkDerivation {
|
||||||
|
pname = "Hyprspace";
|
||||||
|
version = "unstable-${src.shortRev or "dirty"}";
|
||||||
|
inherit src;
|
||||||
|
inherit (hyprland) nativeBuildInputs;
|
||||||
|
buildInputs = [ hyprland ] ++ hyprland.buildInputs;
|
||||||
|
dontUseCmakeConfigure = true;
|
||||||
|
installFlags = [ "PREFIX=$(out)" ];
|
||||||
|
postInstall = ''
|
||||||
|
mv $out/lib/Hyprspace.so $out/lib/libHyprspace.so
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,441 @@
|
|||||||
|
diff -urN metamcp-2.4.22.orig/apps/backend/src/index.ts metamcp-2.4.22/apps/backend/src/index.ts
|
||||||
|
--- a/apps/backend/src/index.ts 2026-05-23 23:45:36.313040968 +0300
|
||||||
|
+++ b/apps/backend/src/index.ts 2026-05-23 23:50:43.286804066 +0300
|
||||||
|
@@ -1,6 +1,14 @@
|
||||||
|
import express from "express";
|
||||||
|
|
||||||
|
import { auth } from "./auth";
|
||||||
|
+import {
|
||||||
|
+ mcpCancellationTotal,
|
||||||
|
+ mcpHopDurationSeconds,
|
||||||
|
+ mcpTraceparentSynthesizedTotal,
|
||||||
|
+ mcpUncaughtThrowTotal,
|
||||||
|
+ renderMetricsExposition,
|
||||||
|
+} from "./lib/observability/metrics";
|
||||||
|
+import { bindFromHeader, HOP_NAME, traceLog, traceStore } from "./lib/observability/trace";
|
||||||
|
import { initializeIdleServers } from "./lib/startup";
|
||||||
|
import mcpProxyRouter from "./routers/mcp-proxy";
|
||||||
|
import oauthRouter from "./routers/oauth";
|
||||||
|
@@ -9,6 +17,42 @@
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
+// cluster#49 — bind W3C trace context for every request and stamp every
|
||||||
|
+// downstream log via traceStore.run(). cluster#50 — observe wall time
|
||||||
|
+// per hop and count cancellations.
|
||||||
|
+app.use((req, res, next) => {
|
||||||
|
+ const raw = req.headers.traceparent as string | undefined;
|
||||||
|
+ const { ctx, event } = bindFromHeader(raw);
|
||||||
|
+ if (event === "traceparent_synthesized") {
|
||||||
|
+ mcpTraceparentSynthesizedTotal.inc({ hop: HOP_NAME });
|
||||||
|
+ }
|
||||||
|
+ const t0 = process.hrtime.bigint();
|
||||||
|
+ let status: "success" | "error" | "cancelled" = "success";
|
||||||
|
+ res.on("close", () => {
|
||||||
|
+ // res.writableEnded is true when we finished writing; false here
|
||||||
|
+ // means the downstream client hung up mid-response.
|
||||||
|
+ if (!res.writableEnded) {
|
||||||
|
+ status = "cancelled";
|
||||||
|
+ mcpCancellationTotal.inc({ hop: HOP_NAME, source: "downstream" });
|
||||||
|
+ }
|
||||||
|
+ const dt = Number(process.hrtime.bigint() - t0) / 1e9;
|
||||||
|
+ mcpHopDurationSeconds.observe(
|
||||||
|
+ { hop: HOP_NAME, tool_name: "", status },
|
||||||
|
+ dt,
|
||||||
|
+ );
|
||||||
|
+ });
|
||||||
|
+ traceStore.run(ctx, () => {
|
||||||
|
+ if (event) traceLog("info", `event=${event}`);
|
||||||
|
+ next();
|
||||||
|
+ });
|
||||||
|
+});
|
||||||
|
+
|
||||||
|
+// cluster#50 — Prometheus exposition. Schema in Specs/mcp-metric-schema.
|
||||||
|
+app.get("/metrics", (_req, res) => {
|
||||||
|
+ res.set("Content-Type", "text/plain; version=0.0.4");
|
||||||
|
+ res.send(renderMetricsExposition());
|
||||||
|
+});
|
||||||
|
+
|
||||||
|
// Global JSON middleware for non-proxy routes
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.path.startsWith("/mcp-proxy/") || req.path.startsWith("/metamcp/")) {
|
||||||
|
@@ -108,3 +152,39 @@
|
||||||
|
status: "ok",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
+
|
||||||
|
+// cluster#50 — Express error tail. Any unhandled error in middleware /
|
||||||
|
+// handlers funnels here; we count it so the uncaught-throw alert fires.
|
||||||
|
+app.use(
|
||||||
|
+ (
|
||||||
|
+ err: unknown,
|
||||||
|
+ _req: express.Request,
|
||||||
|
+ res: express.Response,
|
||||||
|
+ next: express.NextFunction,
|
||||||
|
+ ) => {
|
||||||
|
+ mcpUncaughtThrowTotal.inc({ hop: HOP_NAME, component: "express" });
|
||||||
|
+ traceLog("error", "event=uncaught_throw", {
|
||||||
|
+ error: err instanceof Error ? err.message : String(err),
|
||||||
|
+ });
|
||||||
|
+ if (res.headersSent) return next(err);
|
||||||
|
+ res.status(500).json({ error: "internal error" });
|
||||||
|
+ },
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+// cluster#50 — last-resort process traps so unhandled rejections /
|
||||||
|
+// uncaught exceptions still feed the alert. Counting only; existing
|
||||||
|
+// behaviour (log + continue) is preserved.
|
||||||
|
+process.on("unhandledRejection", (err) => {
|
||||||
|
+ mcpUncaughtThrowTotal.inc({ hop: HOP_NAME, component: "unhandledRejection" });
|
||||||
|
+ traceLog("error", "event=uncaught_throw", {
|
||||||
|
+ component: "unhandledRejection",
|
||||||
|
+ error: err instanceof Error ? err.message : String(err),
|
||||||
|
+ });
|
||||||
|
+});
|
||||||
|
+process.on("uncaughtException", (err) => {
|
||||||
|
+ mcpUncaughtThrowTotal.inc({ hop: HOP_NAME, component: "uncaughtException" });
|
||||||
|
+ traceLog("error", "event=uncaught_throw", {
|
||||||
|
+ component: "uncaughtException",
|
||||||
|
+ error: err.message,
|
||||||
|
+ });
|
||||||
|
+});
|
||||||
|
diff -urN metamcp-2.4.22.orig/apps/backend/src/lib/observability/metrics.ts metamcp-2.4.22/apps/backend/src/lib/observability/metrics.ts
|
||||||
|
--- a/apps/backend/src/lib/observability/metrics.ts 1970-01-01 03:00:00.000000000 +0300
|
||||||
|
+++ b/apps/backend/src/lib/observability/metrics.ts 2026-05-23 23:49:42.080145070 +0300
|
||||||
|
@@ -0,0 +1,160 @@
|
||||||
|
+// Hand-rolled Prometheus metrics for the metamcp aggregator.
|
||||||
|
+//
|
||||||
|
+// We avoid adding `prom-client` to package.json so the pnpm lockfile and
|
||||||
|
+// the Nix `pnpmDeps` hash stay untouched (cluster#50). This module
|
||||||
|
+// emits the canonical schema from Specs/mcp-metric-schema:
|
||||||
|
+// mcp_hop_duration_seconds (histogram)
|
||||||
|
+// mcp_cancellation_total (counter)
|
||||||
|
+// mcp_uncaught_throw_total (counter)
|
||||||
|
+// mcp_traceparent_synthesized_total (counter)
|
||||||
|
+//
|
||||||
|
+// Exposition is text format v0.0.4. The /metrics endpoint is registered
|
||||||
|
+// in index.ts.
|
||||||
|
+
|
||||||
|
+type LabelSet = Record<string, string>;
|
||||||
|
+
|
||||||
|
+function escapeLabel(v: string): string {
|
||||||
|
+ return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+function renderLabels(labels: LabelSet): string {
|
||||||
|
+ const parts: string[] = [];
|
||||||
|
+ for (const k of Object.keys(labels).sort()) {
|
||||||
|
+ parts.push(`${k}="${escapeLabel(labels[k])}"`);
|
||||||
|
+ }
|
||||||
|
+ return parts.length ? `{${parts.join(",")}}` : "";
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+function labelKey(labels: LabelSet): string {
|
||||||
|
+ // Stable key for the labels map. Sort keys so the order is deterministic.
|
||||||
|
+ const ks = Object.keys(labels).sort();
|
||||||
|
+ return ks.map((k) => `${k}=${labels[k]}`).join("\x1f");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+export class Counter {
|
||||||
|
+ private values = new Map<string, { labels: LabelSet; value: number }>();
|
||||||
|
+
|
||||||
|
+ constructor(
|
||||||
|
+ public readonly name: string,
|
||||||
|
+ public readonly help: string,
|
||||||
|
+ public readonly labelNames: readonly string[],
|
||||||
|
+ ) {}
|
||||||
|
+
|
||||||
|
+ inc(labels: LabelSet, by: number = 1): void {
|
||||||
|
+ const key = labelKey(labels);
|
||||||
|
+ const cur = this.values.get(key);
|
||||||
|
+ if (cur) {
|
||||||
|
+ cur.value += by;
|
||||||
|
+ } else {
|
||||||
|
+ this.values.set(key, { labels: { ...labels }, value: by });
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ render(): string {
|
||||||
|
+ const lines: string[] = [];
|
||||||
|
+ lines.push(`# HELP ${this.name} ${this.help}`);
|
||||||
|
+ lines.push(`# TYPE ${this.name} counter`);
|
||||||
|
+ for (const { labels, value } of this.values.values()) {
|
||||||
|
+ lines.push(`${this.name}${renderLabels(labels)} ${value}`);
|
||||||
|
+ }
|
||||||
|
+ return lines.join("\n");
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+export class Histogram {
|
||||||
|
+ private series = new Map<
|
||||||
|
+ string,
|
||||||
|
+ {
|
||||||
|
+ labels: LabelSet;
|
||||||
|
+ bucketCounts: number[]; // counts per bucket boundary (cumulative on render)
|
||||||
|
+ sum: number;
|
||||||
|
+ count: number;
|
||||||
|
+ }
|
||||||
|
+ >();
|
||||||
|
+
|
||||||
|
+ constructor(
|
||||||
|
+ public readonly name: string,
|
||||||
|
+ public readonly help: string,
|
||||||
|
+ public readonly labelNames: readonly string[],
|
||||||
|
+ public readonly buckets: readonly number[],
|
||||||
|
+ ) {}
|
||||||
|
+
|
||||||
|
+ observe(labels: LabelSet, value: number): void {
|
||||||
|
+ const key = labelKey(labels);
|
||||||
|
+ let s = this.series.get(key);
|
||||||
|
+ if (!s) {
|
||||||
|
+ s = {
|
||||||
|
+ labels: { ...labels },
|
||||||
|
+ bucketCounts: new Array(this.buckets.length).fill(0),
|
||||||
|
+ sum: 0,
|
||||||
|
+ count: 0,
|
||||||
|
+ };
|
||||||
|
+ this.series.set(key, s);
|
||||||
|
+ }
|
||||||
|
+ for (let i = 0; i < this.buckets.length; i++) {
|
||||||
|
+ if (value <= this.buckets[i]) s.bucketCounts[i] += 1;
|
||||||
|
+ }
|
||||||
|
+ s.sum += value;
|
||||||
|
+ s.count += 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ render(): string {
|
||||||
|
+ const lines: string[] = [];
|
||||||
|
+ lines.push(`# HELP ${this.name} ${this.help}`);
|
||||||
|
+ lines.push(`# TYPE ${this.name} histogram`);
|
||||||
|
+ for (const s of this.series.values()) {
|
||||||
|
+ for (let i = 0; i < this.buckets.length; i++) {
|
||||||
|
+ const le = String(this.buckets[i]);
|
||||||
|
+ const lbls = renderLabels({ ...s.labels, le });
|
||||||
|
+ lines.push(`${this.name}_bucket${lbls} ${s.bucketCounts[i]}`);
|
||||||
|
+ }
|
||||||
|
+ const lblsInf = renderLabels({ ...s.labels, le: "+Inf" });
|
||||||
|
+ lines.push(`${this.name}_bucket${lblsInf} ${s.count}`);
|
||||||
|
+ lines.push(`${this.name}_sum${renderLabels(s.labels)} ${s.sum}`);
|
||||||
|
+ lines.push(`${this.name}_count${renderLabels(s.labels)} ${s.count}`);
|
||||||
|
+ }
|
||||||
|
+ return lines.join("\n");
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// --- Schema instances --------------------------------------------------------
|
||||||
|
+
|
||||||
|
+const DURATION_BUCKETS = [
|
||||||
|
+ 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300,
|
||||||
|
+];
|
||||||
|
+
|
||||||
|
+export const mcpHopDurationSeconds = new Histogram(
|
||||||
|
+ "mcp_hop_duration_seconds",
|
||||||
|
+ "Wall time spent in this hop processing one MCP request",
|
||||||
|
+ ["hop", "tool_name", "status"],
|
||||||
|
+ DURATION_BUCKETS,
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+export const mcpCancellationTotal = new Counter(
|
||||||
|
+ "mcp_cancellation_total",
|
||||||
|
+ "Cancellations observed per hop",
|
||||||
|
+ ["hop", "source"],
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+export const mcpUncaughtThrowTotal = new Counter(
|
||||||
|
+ "mcp_uncaught_throw_total",
|
||||||
|
+ "Uncaught exceptions per component (today's smoking gun, see cluster#44)",
|
||||||
|
+ ["hop", "component"],
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+export const mcpTraceparentSynthesizedTotal = new Counter(
|
||||||
|
+ "mcp_traceparent_synthesized_total",
|
||||||
|
+ "Inbound MCP requests missing a traceparent header",
|
||||||
|
+ ["hop"],
|
||||||
|
+);
|
||||||
|
+
|
||||||
|
+export function renderMetricsExposition(): string {
|
||||||
|
+ return (
|
||||||
|
+ [
|
||||||
|
+ mcpHopDurationSeconds.render(),
|
||||||
|
+ mcpCancellationTotal.render(),
|
||||||
|
+ mcpUncaughtThrowTotal.render(),
|
||||||
|
+ mcpTraceparentSynthesizedTotal.render(),
|
||||||
|
+ ].join("\n") + "\n"
|
||||||
|
+ );
|
||||||
|
+}
|
||||||
|
diff -urN metamcp-2.4.22.orig/apps/backend/src/lib/observability/trace.ts metamcp-2.4.22/apps/backend/src/lib/observability/trace.ts
|
||||||
|
--- a/apps/backend/src/lib/observability/trace.ts 1970-01-01 03:00:00.000000000 +0300
|
||||||
|
+++ b/apps/backend/src/lib/observability/trace.ts 2026-05-23 23:49:11.700817887 +0300
|
||||||
|
@@ -0,0 +1,127 @@
|
||||||
|
+// W3C Trace Context propagation for the metamcp aggregator.
|
||||||
|
+//
|
||||||
|
+// Implements the contract from cluster#45 (Specs/mcp-request-id):
|
||||||
|
+// traceparent header: 00-<trace-id:32-hex>-<span-id:16-hex>-<flags:2-hex>
|
||||||
|
+// Bound per-request via AsyncLocalStorage. Inbound HTTP header is read by
|
||||||
|
+// the express middleware in index.ts; outbound JSON-RPC to stdio children
|
||||||
|
+// reads currentTraceparentForUpstream() and injects it under
|
||||||
|
+// params._meta.traceparent (process-managed-transport.send).
|
||||||
|
+
|
||||||
|
+import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
|
+import { randomBytes } from "node:crypto";
|
||||||
|
+
|
||||||
|
+export const HOP_NAME = "metamcp";
|
||||||
|
+
|
||||||
|
+export interface TraceCtx {
|
||||||
|
+ trace_id: string;
|
||||||
|
+ span_id: string;
|
||||||
|
+ parent_span_id: string;
|
||||||
|
+ hop: string;
|
||||||
|
+ tool_name: string;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+const TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/;
|
||||||
|
+
|
||||||
|
+export const traceStore = new AsyncLocalStorage<TraceCtx>();
|
||||||
|
+
|
||||||
|
+export function newTraceId(): string {
|
||||||
|
+ return randomBytes(16).toString("hex");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+export function newSpanId(): string {
|
||||||
|
+ return randomBytes(8).toString("hex");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+export type ParsedTraceparent = {
|
||||||
|
+ trace_id: string;
|
||||||
|
+ parent_span_id: string;
|
||||||
|
+ malformed: boolean;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+export function parseTraceparent(raw: string | undefined): ParsedTraceparent {
|
||||||
|
+ if (typeof raw !== "string") {
|
||||||
|
+ return { trace_id: "", parent_span_id: "", malformed: false };
|
||||||
|
+ }
|
||||||
|
+ const m = TRACEPARENT_RE.exec(raw.trim());
|
||||||
|
+ if (!m) {
|
||||||
|
+ return { trace_id: newTraceId(), parent_span_id: "", malformed: true };
|
||||||
|
+ }
|
||||||
|
+ return { trace_id: m[1], parent_span_id: m[2], malformed: false };
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+export type BindResult = {
|
||||||
|
+ ctx: TraceCtx;
|
||||||
|
+ // One of: "" (normal), "traceparent_synthesized", "traceparent_malformed".
|
||||||
|
+ // Caller emits the reserved event log so the dashboard can count it.
|
||||||
|
+ event: "" | "traceparent_synthesized" | "traceparent_malformed";
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+export function bindFromHeader(
|
||||||
|
+ raw: string | undefined,
|
||||||
|
+ tool_name: string = "",
|
||||||
|
+): BindResult {
|
||||||
|
+ let event: BindResult["event"] = "";
|
||||||
|
+ let trace_id = "";
|
||||||
|
+ let parent_span_id = "";
|
||||||
|
+ if (raw == null) {
|
||||||
|
+ trace_id = newTraceId();
|
||||||
|
+ event = "traceparent_synthesized";
|
||||||
|
+ } else {
|
||||||
|
+ const p = parseTraceparent(raw);
|
||||||
|
+ trace_id = p.trace_id;
|
||||||
|
+ parent_span_id = p.parent_span_id;
|
||||||
|
+ if (p.malformed) event = "traceparent_malformed";
|
||||||
|
+ }
|
||||||
|
+ const ctx: TraceCtx = {
|
||||||
|
+ trace_id,
|
||||||
|
+ span_id: newSpanId(),
|
||||||
|
+ parent_span_id,
|
||||||
|
+ hop: HOP_NAME,
|
||||||
|
+ tool_name,
|
||||||
|
+ };
|
||||||
|
+ return { ctx, event };
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Build a traceparent literal for outbound calls. Our span-id becomes the
|
||||||
|
+// downstream hop's parent_span_id (standard W3C propagation). Falls back
|
||||||
|
+// to synthesizing a fresh context when called outside any bound request
|
||||||
|
+// (e.g. session-pool keepalive paths that originate inside the server).
|
||||||
|
+export function currentTraceparentForUpstream(): string {
|
||||||
|
+ let ctx = traceStore.getStore();
|
||||||
|
+ if (!ctx || !ctx.trace_id) {
|
||||||
|
+ ctx = {
|
||||||
|
+ trace_id: newTraceId(),
|
||||||
|
+ span_id: newSpanId(),
|
||||||
|
+ parent_span_id: "",
|
||||||
|
+ hop: HOP_NAME,
|
||||||
|
+ tool_name: "",
|
||||||
|
+ };
|
||||||
|
+ }
|
||||||
|
+ return `00-${ctx.trace_id}-${ctx.span_id}-01`;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+// Single structured log line stamped with the current trace fields. Use
|
||||||
|
+// from any code path that has a bound trace context. Outside a context,
|
||||||
|
+// emits with empty fields so existing call sites do not crash.
|
||||||
|
+export function traceLog(
|
||||||
|
+ level: "info" | "warn" | "error",
|
||||||
|
+ msg: string,
|
||||||
|
+ extra: Record<string, unknown> = {},
|
||||||
|
+): void {
|
||||||
|
+ const ctx = traceStore.getStore();
|
||||||
|
+ const rec: Record<string, unknown> = {
|
||||||
|
+ ts: Date.now() / 1000,
|
||||||
|
+ level,
|
||||||
|
+ msg,
|
||||||
|
+ hop: HOP_NAME,
|
||||||
|
+ trace_id: ctx?.trace_id ?? "",
|
||||||
|
+ span_id: ctx?.span_id ?? "",
|
||||||
|
+ parent_span_id: ctx?.parent_span_id ?? "",
|
||||||
|
+ tool_name: ctx?.tool_name ?? "",
|
||||||
|
+ ...extra,
|
||||||
|
+ };
|
||||||
|
+ const line = JSON.stringify(rec);
|
||||||
|
+ if (level === "error") console.error(line);
|
||||||
|
+ else if (level === "warn") console.warn(line);
|
||||||
|
+ else console.log(line);
|
||||||
|
+}
|
||||||
|
diff -urN metamcp-2.4.22.orig/apps/backend/src/lib/stdio-transport/process-managed-transport.ts metamcp-2.4.22/apps/backend/src/lib/stdio-transport/process-managed-transport.ts
|
||||||
|
--- a/apps/backend/src/lib/stdio-transport/process-managed-transport.ts 2026-05-23 23:45:36.315112630 +0300
|
||||||
|
+++ b/apps/backend/src/lib/stdio-transport/process-managed-transport.ts 2026-05-23 23:51:05.898047455 +0300
|
||||||
|
@@ -6,6 +6,7 @@
|
||||||
|
import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
import spawn from "cross-spawn";
|
||||||
|
|
||||||
|
+import { currentTraceparentForUpstream } from "../observability/trace";
|
||||||
|
import { ReadBuffer, serializeMessage } from "./shared";
|
||||||
|
|
||||||
|
export type StdioServerParameters = {
|
||||||
|
@@ -279,7 +280,32 @@
|
||||||
|
throw new Error("Not connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
- const json = serializeMessage(message);
|
||||||
|
+ // cluster#49 — propagate W3C trace context to the stdio child via
|
||||||
|
+ // params._meta.traceparent (the MCP spec's standard escape hatch
|
||||||
|
+ // for transport-agnostic metadata). Only stamp on messages with
|
||||||
|
+ // params (notifications/requests); JSON-RPC responses ride back
|
||||||
|
+ // up so don't need it. Idempotent: existing _meta is preserved.
|
||||||
|
+ const tracedMessage = (() => {
|
||||||
|
+ const m = message as JSONRPCMessage & {
|
||||||
|
+ params?: Record<string, unknown>;
|
||||||
|
+ };
|
||||||
|
+ if (!m || typeof m !== "object" || !("params" in m)) return message;
|
||||||
|
+ if (m.params == null || typeof m.params !== "object") return message;
|
||||||
|
+ const meta = (m.params as Record<string, unknown>)._meta;
|
||||||
|
+ const traceparent = currentTraceparentForUpstream();
|
||||||
|
+ return {
|
||||||
|
+ ...m,
|
||||||
|
+ params: {
|
||||||
|
+ ...m.params,
|
||||||
|
+ _meta: {
|
||||||
|
+ ...(typeof meta === "object" && meta !== null ? meta : {}),
|
||||||
|
+ traceparent,
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
+ } as JSONRPCMessage;
|
||||||
|
+ })();
|
||||||
|
+
|
||||||
|
+ const json = serializeMessage(tracedMessage);
|
||||||
|
if (this._process.stdin.write(json)) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchFromGitHub,
|
||||||
|
makeWrapper,
|
||||||
|
nodejs_20,
|
||||||
|
pnpm_10,
|
||||||
|
fetchPnpmDeps,
|
||||||
|
pnpmConfigHook,
|
||||||
|
postgresql,
|
||||||
|
}:
|
||||||
|
|
||||||
|
# MetaMCP — MCP aggregator/orchestrator.
|
||||||
|
#
|
||||||
|
# Upstream is a pnpm monorepo (Turbo) with two apps:
|
||||||
|
# - apps/backend : Express/tRPC API, built with tsup -> dist/index.js
|
||||||
|
# port 12009 (internal), runs drizzle-kit migrations at boot
|
||||||
|
# - apps/frontend : Next.js 15, port 12008 (public), launched via `next start`
|
||||||
|
#
|
||||||
|
# Both require runtime node_modules (Next.js non-standalone; drizzle-kit is
|
||||||
|
# a devDep invoked at runtime by docker-entrypoint.sh). We ship the whole
|
||||||
|
# built tree under $out/lib/metamcp and provide launcher scripts.
|
||||||
|
#
|
||||||
|
# The upstream Dockerfile patches Next.js' proxy timeout (30s -> 600s) by
|
||||||
|
# sed-editing two files inside node_modules/.pnpm/next@.../...; we replicate
|
||||||
|
# that in postBuild so the behaviour matches the official image.
|
||||||
|
#
|
||||||
|
# Build is single-derivation: pnpm fetch -> pnpm build -> install. First
|
||||||
|
# build prints the right pnpmDeps hash; paste it back here and rebuild.
|
||||||
|
|
||||||
|
let
|
||||||
|
pname = "metamcp";
|
||||||
|
version = "2.4.22";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "metatool-ai";
|
||||||
|
repo = "metamcp";
|
||||||
|
rev = "v${version}";
|
||||||
|
hash = "sha256-EEb3RUjsaJ5ZSHSIkAxfdV/BAjZEAvw3rtjALM4RpSc=";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
|
inherit pname version src;
|
||||||
|
|
||||||
|
pnpmDeps = fetchPnpmDeps {
|
||||||
|
inherit pname version src;
|
||||||
|
fetcherVersion = 3;
|
||||||
|
hash = "sha256-nHHLLu5wBzzP4i/oTnOkuIiPQvvvBAIIVtKdfpDiXQw=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
nodejs_20
|
||||||
|
pnpm_10
|
||||||
|
pnpmConfigHook
|
||||||
|
makeWrapper
|
||||||
|
];
|
||||||
|
|
||||||
|
# Upstream pins `packageManager: pnpm@9.0.0`; nixpkgs ships pnpm 10. The
|
||||||
|
# lockfile is v9 (forward-compatible). Rewrite the pin to the pnpm we
|
||||||
|
# actually have so pnpm 10 doesn't try to network-fetch pnpm@9 and Turbo
|
||||||
|
# (which *requires* the field) still finds it.
|
||||||
|
postPatch = ''
|
||||||
|
${nodejs_20}/bin/node -e '
|
||||||
|
const fs = require("fs");
|
||||||
|
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
||||||
|
pkg.packageManager = "pnpm@${pnpm_10.version}";
|
||||||
|
if (pkg.engines) delete pkg.engines.pnpm;
|
||||||
|
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2) + "\n");
|
||||||
|
'
|
||||||
|
|
||||||
|
# cluster#49, cluster#50 — observability patch: W3C traceparent
|
||||||
|
# propagation (HTTP header → params._meta.traceparent on stdio
|
||||||
|
# children) + hand-rolled Prometheus metrics on /metrics, no new
|
||||||
|
# dependency so pnpmDeps stays untouched. Two new source files
|
||||||
|
# under apps/backend/src/lib/observability/ and minimal edits to
|
||||||
|
# apps/backend/src/index.ts and process-managed-transport.ts.
|
||||||
|
# Retire when this lands upstream.
|
||||||
|
patch -p1 < ${./metamcp-observability.patch}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# pnpmConfigHook places node_modules; build the workspace with Turbo.
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
|
||||||
|
# Match the upstream Dockerfile sed-patch on Next.js proxy timeout.
|
||||||
|
# Files live under the pnpm virtual store; glob to tolerate minor
|
||||||
|
# next/react version bumps on later tags.
|
||||||
|
for f in \
|
||||||
|
node_modules/.pnpm/next@*/node_modules/next/dist/server/lib/router-utils/proxy-request.js \
|
||||||
|
node_modules/.pnpm/next@*/node_modules/next/dist/esm/server/lib/router-utils/proxy-request.js; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
sed -i -e "s/30000/600000/" "$f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out/lib/metamcp $out/bin
|
||||||
|
|
||||||
|
# Ship the whole built workspace. This is bulky but reliable:
|
||||||
|
# `next start` needs .next + node_modules + package.json side-by-side,
|
||||||
|
# and the backend's runtime invokes `pnpm exec drizzle-kit migrate`.
|
||||||
|
cp -r \
|
||||||
|
apps \
|
||||||
|
packages \
|
||||||
|
node_modules \
|
||||||
|
package.json \
|
||||||
|
pnpm-workspace.yaml \
|
||||||
|
pnpm-lock.yaml \
|
||||||
|
turbo.json \
|
||||||
|
$out/lib/metamcp/
|
||||||
|
|
||||||
|
# Next.js standalone post-processing: `output: "standalone"` produces
|
||||||
|
# apps/frontend/.next/standalone/apps/frontend/server.js but does NOT
|
||||||
|
# copy .next/static or public/ into the standalone tree. Do it here
|
||||||
|
# so the runtime UI has its CSS, JS chunks and static assets.
|
||||||
|
SA=$out/lib/metamcp/apps/frontend/.next/standalone/apps/frontend
|
||||||
|
cp -r $out/lib/metamcp/apps/frontend/.next/static $SA/.next/static
|
||||||
|
cp -r $out/lib/metamcp/apps/frontend/public $SA/public
|
||||||
|
|
||||||
|
# Launcher: re-implementation of upstream's docker-entrypoint.sh.
|
||||||
|
# Cleaner than substituteInPlace-ing the upstream script (the original
|
||||||
|
# has overlapping `/app` substrings that break naive replacement) and
|
||||||
|
# gives us a single place to keep the orchestration logic in sync.
|
||||||
|
cat > $out/bin/metamcp <<EOF
|
||||||
|
#!${stdenv.shell}
|
||||||
|
set -e
|
||||||
|
|
||||||
|
export PATH=${
|
||||||
|
lib.makeBinPath [
|
||||||
|
nodejs_20
|
||||||
|
pnpm_10
|
||||||
|
postgresql # for pg_isready
|
||||||
|
]
|
||||||
|
}:\$PATH
|
||||||
|
|
||||||
|
ROOT=$out/lib/metamcp
|
||||||
|
: "\''${POSTGRES_HOST:=127.0.0.1}"
|
||||||
|
: "\''${POSTGRES_PORT:=5432}"
|
||||||
|
: "\''${POSTGRES_USER:=postgres}"
|
||||||
|
|
||||||
|
echo "Waiting for PostgreSQL at \$POSTGRES_HOST:\$POSTGRES_PORT..."
|
||||||
|
until pg_isready -h "\$POSTGRES_HOST" -p "\$POSTGRES_PORT" -U "\$POSTGRES_USER"; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Running drizzle migrations..."
|
||||||
|
cd "\$ROOT/apps/backend"
|
||||||
|
pnpm exec drizzle-kit migrate
|
||||||
|
|
||||||
|
echo "Starting backend on :12009..."
|
||||||
|
PORT=12009 node "\$ROOT/apps/backend/dist/index.js" &
|
||||||
|
BACKEND_PID=\$!
|
||||||
|
sleep 3
|
||||||
|
kill -0 \$BACKEND_PID 2>/dev/null || { echo "Backend died"; exit 1; }
|
||||||
|
|
||||||
|
echo "Starting frontend on :12008..."
|
||||||
|
cd "\$ROOT/apps/frontend/.next/standalone/apps/frontend"
|
||||||
|
# Next.js standalone uses \$HOSTNAME as the bind address. The shell
|
||||||
|
# inherits HOSTNAME=<system-hostname>, leaving the server unreachable
|
||||||
|
# on 127.0.0.1 — force 0.0.0.0 unless overridden via METAMCP_HOSTNAME.
|
||||||
|
PORT=12008 HOSTNAME="\''${METAMCP_HOSTNAME:-0.0.0.0}" node server.js &
|
||||||
|
FRONTEND_PID=\$!
|
||||||
|
sleep 3
|
||||||
|
kill -0 \$FRONTEND_PID 2>/dev/null || { kill \$BACKEND_PID 2>/dev/null; echo "Frontend died"; exit 1; }
|
||||||
|
|
||||||
|
trap 'kill \$BACKEND_PID \$FRONTEND_PID 2>/dev/null || true' TERM INT
|
||||||
|
wait \$BACKEND_PID \$FRONTEND_PID
|
||||||
|
EOF
|
||||||
|
chmod +x $out/bin/metamcp
|
||||||
|
|
||||||
|
# Direct sub-launchers (handy for systemd "two-unit" deployments if
|
||||||
|
# you ever want to skip the bundled orchestrator).
|
||||||
|
makeWrapper ${nodejs_20}/bin/node $out/bin/metamcp-backend \
|
||||||
|
--add-flags "$out/lib/metamcp/apps/backend/dist/index.js" \
|
||||||
|
--set NODE_ENV production
|
||||||
|
|
||||||
|
cat > $out/bin/metamcp-frontend <<EOF
|
||||||
|
#!${stdenv.shell}
|
||||||
|
cd $out/lib/metamcp/apps/frontend/.next/standalone/apps/frontend
|
||||||
|
exec env HOSTNAME="\''${METAMCP_HOSTNAME:-0.0.0.0}" ${nodejs_20}/bin/node server.js "\$@"
|
||||||
|
EOF
|
||||||
|
chmod +x $out/bin/metamcp-frontend
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
# The pnpm/turbo build writes into $HOME; give it a writable one.
|
||||||
|
preBuild = ''
|
||||||
|
export HOME=$TMPDIR
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "MetaMCP — MCP aggregator, orchestrator, middleware, gateway";
|
||||||
|
homepage = "https://github.com/metatool-ai/metamcp";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
mainProgram = "metamcp";
|
||||||
|
platforms = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
python3Packages,
|
||||||
|
fetchurl,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
mkWheel =
|
||||||
|
{
|
||||||
|
pname,
|
||||||
|
version,
|
||||||
|
url,
|
||||||
|
sha256,
|
||||||
|
}:
|
||||||
|
python3Packages.buildPythonPackage {
|
||||||
|
inherit pname version;
|
||||||
|
src = fetchurl { inherit url sha256; };
|
||||||
|
format = "wheel";
|
||||||
|
doCheck = false;
|
||||||
|
dontCheckRuntimeDeps = true;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
xontrib-prompt-starship = mkWheel {
|
||||||
|
pname = "xontrib-prompt-starship";
|
||||||
|
version = "0.3.7";
|
||||||
|
url = "https://files.pythonhosted.org/packages/94/71/968450a151d003bb80ed6fbfb190f22cfac076cbd4442dc58b48c5545644/xontrib_prompt_starship-0.3.7-py3-none-any.whl";
|
||||||
|
sha256 = "12a213fc454c9547c6426d19c4a43982be48500019378267bdab465d0749dccd";
|
||||||
|
};
|
||||||
|
|
||||||
|
xontrib-broot = mkWheel {
|
||||||
|
pname = "xontrib-broot";
|
||||||
|
version = "0.2.1";
|
||||||
|
url = "https://files.pythonhosted.org/packages/a8/97/a595ef322a40bcfaf885af8538d48cd0c506b8e06e3834458909c6b9bf90/xontrib_broot-0.2.1-py3-none-any.whl";
|
||||||
|
sha256 = "5eee2af0740fcc0355937b1365a15bcfebbe7c045215f0a597a519ce05cca96f";
|
||||||
|
};
|
||||||
|
|
||||||
|
xontrib-term-integrations = mkWheel {
|
||||||
|
pname = "xontrib-term-integrations";
|
||||||
|
version = "0.2.0";
|
||||||
|
url = "https://files.pythonhosted.org/packages/0e/df/76f0d98fb67267124f498b0da6b598939e85d5ef9aad49c3476929395ea0/xontrib_term_integrations-0.2.0-py3-none-any.whl";
|
||||||
|
sha256 = "cc55f4a1885361349d1a5a39aa6f44abc080e318355335bc14e7ea28c5b30776";
|
||||||
|
};
|
||||||
|
|
||||||
|
xontrib-direnv = mkWheel {
|
||||||
|
pname = "xonsh-direnv";
|
||||||
|
version = "1.6.5";
|
||||||
|
url = "https://files.pythonhosted.org/packages/f4/c3/47e5e0fa9db04f8d42272d8e1ffe9367e1643da7c24b944757fc1b18369a/xonsh_direnv-1.6.5-py3-none-any.whl";
|
||||||
|
sha256 = "e67e5c4f4328d9c8ee388476c83c1eb86d75d1b53b8595a4a6022641e7fb0214";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
--- a/src/bootstrap/src/lib.rs
|
||||||
|
+++ b/src/bootstrap/src/lib.rs
|
||||||
|
@@ -1990,7 +1990,7 @@ impl Build {
|
||||||
|
use std::os::unix::fs::symlink as symlink_file;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::fs::symlink_file;
|
||||||
|
- if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
|
||||||
|
+ if !self.config.dry_run() { let _ = std::fs::remove_file(link.as_ref()); let _ = std::fs::remove_dir_all(link.as_ref()); symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns if config.ninja is enabled, and checks for ninja existence,
|
||||||
Executable
+95
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Update flow for gitea-local-fork driven by passthru.updateScript.
|
||||||
|
# Invoked by `nix-update --use-update-script gitea-local-fork`.
|
||||||
|
#
|
||||||
|
# Source fork: /home/oleks/projects/gitea
|
||||||
|
# Source remote: https://git.oleks.space/oleks/gitea.git (named `oleks` locally)
|
||||||
|
# Tracked branch: oleks/main (formerly `feat/projects-api`, renamed 2026-05-13)
|
||||||
|
#
|
||||||
|
# nix-update with --version=branch=X constructs the new version as
|
||||||
|
# `<latest-tag>-unstable-<date>` where <latest-tag> is the most recent
|
||||||
|
# tag on the remote. If we leave an old `v1.26.0-unstable-<date>` tag
|
||||||
|
# from a previous run on the gitea fork, nix-update will pick that
|
||||||
|
# as <latest-tag> and we end up with double suffixes. So:
|
||||||
|
#
|
||||||
|
# 0. Delete any prior `*-unstable-*` tag on the gitea fork (local + remote).
|
||||||
|
# 1. Run nix-update default flow: it now sees only clean semver tags
|
||||||
|
# (v1.26.0 etc.), constructs `1.26.0-unstable-<commit-date>`.
|
||||||
|
# 2. Tag the gitea fork at the new rev with v<version>, push the tag.
|
||||||
|
# 3. Commit flake-hub's package.nix change, push to origin.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
FLAKE_HUB="${FLAKE_HUB:-$HOME/projects/nix-customs/flake-hub}"
|
||||||
|
GITEA_REPO="${GITEA_REPO:-$HOME/projects/gitea}"
|
||||||
|
PKG_FILE="$FLAKE_HUB/packages/gitea-local-fork.nix"
|
||||||
|
|
||||||
|
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
||||||
|
|
||||||
|
# ── 0. Wipe prior unstable tags from gitea fork ───────────────────────────
|
||||||
|
cd "$GITEA_REPO"
|
||||||
|
log "Cleaning up previous unstable tags on gitea fork (so nix-update sees only clean semver tags)"
|
||||||
|
mapfile -t PREV_TAGS < <(git ls-remote --tags oleks 'refs/tags/v*-unstable-*' |
|
||||||
|
awk '{print $2}' |
|
||||||
|
sed 's|refs/tags/||;s|\^{}$||' |
|
||||||
|
sort -u)
|
||||||
|
for tag in "${PREV_TAGS[@]}"; do
|
||||||
|
[ -z "$tag" ] && continue
|
||||||
|
log " deleting $tag"
|
||||||
|
git tag -d "$tag" 2>/dev/null || true
|
||||||
|
git push oleks ":refs/tags/$tag" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 1. nix-update default flow ────────────────────────────────────────────
|
||||||
|
# Also normalize the in-file version to its bare semver baseline so
|
||||||
|
# nix-update doesn't see a stale unstable suffix from the local file.
|
||||||
|
cd "$FLAKE_HUB"
|
||||||
|
log "Normalizing in-file version baseline"
|
||||||
|
sed -i -E 's/(version = "[^"]+?)-unstable-[0-9]{4}-[0-9]{2}-[0-9]{2}"/\1"/' "$PKG_FILE"
|
||||||
|
|
||||||
|
log "Running nix-update against oleks/main tip on the gitea fork"
|
||||||
|
nix run nixpkgs#nix-update -- \
|
||||||
|
--flake gitea-local-fork \
|
||||||
|
--version=branch=main \
|
||||||
|
--build
|
||||||
|
|
||||||
|
# ── 2. Tag gitea fork ─────────────────────────────────────────────────────
|
||||||
|
NEW_REV=$(grep -oP 'rev = "\K[a-f0-9]{40}' "$PKG_FILE" | head -1)
|
||||||
|
NEW_VER=$(grep -oP 'version = "\K[^"]+' "$PKG_FILE" | head -1)
|
||||||
|
TAG="v${NEW_VER}"
|
||||||
|
log "New version: $NEW_VER (rev ${NEW_REV:0:12})"
|
||||||
|
|
||||||
|
cd "$GITEA_REPO"
|
||||||
|
if git rev-parse --verify --quiet "refs/tags/$TAG" >/dev/null; then
|
||||||
|
log "Tag $TAG already exists locally — skipping"
|
||||||
|
else
|
||||||
|
log "Tagging gitea fork: $TAG at ${NEW_REV:0:12}"
|
||||||
|
git -c commit.gpgsign=false tag -a "$TAG" "$NEW_REV" \
|
||||||
|
-m "Pinned by nix-customs/flake-hub gitea-local-fork derivation"
|
||||||
|
fi
|
||||||
|
log "Pushing tag to oleks remote"
|
||||||
|
git push oleks "$TAG" 2>/dev/null || log " tag already on remote"
|
||||||
|
|
||||||
|
# ── 3. Commit + push flake-hub changes ────────────────────────────────────
|
||||||
|
cd "$FLAKE_HUB"
|
||||||
|
if git diff --quiet -- "$PKG_FILE"; then
|
||||||
|
log "flake-hub: no changes to commit"
|
||||||
|
else
|
||||||
|
log "flake-hub: committing version + hashes bump"
|
||||||
|
git -c commit.gpgsign=false add "$PKG_FILE"
|
||||||
|
git -c commit.gpgsign=false commit -m "gitea-local-fork: refresh to $NEW_VER
|
||||||
|
|
||||||
|
Pin to gitea fork rev ${NEW_REV:0:12} (tag $TAG).
|
||||||
|
Refreshed by nix-update via passthru.updateScript."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git rev-parse '@{u}' >/dev/null 2>&1; then
|
||||||
|
log "flake-hub: pushing"
|
||||||
|
git push
|
||||||
|
else
|
||||||
|
log "flake-hub: no upstream tracking — skipping push"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Done. New version $NEW_VER live at:"
|
||||||
|
log " gitea tag: https://git.oleks.space/oleks/gitea/src/tag/$TAG"
|
||||||
|
log " derivation: $PKG_FILE"
|
||||||
Reference in New Issue
Block a user