Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/release-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ Version: ${VERSION}

### mono-engine

docker pull ghcr.io/web3infra-foundation/mono-engine:${VERSION}
docker pull ghcr.io/gitmono-dev/mono-engine:${VERSION}

### ui

docker pull ghcr.io/web3infra-foundation/ui:${VERSION}
docker pull ghcr.io/gitmono-dev/ui:${VERSION}

### orion-server

docker pull ghcr.io/web3infra-foundation/orion-server:${VERSION}
docker pull ghcr.io/gitmono-dev/orion-server:${VERSION}

---

Expand Down
84 changes: 75 additions & 9 deletions .github/workflows/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,177 +26,243 @@

jobs:
format:
if: ${{ !(github.repository == 'web3infra-foundation/mega' && github.event_name == 'push') }}
if: ${{ !(github.repository == 'gitmono-dev/mega' && github.event_name == 'push') }}
name: Rustfmt Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive


- name: Install nightly toolchain
run: |
rustup toolchain install nightly --profile minimal
rustup component add rustfmt --toolchain nightly

- run: cargo +nightly fmt --all --check

#
clippy:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
if: ${{ !(github.repository == 'web3infra-foundation/mega' && github.event_name == 'push') }}
if: ${{ !(github.repository == 'gitmono-dev/mega' && github.event_name == 'push') }}
name: Clippy Check
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
RUSTUP_TOOLCHAIN: stable
RUSTC_WRAPPER: sccache
SCCACHE_DIR: ${{ github.workspace }}/.sccache
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive


