feat(settings): manifest auto-sync on push + wiki pages #510
@@ -0,0 +1,42 @@
|
||||
# MokoGitea
|
||||
|
||||
Fork of Gitea — self-hosted Git service at git.mokoconsulting.tech. Go backend + TypeScript frontend.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Language** | Go 1.26+ / TypeScript |
|
||||
| **Module** | `code.mokoconsulting.tech/MokoConsulting/MokoGitea` |
|
||||
| **Branch** | develop on `dev`, merge to `main` (protected) |
|
||||
| **Wiki** | [MokoGitea Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/wiki) |
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
make help # List all available targets
|
||||
make fmt # Format .go files
|
||||
make lint-go # Lint Go code
|
||||
make lint-js # Lint TypeScript
|
||||
make tidy # After go.mod changes
|
||||
make build # Build binary
|
||||
|
||||
# Testing
|
||||
go test -run '^TestName$' ./modulepath/ # Single Go test
|
||||
pnpm exec vitest <path-filter> # Single JS test
|
||||
GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e # Single Playwright test
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- Add current year copyright header on new `.go` files
|
||||
- No trailing whitespace in edited files
|
||||
- Conventional Commits for commit messages and PR titles
|
||||
- Never force-push, amend, or squash unless asked — use new commits
|
||||
- Preserve existing code comments
|
||||
- TypeScript: use `!` (non-null assertion) not `?.`/`??` when value is known to exist
|
||||
- CSS: prefer `flex-*` helpers over per-child `tw-ml-*`/`tw-mr-*` margins
|
||||
- Add `Co-Authored-By` lines to all commits
|
||||
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
|
||||
- **Attribution**: `Authored-by: Moko Consulting`
|
||||
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
|
||||
@@ -1,16 +0,0 @@
|
||||
- Use `make help` to find available development targets
|
||||
- Run `make fmt` to format `.go` files, and run `make lint-go` to lint them
|
||||
- Run `make lint-js` to lint `.ts` files
|
||||
- Run `make tidy` after any `go.mod` changes
|
||||
- Run single go tests with `go test -run '^TestName$' ./modulepath/`
|
||||
- Run single js test files with `pnpm exec vitest <path-filter>`
|
||||
- Run single playwright e2e test files with `GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e`
|
||||
- Add the current year into the copyright header of new `.go` files
|
||||
- Ensure no trailing whitespace in edited files
|
||||
- Use Conventional Commits format for commit messages and PR titles (e.g. `type(scope): subject`)
|
||||
- Never force-push, amend, or squash unless asked. Use new commits and normal push for pull request updates
|
||||
- Preserve existing code comments, do not remove or rewrite comments that are still relevant
|
||||
- In TypeScript, use `!` (non-null assertion) instead of `?.`/`??` when a value is known to always exist
|
||||
- For CSS layout, prefer `flex-*` helpers over per-child `tw-ml-*` / `tw-mr-*` margins; fall back to `tw-*` utilities when specificity requires `!important`
|
||||
- Include authorship attribution in issue and pull request comments
|
||||
- Add `Co-Authored-By` lines to all commits, indicating name and model used
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
|
||||
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/git"
|
||||
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
|
||||
)
|
||||
|
||||
// manifestXML mirrors the .mokogitea/manifest.xml schema for XML parsing.
|
||||
type manifestXML struct {
|
||||
XMLName xml.Name `xml:"moko-platform"`
|
||||
Identity manifestIdentity `xml:"identity"`
|
||||
Governance manifestGovernance `xml:"governance"`
|
||||
Build manifestBuild `xml:"build"`
|
||||
}
|
||||
|
||||
type manifestIdentity struct {
|
||||
Name string `xml:"name"`
|
||||
Org string `xml:"org"`
|
||||
Description string `xml:"description"`
|
||||
Version string `xml:"version"`
|
||||
License manifestLicense `xml:"license"`
|
||||
}
|
||||
|
||||
type manifestLicense struct {
|
||||
SPDX string `xml:"spdx,attr"`
|
||||
Name string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type manifestGovernance struct {
|
||||
Platform string `xml:"platform"`
|
||||
StandardsVersion string `xml:"standards-version"`
|
||||
StandardsSource string `xml:"standards-source"`
|
||||
}
|
||||
|
||||
type manifestBuild struct {
|
||||
Language string `xml:"language"`
|
||||
PackageType string `xml:"package-type"`
|
||||
EntryPoint string `xml:"entry-point"`
|
||||
}
|
||||
|
||||
// SyncManifestFromCommit reads .mokogitea/manifest.xml from the given commit
|
||||
// and upserts the values into the repo_manifest database table.
|
||||
// This is called on push to the default branch to keep the database in sync
|
||||
// with the XML file. If no manifest.xml exists, this is a no-op.
|
||||
func SyncManifestFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit) {
|
||||
if commit == nil {
|
||||
return
|
||||
}
|
||||
|
||||
entry, err := commit.GetTreeEntryByPath(".mokogitea/manifest.xml")
|
||||
if err != nil || entry == nil {
|
||||
return // no manifest.xml — not an error
|
||||
}
|
||||
|
||||
reader, err := entry.Blob().DataAsync()
|
||||
if err != nil {
|
||||
log.Error("SyncManifest: read blob for %s: %v", repo.FullName(), err)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
var mxml manifestXML
|
||||
decoder := xml.NewDecoder(reader)
|
||||
if err := decoder.Decode(&mxml); err != nil {
|
||||
log.Error("SyncManifest: parse XML for %s: %v", repo.FullName(), err)
|
||||
return
|
||||
}
|
||||
|
||||
manifest := &repo_model.RepoManifest{
|
||||
RepoID: repo.ID,
|
||||
Name: mxml.Identity.Name,
|
||||
Org: mxml.Identity.Org,
|
||||
Description: mxml.Identity.Description,
|
||||
Version: mxml.Identity.Version,
|
||||
LicenseSPDX: mxml.Identity.License.SPDX,
|
||||
LicenseName: mxml.Identity.License.Name,
|
||||
Platform: mxml.Governance.Platform,
|
||||
StandardsVersion: mxml.Governance.StandardsVersion,
|
||||
StandardsSource: mxml.Governance.StandardsSource,
|
||||
Language: mxml.Build.Language,
|
||||
PackageType: mxml.Build.PackageType,
|
||||
EntryPoint: mxml.Build.EntryPoint,
|
||||
}
|
||||
|
||||
if err := repo_model.CreateOrUpdateRepoManifest(ctx, manifest); err != nil {
|
||||
log.Error("SyncManifest: save for %s: %v", repo.FullName(), err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("SyncManifest: synced .mokogitea/manifest.xml for %s", repo.FullName())
|
||||
}
|
||||
@@ -193,6 +193,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
if err := DelRepoDivergenceFromCache(ctx, repo.ID); err != nil {
|
||||
log.Error("DelRepoDivergenceFromCache: %v", err)
|
||||
}
|
||||
// Auto-sync .mokogitea/manifest.xml to database on default branch push
|
||||
SyncManifestFromCommit(ctx, repo, newCommit)
|
||||
} else {
|
||||
if err := DelDivergenceFromCache(repo.ID, branch); err != nil {
|
||||
log.Error("DelDivergenceFromCache: %v", err)
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
# Custom Fields
|
||||
|
||||
Custom fields allow organizations to define structured metadata that appears in issue sidebars and repository settings across all repos in the organization.
|
||||
|
||||
## Overview
|
||||
|
||||
Custom fields are defined at the **organization level** in Org Settings > Custom Fields. Each field has a scope:
|
||||
|
||||
- **Issue scope** — appears in the issue sidebar for inline editing
|
||||
- **Repo scope** — appears in Repository Settings > Metadata for repo-level values
|
||||
|
||||
## Field Types
|
||||
|
||||
| Type | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| `text` | Free-form text input | "Affected Component" |
|
||||
| `number` | Numeric input | "Story Points" |
|
||||
| `date` | Date picker | "Due Date" |
|
||||
| `dropdown` | Select from predefined options | "Priority: Low/Medium/High/Critical" |
|
||||
| `checkbox` | Boolean toggle | "Requires QA" |
|
||||
| `url` | URL input | "Design Link" |
|
||||
|
||||
## Org Settings
|
||||
|
||||
Navigate to **Organization Settings > Custom Fields** to manage field definitions.
|
||||
|
||||
Each field has:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| Name | Display name |
|
||||
| Scope | `issue` (sidebar) or `repo` (metadata) |
|
||||
| Type | One of: text, number, date, dropdown, checkbox, url |
|
||||
| Options | JSON array for dropdown options (e.g., `["Low","Medium","High"]`) |
|
||||
| Description | Help text (shown as tooltip) |
|
||||
| Sort Order | Controls display order |
|
||||
| Is Active | Inactive fields are hidden from new forms but preserved on existing entities |
|
||||
|
||||
## Issue Sidebar
|
||||
|
||||
Issue-scoped fields appear in the sidebar between labels and milestones. Dropdown fields auto-submit on change. Text/number/date fields display their current value.
|
||||
|
||||
Each field renders as an inline form posting to:
|
||||
```
|
||||
POST /{owner}/{repo}/issues/{issue_id}/custom-fields/{field_id}
|
||||
```
|
||||
|
||||
## Repository Metadata
|
||||
|
||||
Repo-scoped fields appear on the **Repository Settings > Metadata** page. All fields for the org are shown with their current values for the repository. Values are saved via form POST.
|
||||
|
||||
## Issue Template Integration
|
||||
|
||||
Custom fields can be pre-filled from issue template YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
name: Bug Report
|
||||
about: Report a bug
|
||||
custom_fields:
|
||||
Priority: High
|
||||
Affected Component: Backend
|
||||
```
|
||||
|
||||
When a new issue is created from this template, the sidebar shows the custom fields with the specified defaults pre-selected.
|
||||
|
||||
## API
|
||||
|
||||
### Issue-Level Custom Fields
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/v1/repos/{owner}/{repo}/issues/{index}/custom-fields` | Get field values for an issue |
|
||||
| PUT | `/api/v1/repos/{owner}/{repo}/issues/{index}/custom-fields` | Set field values (name-value map) |
|
||||
|
||||
### Repo-Level Metadata
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/v1/repos/{owner}/{repo}/metadata` | Get repo metadata field values |
|
||||
| PUT | `/api/v1/repos/{owner}/{repo}/metadata` | Set repo metadata field values |
|
||||
|
||||
### Org-Level Definitions
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/v1/orgs/{org}/custom-fields` | List all field definitions |
|
||||
| POST | `/api/v1/orgs/{org}/custom-fields` | Create a field definition |
|
||||
| DELETE | `/api/v1/orgs/{org}/custom-fields/{id}` | Delete a field definition |
|
||||
|
||||
## Database
|
||||
|
||||
### Tables
|
||||
|
||||
**`custom_field_def`** — field definitions (org-level)
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | bigint | Primary key |
|
||||
| owner_id | bigint | Org ID (0 = legacy repo-level) |
|
||||
| repo_id | bigint | 0 for org-level definitions |
|
||||
| scope | varchar(10) | `issue` or `repo` |
|
||||
| name | varchar | Field name |
|
||||
| field_type | varchar(20) | text, number, date, dropdown, checkbox, url |
|
||||
| description | text | Help text |
|
||||
| options | text | JSON array for dropdown options |
|
||||
| required | bool | Whether the field is required |
|
||||
| sort_order | int | Display order |
|
||||
| is_active | bool | Visibility flag |
|
||||
|
||||
**`custom_field_value`** — field values (per entity)
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | bigint | Primary key |
|
||||
| entity_id | bigint | Issue ID or Repo ID |
|
||||
| entity_type | varchar(10) | `issue` or `repo` |
|
||||
| field_id | bigint | FK to custom_field_def |
|
||||
| value | text | The stored value |
|
||||
|
||||
### Cascade on Delete
|
||||
|
||||
When a field definition is deleted, all associated values in `custom_field_value` are also deleted.
|
||||
|
||||
## Relationship to Other Systems
|
||||
|
||||
| System | Relationship |
|
||||
|--------|-------------|
|
||||
| Update Server | Repo-scoped custom fields with specific names (Extension Name, Display Name, etc.) are read by the update feed generators as the highest-priority metadata source. |
|
||||
| Manifest Settings | Manifest fields follow the moko-platform schema and are separate from custom fields. Custom fields are user-defined; manifest fields are standardized. |
|
||||
| Issue Statuses | Custom statuses are a separate feature with their own dedicated table and UI, not implemented as custom fields. |
|
||||
|
||||
---
|
||||
|
||||
| Revision | Date | Author | Description |
|
||||
|---|---|---|---|
|
||||
| 1.0 | 2026-06-06 | Jonathan Miller (@jmiller) | Initial version |
|
||||
@@ -0,0 +1,107 @@
|
||||
# Custom Issue Statuses
|
||||
|
||||
Custom issue statuses extend Gitea's binary Open/Closed model with org-defined workflow states. Each status has a name, color, and an optional "closes issue" flag that triggers automatic close/reopen when selected.
|
||||
|
||||
## Overview
|
||||
|
||||
Statuses are defined at the **organization level** and appear in the issue sidebar for all repositories under that organization. This is the same pattern as org-level labels and custom fields.
|
||||
|
||||
### Key Concepts
|
||||
|
||||
- **Status definitions** are managed in Org Settings > Issue Statuses
|
||||
- **Status selection** appears as a dropdown in the issue sidebar
|
||||
- **Auto close/reopen** — selecting a status with `closes_issue = true` automatically closes the issue; switching to a non-closing status reopens it
|
||||
- **Status is supplemental** — the existing Open/Closed binary state is preserved; statuses add granularity on top
|
||||
|
||||
## Org Settings
|
||||
|
||||
Navigate to **Organization Settings > Issue Statuses** to manage status definitions.
|
||||
|
||||
Each status has:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| Name | Display name (e.g., "In Progress", "Won't Fix", "Blocked") |
|
||||
| Color | Hex color for visual distinction (e.g., `#2563eb`) |
|
||||
| Description | Help text shown to users |
|
||||
| Closes Issue | When checked, selecting this status automatically closes the issue |
|
||||
| Sort Order | Controls display order in dropdowns (ascending) |
|
||||
| Is Active | Inactive statuses are hidden from dropdowns but preserved on existing issues |
|
||||
|
||||
### Example Statuses
|
||||
|
||||
| Status | Color | Closes Issue | Use Case |
|
||||
|--------|-------|:------------:|----------|
|
||||
| In Progress | Blue | No | Work is actively being done |
|
||||
| Needs Info | Yellow | No | Waiting for more information from reporter |
|
||||
| Blocked | Red | No | Cannot proceed due to external dependency |
|
||||
| Won't Fix | Gray | Yes | Decided not to address this issue |
|
||||
| Duplicate | Purple | Yes | Already tracked in another issue |
|
||||
| Resolved | Green | Yes | Fix has been implemented and verified |
|
||||
|
||||
## Issue Sidebar
|
||||
|
||||
When an organization has custom statuses defined, a **Status** dropdown appears in the issue sidebar between labels and custom fields. The dropdown:
|
||||
|
||||
- Shows all active status definitions for the repo's organization
|
||||
- Auto-submits on change (no save button needed)
|
||||
- Displays a colored left border on each option
|
||||
- Shows a power symbol on statuses that close the issue
|
||||
- Selecting "—" (empty) clears the status
|
||||
|
||||
### Auto Close/Reopen Behavior
|
||||
|
||||
| Current State | Selected Status | Result |
|
||||
|:---:|---|---|
|
||||
| Open | Status with `closes_issue = true` | Issue is closed automatically |
|
||||
| Closed | Status with `closes_issue = false` | Issue is reopened automatically |
|
||||
| Open | Status with `closes_issue = false` | Status set, issue stays open |
|
||||
| Closed | Status with `closes_issue = true` | Status set, issue stays closed |
|
||||
|
||||
All close/reopen actions go through the standard Gitea service layer, so webhooks, notifications, and timeline events fire normally.
|
||||
|
||||
## Database
|
||||
|
||||
### Tables
|
||||
|
||||
**`issue_status_def`** (migration v346) — org-level status definitions
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| id | bigint | Primary key |
|
||||
| org_id | bigint | Organization ID |
|
||||
| name | varchar | Status name |
|
||||
| color | varchar(7) | Hex color |
|
||||
| description | text | Help text |
|
||||
| closes_issue | bool | Auto-close flag |
|
||||
| sort_order | int | Display order |
|
||||
| is_active | bool | Visibility flag |
|
||||
|
||||
**`issue`** table — added `status_id` column (bigint, default 0)
|
||||
|
||||
### Cascade on Delete
|
||||
|
||||
When a status definition is deleted, all issues referencing it have their `status_id` set to 0 (cleared). Issues are not closed or reopened during deletion.
|
||||
|
||||
## Routes
|
||||
|
||||
### Web Routes (Org Settings)
|
||||
|
||||
| Method | Path | Handler |
|
||||
|--------|------|---------|
|
||||
| GET | `/org/{org}/settings/issue-statuses` | `SettingsIssueStatuses` |
|
||||
| POST | `/org/{org}/settings/issue-statuses` | `SettingsIssueStatusesCreatePost` |
|
||||
| POST | `/org/{org}/settings/issue-statuses/{id}/edit` | `SettingsIssueStatusesEditPost` |
|
||||
| POST | `/org/{org}/settings/issue-statuses/{id}/delete` | `SettingsIssueStatusesDeletePost` |
|
||||
|
||||
### Web Routes (Issue Sidebar)
|
||||
|
||||
| Method | Path | Handler |
|
||||
|--------|------|---------|
|
||||
| POST | `/{owner}/{repo}/issues/{id}/custom-status` | `UpdateIssueCustomStatus` |
|
||||
|
||||
---
|
||||
|
||||
| Revision | Date | Author | Description |
|
||||
|---|---|---|---|
|
||||
| 1.0 | 2026-06-06 | Jonathan Miller (@jmiller) | Initial version |
|
||||
@@ -0,0 +1,50 @@
|
||||
# MokoGitea
|
||||
|
||||
Moko Consulting's custom fork of [Gitea](https://gitea.com), extending the self-hosted Git service with commercial licensing, update feeds, custom issue workflows, and org-level management features.
|
||||
|
||||
| Field | Value |
|
||||
|-----|-----|
|
||||
| **Language** | Go |
|
||||
| **License** | MIT |
|
||||
| **Upstream** | Gitea 1.26.1 |
|
||||
| **Version** | v1.26.1-moko.06.04.00 |
|
||||
| **Platform** | [Gitea](https://git.mokoconsulting.tech/MokoConsulting/MokoGitea) |
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Commercial License System** — Package-based license keys with download gating, domain restriction, key expiry, and payment webhook API
|
||||
- **Update Server** — Built-in update feeds for Joomla, WordPress, Dolibarr, Composer, Drupal, PrestaShop, and WHMCS
|
||||
- **Custom Issue Statuses** — Org-defined workflow states (In Progress, Blocked, Won't Fix) with auto close/reopen
|
||||
- **Custom Fields** — Org-level field definitions for issues (sidebar) and repos (metadata) with dropdown, text, number, date, checkbox, and URL types
|
||||
- **Manifest Settings** — Per-repo identity/governance/build metadata with REST API for CI/CD integration
|
||||
- **Well-Known File Tabs** — README/LICENSE/CONTRIBUTING/SECURITY/CHANGELOG tabs on repo home page
|
||||
- **Org-Level Branch Protection** — Organization-scoped rulesets that cascade to all repos. Supports glob patterns. Full CRUD API
|
||||
- **Enterprise Sub-Orgs** — Parent-child organization hierarchy
|
||||
- **Three-Level Visibility** — Public (200), Private (403), Hidden (404) for repositories
|
||||
- **Configurable Help/Support URLs** — Replace hardcoded docs.gitea.com links via HELP_URL and SUPPORT_URL in app.ini
|
||||
- **Project Board API** — REST API endpoints for managing project boards, columns, and cards
|
||||
- **Custom branding** — Moko Consulting visual identity (logos, colors, footer)
|
||||
|
||||
## Pages
|
||||
|
||||
| Page | Description |
|
||||
|---|---|
|
||||
| [Branding](Branding) | Custom branding and visual identity details |
|
||||
| [Custom Fields](Custom-Fields) | Org-level custom fields for issues and repos |
|
||||
| [Custom Issue Statuses](Custom-Issue-Statuses) | Org-defined workflow states with auto close/reopen |
|
||||
| [Deployment](Deployment) | Production deployment guide |
|
||||
| [Manifest Settings](Manifest-Settings) | Per-repo manifest settings and REST API |
|
||||
| [Org Branch Protection API](Org-Branch-Protection-API) | Org-level branch protection rulesets and API reference |
|
||||
| [Project API](Project-API) | Custom API endpoint reference for project boards |
|
||||
| [Roadmap](Roadmap) | Development roadmap and planned features |
|
||||
|
||||
---
|
||||
|
||||
| Revision | Date | Author | Description |
|
||||
|---|---|---|---|
|
||||
| 4.0 | 2026-06-06 | Jonathan Miller (@jmiller) | Add manifest settings, custom statuses, custom fields, well-known tabs, update version to v1.26.1-moko.06.04.00 |
|
||||
| 3.0 | 2026-05-12 | Jonathan Miller (@jmiller) | Add org branch protection, help URLs, version convention |
|
||||
| 2.0 | 2026-05-10 | Jonathan Miller (@jmiller) | Rewrite with detailed features and fork documentation |
|
||||
| 1.0 | 2026-05-09 | Jonathan Miller (@jmiller) | Initial version |
|
||||
@@ -0,0 +1,111 @@
|
||||
# Manifest Settings
|
||||
|
||||
The manifest settings feature provides a centralized way to store and manage project identity, governance, and build metadata for each repository. Settings are stored in the database and exposed via both a web UI and REST API.
|
||||
|
||||
## Overview
|
||||
|
||||
Each repository can have a manifest that describes:
|
||||
|
||||
- **Identity** — project name, organization, description, version, and license
|
||||
- **Governance** — platform type, moko-platform standards version, and standards source URL
|
||||
- **Build** — language, package type, and entry point
|
||||
|
||||
These settings replace the legacy `.mokogitea/manifest.xml` file-based approach.
|
||||
|
||||
## Repo Settings Page
|
||||
|
||||
Navigate to **Repository Settings > Manifest** to view and edit manifest fields.
|
||||
|
||||
| Section | Fields |
|
||||
|---------|--------|
|
||||
| Identity | Name, Org, Description, Version, License SPDX, License Name |
|
||||
| Governance | Platform, Standards Version, Standards Source |
|
||||
| Build | Language, Package Type, Entry Point |
|
||||
|
||||
### Auto-Migration from manifest.xml
|
||||
|
||||
On first visit to the Manifest settings page, if no manifest exists in the database but a `.mokogitea/manifest.xml` file exists in the repository, the system will:
|
||||
|
||||
1. Parse the XML and extract all fields
|
||||
2. Store them in the database
|
||||
3. Display a flash message indicating migration was successful
|
||||
4. The manifest.xml file can then be manually deleted from the repository
|
||||
|
||||
If a field already has a value in the database (e.g., from org-level custom fields), the existing value is preserved and the manifest.xml value is skipped.
|
||||
|
||||
## REST API
|
||||
|
||||
The manifest API allows Actions workflows and the moko-platform CLI to read and write manifest settings programmatically.
|
||||
|
||||
### Get Manifest
|
||||
|
||||
```
|
||||
GET /api/v1/repos/{owner}/{repo}/manifest
|
||||
Authorization: token {access_token}
|
||||
```
|
||||
|
||||
Returns the current manifest settings. If no manifest has been saved, returns defaults derived from repository metadata (name, owner, description).
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"name": "MokoGitea",
|
||||
"org": "MokoConsulting",
|
||||
"description": "Moko fork of Gitea",
|
||||
"version": "06.04.00",
|
||||
"license_spdx": "GPL-3.0-or-later",
|
||||
"license_name": "GNU General Public License v3",
|
||||
"platform": "go",
|
||||
"standards_version": "05.00.00",
|
||||
"standards_source": "https://code.mokoconsulting.tech/MokoConsulting/moko-platform",
|
||||
"language": "Go",
|
||||
"package_type": "application",
|
||||
"entry_point": "./"
|
||||
}
|
||||
```
|
||||
|
||||
### Update Manifest
|
||||
|
||||
```
|
||||
PUT /api/v1/repos/{owner}/{repo}/manifest
|
||||
Authorization: token {access_token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
Requires repo admin permission. Accepts the same JSON structure as the GET response. Creates or updates the manifest.
|
||||
|
||||
### Usage in Actions Workflows
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Read manifest version
|
||||
run: |
|
||||
VERSION=$(curl -s "$GITEA_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/manifest" \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" | jq -r '.version')
|
||||
echo "Current version: $VERSION"
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
curl -s -X PUT "$GITEA_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/manifest" \
|
||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"version\": \"$NEW_VERSION\"}"
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
Manifest settings are stored in the `repo_manifest` table (migration v347). One row per repository, keyed by `repo_id`.
|
||||
|
||||
## Relationship to Other Systems
|
||||
|
||||
| System | Relationship |
|
||||
|--------|-------------|
|
||||
| Update Server | The update server generators read from both manifest settings and update_stream_config. Manifest provides identity metadata; update_stream_config provides feed-specific settings. |
|
||||
| Custom Fields | Repo-scoped custom fields (org settings) are separate from manifest fields. Custom fields are user-defined; manifest fields follow the moko-platform schema. |
|
||||
| moko-platform CLI | The CLI reads manifest settings via the API for version bumping, build decisions, and cross-repo syncing (see issue #505). |
|
||||
|
||||
---
|
||||
|
||||
| Revision | Date | Author | Description |
|
||||
|---|---|---|---|
|
||||
| 1.0 | 2026-06-06 | Jonathan Miller (@jmiller) | Initial version |
|
||||
Reference in New Issue
Block a user