- name: Install system dependencies
uses: ./.github/install-dep
with:
cache-key: sysdeps
platform: ubuntu
- name: Install sccache
run: |
sudo apt-get update
sudo apt-get install -y sccache
- name: Prepare sccache directory
run: |
mkdir -p "$SCCACHE_DIR"
- name: Cache sccache
uses: actions/cache@v4
with:
path: ${{ env.SCCACHE_DIR }}
key: sccache-${{ runner.os }}-stable-${{ hashFiles('**/Cargo.lock') }}-clippy
restore-keys: |
sccache-${{ runner.os }}-stable-${{ hashFiles('**/Cargo.lock') }}-
sccache-${{ runner.os }}-stable-
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: base-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
cache-on-failure: true
- name: Enforce DTO boundary (mono must not import jupiter::model)
run: |
if rg -q 'jupiter::model' mono/; then
echo "mono must not depend on jupiter::model; use ceres::model and MonoApiService facades"
rg 'jupiter::model' mono/
exit 1
fi
- name: Enforce module dependency boundaries
run: |
if rg -q 'use callisto::' mono/src/; then
echo "mono/src must not import callisto directly; use ceres::model and MonoApiService facades"
rg 'use callisto::' mono/src/
exit 1
fi
if rg -q 'jupiter::service::' mono/src/; then
echo "mono/src must not import jupiter::service directly; use MonoApiService facades"
rg 'jupiter::service::' mono/src/
exit 1
fi
if rg -q 'api_service::mono::MonoApiService' ceres/src/transport/; then
echo "ceres transport must not depend on MonoApiService"
rg 'api_service::mono::MonoApiService' ceres/src/transport/
exit 1
fi
if rg -q 'use crate::(pack|transport)' ceres/src/application/; then
echo "ceres application must not import transport/pack directly"
rg 'use crate::(pack|transport)' ceres/src/application/
exit 1
fi
if rg --glob '!lfs_router.rs' -q '\.storage\.|_stg\(\)' mono/src/api/router/; then
if rg -q 'application::' ceres/src/transport/; then
echo "ceres transport must not import application modules"
rg 'application::' ceres/src/transport/
exit 1
fi
if rg -q '\.storage\.|_stg\(\)' mono/src/api/router/; then
echo "mono routers must use MonoApiService facades, not storage"
rg --glob '!lfs_router.rs' '\.storage\.|_stg\(\)' mono/src/api/router/
rg '\.storage\.|_stg\(\)' mono/src/api/router/
exit 1
fi
MONO_MONOREPO_WHITELIST=(
mono/src/api/api_router.rs
mono/src/api/router/commit_router.rs
mono/src/api/router/preview_router.rs
mono/src/api/router/repo_router.rs
mono/src/api/router/tag_router.rs
mono/src/api/router/buck_router.rs
)
for f in mono/src/api/router/*.rs; do
skip=false
for w in "${MONO_MONOREPO_WHITELIST[@]}"; do
if [ "$f" = "$w" ]; then
skip=true
break
fi
done
if $skip; then
continue
fi
if rg -q 'monorepo\(\)' "$f"; then
echo "mono router $f must use state.services().<domain>() or state.git() instead of monorepo()"
rg 'monorepo\(\)' "$f"
exit 1
fi
done
if rg -q 'artifact_app_service|build_trigger_service' mono/; then
echo "mono must use state.services().artifact() / build_trigger() instead of state factories"
rg 'artifact_app_service|build_trigger_service' mono/
exit 1
fi
if rg -q 'ProtocolApiState' ceres/ mono/; then
echo "ProtocolApiState alias removed; use TransportRuntime"
rg 'ProtocolApiState' ceres/ mono/
exit 1
fi
MONO_STORAGE_ALLOWLIST=(
ceres/src/application/api_service/mono/admin/group.rs
ceres/src/application/api_service/mono/admin/bot.rs
ceres/src/application/api_service/mono/commit.rs
ceres/src/application/api_service/mono/edit/entry.rs
ceres/src/application/api_service/mono/sync.rs
ceres/src/application/api_service/mono/tag.rs
ceres/src/application/api_service/mono/buck/upload.rs
)
for f in $(find ceres/src/application/api_service/mono ceres/src/application/webhook -name '*.rs'); do
skip=false
for w in "${MONO_STORAGE_ALLOWLIST[@]}"; do
if [ "$f" = "$w" ]; then
skip=true
break
fi
done
if $skip; then
continue
fi
if rg -q '\.(issue_storage|cl_storage|conversation_storage)(\(\)|\b)' "$f"; then
echo "ceres mono modules must use jupiter services, not *_storage in $f"
rg '\.(issue_storage|cl_storage|conversation_storage)(\(\)|\b)' "$f"
exit 1
fi
done
if rg -q 'ceres::(api_service|pack|protocol|build_trigger|code_edit)' mono/; then
echo "mono must use ceres::application::* and ceres::transport::* instead of legacy re-exports"
rg 'ceres::(api_service|pack|protocol|build_trigger|code_edit)' mono/
exit 1
fi
- name: Run cargo clippy
run: |
sccache --start-server || true
cargo +stable clippy --all-targets --all-features -- -D warnings
sccache --show-stats || true

test:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
if: ${{ !(github.repository == 'web3infra-foundation/mega' && github.event_name == 'push') }}
if: ${{ !(github.repository == 'gitmono-dev/mega' && github.event_name == 'push') }}
name: Full Test
runs-on: ${{ fromJson(github.repository == 'web3infra-foundation/mega' && '["self-hosted","Linux","X64"]' || '"ubuntu-latest"') }}
runs-on: ${{ fromJson(github.repository == 'gitmono-dev/mega' && '["self-hosted","Linux","X64"]' || '"ubuntu-latest"') }}
env:
CARGO_TERM_COLOR: always
RUSTUP_TOOLCHAIN: stable
HOME: ${{ github.repository == 'web3infra-foundation/mega' && '/home/github' || '/home/runner' }}
HOME: ${{ github.repository == 'gitmono-dev/mega' && '/home/github' || '/home/runner' }}
RUSTC_WRAPPER: sccache
SCCACHE_DIR: ${{ github.workspace }}/.sccache
steps:
- name: Install Redis
run: sudo apt-get update && sudo apt-get install -y redis-server

- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
lfs: true


- name: Install system dependencies
uses: ./.github/install-dep
with:
cache-key: sysdeps
platform: ubuntu
self-hosted: ${{ github.repository == 'web3infra-foundation/mega' }}
self-hosted: ${{ github.repository == 'gitmono-dev/mega' }}
use-gtk: false
- name: Install sccache
run: |
sudo apt-get update
sudo apt-get install -y sccache
- name: Prepare sccache directory
run: |
mkdir -p "$SCCACHE_DIR"
- name: Cache sccache
uses: actions/cache@v4
with:
path: ${{ env.SCCACHE_DIR }}
key: sccache-${{ runner.os }}-stable-${{ hashFiles('**/Cargo.lock') }}-test
restore-keys: |
sccache-${{ runner.os }}-stable-${{ hashFiles('**/Cargo.lock') }}-
sccache-${{ runner.os }}-stable-
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: base-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
cache-on-failure: true

- name: Set up git lfs
run: |
echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
git lfs install
git config --global user.email "mega@github.com"
git config --global user.name "Mega"
git config --global lfs.url http://localhost:8000

- name: Run cargo test
run: |
sccache --start-server || true
cargo test --manifest-path common/Cargo.toml --all-features --no-fail-fast -- --nocapture
cargo test --manifest-path jupiter/Cargo.toml --all-features --no-fail-fast -- --nocapture
cargo test --manifest-path ceres/Cargo.toml --all-features --no-fail-fast -- --nocapture
cargo test --manifest-path vault/Cargo.toml --all-features --no-fail-fast -- --nocapture
cargo test --manifest-path saturn/Cargo.toml --all-features --no-fail-fast -- --nocapture
cargo test --manifest-path orion-server/Cargo.toml --all-features --no-fail-fast -- --nocapture
sccache --show-stats || true

# Note: The fuse/scorpio job has been removed as scorpio has been moved
# to its own repository: https://github.com/web3infra-foundation/scorpiofs
# to its own repository: https://github.com/gitmono-dev/scorpiofs

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
2 changes: 1 addition & 1 deletion .github/workflows/mono-engine-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:

deploy-aws:
needs: manifest
if: ${{ github.repository == 'web3infra-foundation/mega' }}
if: ${{ github.repository == 'gitmono-dev/mega' }}
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/orion-server-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:

deploy-aws:
needs: manifest
if: ${{ github.repository == 'web3infra-foundation/mega' }}
if: ${{ github.repository == 'gitmono-dev/mega' }}
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/web-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ jobs:

deploy-aws:
needs: manifest
if: ${{ github.repository == 'web3infra-foundation/mega' }}
if: ${{ github.repository == 'gitmono-dev/mega' }}
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/web-sync-server-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:

deploy-aws:
needs: build-and-push
if: ${{ github.repository == 'web3infra-foundation/mega' }}
if: ${{ github.repository == 'gitmono-dev/mega' }}
runs-on: ubuntu-latest
strategy:
matrix:
Expand Down
2 changes: 1 addition & 1 deletion ceres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ common = { workspace = true }
jupiter = { workspace = true }
callisto = { workspace = true }
git-internal = { workspace = true }
orion-client = { workspace = true }
saturn = { workspace = true }

anyhow = { workspace = true }
Expand Down Expand Up @@ -49,4 +48,5 @@ migrate = ["jupiter/migrate"]
[dev-dependencies]
axum = { workspace = true }
jupiter-migrate = { workspace = true }
orion-client = { workspace = true }
tempfile = { workspace = true }
45 changes: 36 additions & 9 deletions ceres/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,42 @@

Monorepo domain library for Mega: Git transport, REST application logic, and shared models.

## Design goals

Ceres is the **domain layer** of Mega. `mono` is the composition root (HTTP/SSH servers, routers, auth, OpenAPI); `jupiter` is persistence. Ceres owns everything in between: what Git pushes mean, how CLs merge, how REST APIs behave, and which DTOs cross crate boundaries.

| Goal | What it means in practice |
|------|---------------------------|
| **Transport / application split** | `transport/` handles Git Smart HTTP/SSH and pack I/O only. `application/` owns business reactions (CL edits, merge queue, builds, webhooks). The two layers talk through `bus` events (`MonoReceivePackFinalized`, `ImportReceivePackFinalized`), not direct imports. |
| **Thin `mono` routers** | HTTP handlers in `mono` validate input, call `MonoAppServices`, and map errors to HTTP. Routers must not touch `Storage`, `callisto`, or `jupiter::service` directly. |
| **Domain-scoped facades** | `MonoAppServices` exposes per-domain services (`cl()`, `issue()`, `user()`, `artifact()`, …). `MonoApiService` remains the Git/protocol-oriented core and narrows further over time (`git_ops`, `stack`, merge paths). |
| **DTO hub** | `ceres/model` is the only HTTP/OpenAPI schema surface for mono REST. Map `jupiter::model` / `callisto` entities here; never leak storage types into `mono`. |
| **Typed errors** | Application code returns `MegaError`. Git transport returns `ProtocolError`. HTTP status mapping lives in `common::errors`; `mono` adapts to `ApiError` / `protocol_error`. |
| **Framework isolation** | Ceres application code does not depend on `axum`. Web framework concerns stay in `mono`. (`axum-core` is confined to `infra/pack_decode.rs` for a `git-internal` stream error shim.) |
| **Enforced boundaries** | Dependency rules below are checked in CI (`.github/workflows/base.yml`): no `jupiter::model` in `mono`, no `MonoApiService` in `transport`, no legacy `ceres::api_service` paths in `mono`, no `.storage` / `_stg()` in routers. |
| **Incremental modularization** | Split the historical god-object (`mono_api_service.rs`) into `application/api_service/mono/*` modules without breaking callers. Prefer new logic in domain modules + `*ApplicationService` types over growing `MonoApiService` blindly. |
| **Substitutable ports** | Cross-cutting integration uses explicit ports (`GitOpsPort`, `BuildDispatchPort`, `changes_port`) so transport and triggers can be tested or swapped without reaching into storage details. |

**Non-goals:** Ceres does not run servers, own database migrations (`jupiter-migrate`), or define the mono ↔ Orion wire protocol (`api-model`). It does not embed frontend or deployment config.

## Module layout

```
ceres/src/
├── lib.rs
├── bus/ # Transport ↔ application event bus
├── infra/ # Shared infrastructure (GitObjectCache, pack streams, decode errors)
├── infra/ # TransportContext, GitObjectCache, pack streams, decode errors
├── transport/
│ ├── protocol/ # Smart HTTP/SSH Git protocol
│ └── pack/ # receive-pack / upload-pack handlers
├── application/
│ ├── api_service/ # MonoApiService, REST-facing ops
│ ├── api_service/ # MonoApiService, MonoAppServices, domain *ApplicationService
│ ├── code_edit/ # CL create/update pipelines + post-receive handlers
│ └── build_trigger/ # Orion build dispatch
├── model/ # HTTP/API DTOs
│ ├── build_trigger/ # Orion build dispatch
│ ├── artifact/ # Artifact upload orchestration
│ ├── notification/ # Email dispatch + notification triggers
│ └── webhook/ # Webhook admin + delivery
├── model/ # HTTP/API DTOs (utoipa schemas)
├── diff/, merge_checker/, lfs/
```

Expand Down Expand Up @@ -45,7 +66,7 @@ Three DTO layers; keep imports aligned with this table:

Rules:

- `mono` routers must **not** `use jupiter::model` — map via `ceres::model` and `MonoApiService` facades.
- `mono` routers must **not** `use jupiter::model` — map via `ceres::model` and `MonoAppServices` facades.
- `mono/src` must **not** `use callisto::` or `jupiter::service::` — storage entities and service calls stay in `ceres` application layer.
- `ceres/src/transport` must **not** reference `MonoApiService` (transport ↔ application boundary).
- `api-model` is **not** mono HTTP schema (except shared wrappers like `CommonPage` / `Pagination`).
Expand All @@ -63,9 +84,10 @@ Rules:

Rules:

- REST routers and `MonoApiService` use `MegaError` only; `api_handler` resolves import vs mono handlers and returns `MegaError`.
- REST routers and application services use `MegaError` only; `api_handler` resolves import vs mono handlers and returns `MegaError`.
- `ProtocolError` is confined to Git client protocol paths; use `mega_to_protocol_error` at transport boundaries when mapping domain failures.
- Do not use `ProtocolError` in REST handlers.
- Prefer typed `MegaError` variants (`BadRequest`, `NotFound`, `Conflict`, …) over `[code:xxx]` string encoding in new code.

Long term: extract `ceres/model` → `mega-api-types` only if a non-mono consumer needs HTTP DTOs without ceres domain code.

Expand All @@ -91,9 +113,14 @@ Import-repo pushes follow the same pattern via `ImportReceivePackFinalized` →

## Assembly (`mono`)

`mono` constructs a `TransportRuntime` (alias: `ProtocolApiState`) with storage, `GitObjectCache`, and `RuntimeApplicationHandler`, then passes it to HTTP/SSH Git routers and REST handlers.
`mono` constructs `MonoAppServices` (which owns `TransportRuntime`, domain `*ApplicationService`s, and `MonoApiService`), then passes it to REST and Git protocol handlers via `MonoApiServiceState`.

```rust
let runtime = TransportRuntime::new(storage, git_object_cache);
// runtime.application handles MonoReceivePackFinalized / ImportReceivePackFinalized
let services = MonoAppServices::new(storage, git_object_cache, build_dispatch);
// services.transport_runtime() — Git pack handlers + application event bus
// services.cl() / services.issue() / … — REST domain facades
// services.git() — Git/protocol-oriented MonoApiService (GitApplicationService alias)
// services.changes_port() — ChangesPort for build trigger (Arc<dyn ChangesPort>)
```

`build_mono_stack()` in `stack.rs` is the sole construction path for the shared `git` + `cl` pair.
16 changes: 13 additions & 3 deletions ceres/src/application/api_service/import_api_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,19 @@ use crate::{

#[derive(Clone)]
pub struct ImportApiService {
pub storage: Storage,
pub repo: Repo,
pub git_object_cache: Arc<GitObjectCache>,
storage: Storage,
repo: Repo,
git_object_cache: Arc<GitObjectCache>,
}

impl ImportApiService {
pub fn new(storage: Storage, repo: Repo, git_object_cache: Arc<GitObjectCache>) -> Self {
Self {
storage,
repo,
git_object_cache,
}
}
}

#[async_trait]
Expand Down
Loading
Loading