Compare commits

...

65 Commits

Author SHA1 Message Date
gitea-actions[bot] bfed3e16ae chore(version): pre-release bump to 06.12.03-dev [skip ci] 2026-06-07 18:15:36 +00:00
jmiller f48ca157a3 Merge pull request 'fix: allow disabling update server once enabled' (#577) from fix/update-server-disable into dev
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 10s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 36s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 39s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 3m10s
2026-06-07 18:14:14 +00:00
Jonathan Miller 2eb2ed67bf fix(licensing): allow disabling update server by deleting repo-level config
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Failing after 10s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
When the update server checkbox is unchecked, delete the repo-level
config override instead of saving it with LicensingEnabled=false. This
allows the org-level config to take effect properly and fixes the issue
where the update server could not be turned off once enabled.
2026-06-07 13:12:24 -05:00
gitea-actions[bot] 1ca4996307 chore(version): pre-release bump to 06.12.02-dev [skip ci]
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
2026-06-07 17:52:15 +00:00
jmiller f86527994b Merge pull request 'fix(manifest): fix template end mismatch in Joomla settings block' (#575) from fix/manifest-template-end into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 59s
2026-06-07 17:51:45 +00:00
Jonathan Miller c016c603b4 fix(manifest): fix template end mismatch in Joomla settings block
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
2026-06-07 12:51:20 -05:00
gitea-actions[bot] 3cd6146fa0 chore(version): pre-release bump to 06.12.01-dev [skip ci] 2026-06-07 17:37:50 +00:00
jmiller 902ee39e90 Merge pull request 'fix(manifest): sync new fields + rename moko-platform everywhere' (#573) from feat/manifest-element-name into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 23s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 2m8s
2026-06-07 17:37:09 +00:00
Jonathan Miller aae7b65329 fix(manifest): sync version_prefix + element_name, rename moko-platform everywhere
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
- manifest_sync.go: add VersionPrefix and ElementName to XML parse and DB sync
- deploy workflow: use manifest API for version prefix instead of hardcoded sed
- pre-release workflow: rename moko-platform paths to mokoplatform
- All workflow headers: rename moko-platform references
- manifest.xml: update root element to mokoplatform, add version-prefix field
- Server: symlink /opt/mokoplatform -> /opt/moko-platform for compatibility
2026-06-07 12:36:43 -05:00
gitea-actions[bot] 35f9cd2882 chore(version): pre-release bump to 05.51.03-dev [skip ci] 2026-06-07 17:26:15 +00:00
jmiller b8ad5398a3 Merge pull request 'feat(manifest): element name, version prefix, platform/language dropdowns' (#572) from feat/manifest-element-name into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 2m48s
2026-06-07 17:25:07 +00:00
Jonathan Miller 09aa8d8201 feat(manifest): add version prefix + fix platform dropdown values
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
- Add version_prefix field for fork versioning (e.g., v1.26.1-moko.)
- Change platform dropdown from programming languages to deployment
  targets (Joomla, Dolibarr, WordPress, Generic, etc.) so template
  repos can be mapped to platforms
- Available in settings UI and REST API
2026-06-07 12:24:07 -05:00
gitea-actions[bot] b6e88e4baf chore(version): pre-release bump to 05.51.02-dev [skip ci] 2026-06-07 17:13:58 +00:00
jmiller d274aabb4f Merge pull request 'feat(manifest): version prefix + platform dropdown fix' (#570) from feat/manifest-version-prefix into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 11s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 1m11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 1m32s
2026-06-07 17:13:15 +00:00
Jonathan Miller 33ebcd7726 feat(manifest): add version prefix + fix platform dropdown values
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Access control (push) Successful in 1s
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
- Add version_prefix field for fork versioning (e.g., v1.26.1-moko.)
- Change platform dropdown from programming languages to deployment
  targets (Joomla, Dolibarr, WordPress, Generic, etc.) so template
  repos can be mapped to platforms
- Available in settings UI and REST API
2026-06-07 12:11:24 -05:00
gitea-actions[bot] bcfae6d370 chore(version): pre-release bump to 05.51.01-dev [skip ci] 2026-06-07 16:58:28 +00:00
Jonathan Miller 6bb6e2ffd8 chore: rename moko-platform to MokoPlatform + changelog for v1.26.1-moko.06.12
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 11s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
PR RC Release / Build RC Release (pull_request) Failing after 31s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 1m28s
Rename moko-platform references across locale, changelog, and README.
Add changelog entry for dependency scanner, CDN, and Joomla fix.
Closes #548
2026-06-07 11:57:26 -05:00
jmiller 90fb6169d0 feat: auto-detect version bump level from PR source branch [skip ci] 2026-06-07 16:50:22 +00:00
gitea-actions[bot] 5f6d25ff7b chore(release): build 05.51.00 [skip ci] 2026-06-07 16:40:35 +00:00
jmiller 9adcac546f Merge pull request 'release: dependency scanner + CDN release delivery' (#566) from dev into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 1m41s
2026-06-07 16:39:23 +00:00
jmiller b6b4d6f525 Merge pull request 'fix(licensing): hide require-key option for Joomla update servers' (#567) from fix/hide-joomla-require-key into dev
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 3m23s
2026-06-07 16:39:09 +00:00
Jonathan Miller 74279c55e3 fix(licensing): hide require-key option for Joomla update servers
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m25s
Joomla's update system does not support license key authentication,
so hide the "Require license key for update feeds" checkbox when
the platform is set to Joomla or Joomla+Dolibarr.
2026-06-07 11:38:05 -05:00
jmiller 78803e60df Merge pull request 'feat(cdn): built-in CDN for release asset delivery' (#565) from feat/cdn-release-delivery into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 10s
Universal: Build & Release / Promote to RC (pull_request) Successful in 27s
PR RC Release / Build RC Release (pull_request) Failing after 29s
2026-06-07 16:12:41 +00:00
jmiller 75316bf80a Merge pull request 'feat(security): dependency vulnerability scanner' (#562) from feat/dependency-scanner into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
2026-06-07 16:12:24 +00:00
Jonathan Miller 37d59e7b59 feat(cdn): built-in CDN for release asset delivery (#561)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 8s
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 2m55s
Add CDN system that serves release assets via a dedicated hostname
(e.g., cdn.mokoconsulting.tech) with per-asset public/private toggles,
IP/referrer allowlists, and aggressive caching headers.

- Host-based routing intercepts CDN domain before auth middleware
- Per-attachment cdn_public flag controls CDN visibility
- Releases in an update stream are excluded from CDN (update server takes precedence)
- CORS, ETag, Cache-Control headers for downstream CDN compatibility
- IP/CIDR and referrer domain allowlists for abuse prevention
2026-06-07 11:07:30 -05:00
Jonathan Miller 18fc79fa0a feat(security): add dependency vulnerability scanner (#551)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m22s
Add dependency scanner module that parses manifest files (go.mod,
package.json, composer.json, requirements.txt) and checks dependencies
against the OSV.dev API for known CVEs. Implements the existing Scanner
interface and wires into the orchestrator for push-time scanning.
2026-06-07 10:32:04 -05:00
jmiller 931d685593 ci: auto pre-release on push to dev/alpha/beta/rc branches [skip ci] 2026-06-07 15:26:05 +00:00
Jonathan Miller 9121f1b36a Merge dev: final version
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 3m49s
2026-06-07 02:21:16 +00:00
jmiller f3ce51d629 Merge pull request 'chore: final version update' (#560) from chore/final-version into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
2026-06-07 02:21:06 +00:00
Jonathan Miller 1f505b48c7 chore: update wiki version to v1.26.1-moko.06.11.01
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m49s
2026-06-06 21:20:37 -05:00
Jonathan Miller 4b07ccc578 Merge dev: manifest desc dedup fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Deploy MokoGitea / deploy (push) Failing after 39s
2026-06-07 02:08:43 +00:00
jmiller afda7abcbe Merge pull request 'fix(settings): remove duplicate description from manifest' (#559) from fix/manifest-desc-dedup into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
2026-06-07 02:08:31 +00:00
Jonathan Miller 798d9c3ae0 fix(settings): remove duplicate description from manifest page
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
PR RC Release / Build RC Release (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m40s
Description is already managed in repo general settings. The manifest
now reads it from the repository object instead of a separate form field.
2026-06-06 21:07:39 -05:00
Jonathan Miller 4d42205cc8 Merge dev: wiki version update
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 32s
2026-06-07 01:57:22 +00:00
jmiller e0f22dd397 Merge pull request 'chore: wiki version update' (#558) from chore/wiki-version-update into dev
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
2026-06-07 01:57:10 +00:00
Jonathan Miller ecda05aa46 chore: update wiki version to v1.26.1-moko.06.11.00 and roadmap
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m26s
2026-06-06 20:56:25 -05:00
Jonathan Miller 3a492c5bd5 Merge dev: type settings, MCP SSE, npm workflow
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 29s
2026-06-07 00:53:54 +00:00
Jonathan Miller c947ebcb49 Merge dev: error pages login
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 3m47s
2026-06-06 23:26:52 +00:00
Jonathan Miller eef6292832 Merge dev: 403 OAuth login fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 28s
2026-06-06 23:16:50 +00:00
Jonathan Miller 27aeb19dda Merge dev: changelog + MCP update
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 4s
Deploy MokoGitea / deploy (push) Failing after 3m41s
2026-06-06 22:52:07 +00:00
Jonathan Miller d1b2fca784 Merge dev: wiki updates
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 3m53s
2026-06-06 22:27:01 +00:00
Jonathan Miller 971c5fc7a7 Merge dev: first-class type field + list badges
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 27s
2026-06-06 22:13:49 +00:00
Jonathan Miller cd36065464 Merge dev: dashboard badge fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 43s
2026-06-06 21:56:37 +00:00
Jonathan Miller 0debc72356 Merge dev: security tab
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 30s
2026-06-06 21:36:45 +00:00
Jonathan Miller 1ef6ef5fd4 Merge dev: security scanning platform (#508)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 2m58s
2026-06-06 21:25:52 +00:00
Jonathan Miller 62a44a3668 Merge dev: wiki folder fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 30s
2026-06-06 20:52:27 +00:00
Jonathan Miller 3c456dfe85 Merge dev: wiki dir fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 33s
2026-06-06 20:35:33 +00:00
Jonathan Miller 7b75ce9564 Merge dev: wiki type fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 30s
2026-06-06 20:29:05 +00:00
Jonathan Miller 3abd239397 Merge dev: wiki display fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 31s
2026-06-06 20:25:49 +00:00
Jonathan Miller 1e69927cec Merge dev: wiki slash fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 25s
2026-06-06 20:17:00 +00:00
Jonathan Miller c71e622e11 Merge dev: wiki folder navigation
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 37s
2026-06-06 20:10:33 +00:00
Jonathan Miller 2ba5e42113 Merge dev: wiki updates
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 28s
2026-06-06 19:50:43 +00:00
Jonathan Miller 7240deb822 Merge dev: closed issue permissions fix
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 25s
2026-06-06 19:21:58 +00:00
gitea-actions[bot] 727cff9eb8 chore(release): build 05.50.00 [skip ci] 2026-06-06 19:14:38 +00:00
jmiller 4c715d8424 Merge pull request 'release: v1.26.1-moko.06.07.02' (#529) from rc/v1.26.1-moko.06.07.02 into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 13m45s
2026-06-06 19:13:55 +00:00
gitea-actions[bot] c0acdd1f58 chore(release): build 05.49.00 [skip ci] 2026-06-06 18:43:15 +00:00
jmiller c73109e2e6 Merge pull request 'release: v1.26.1-moko.06.07.01' (#527) from rc/v1.26.1-moko.06.07.01 into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 33s
2026-06-06 18:42:28 +00:00
jmiller 3a159b7da6 Merge pull request 'release: v1.26.1-moko.06.07' (#525) from rc/v1.26.1-moko.06.07 into main
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 33s
2026-06-06 17:56:51 +00:00
jmiller 7c8b20b779 Merge pull request 'release: v1.26.1-moko.06.06.02' (#522) from rc/v1.26.1-moko.06.06.02 into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 34s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-06 17:41:13 +00:00
jmiller 4e8af85178 Merge pull request 'release: v1.26.1-moko.06.06.01' (#520) from rc/v1.26.1-moko.06.06.01 into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 27s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-06 17:30:28 +00:00
jmiller aa1a67c4cb Merge pull request 'release: v1.26.1-moko.06.06' (#518) from rc/v1.26.1-moko.06.06 into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 40s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-06 17:08:02 +00:00
Jonathan Miller 5642057c80 chore: resolve manifest conflict (use RC version)
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Deploy MokoGitea / deploy (push) Failing after 29s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-06 16:25:53 +00:00
Jonathan Miller 4dd27ccdb8 Merge release v1.26.1-moko.06.05.01 (#515) 2026-06-06 16:24:36 +00:00
gitea-actions[bot] 71a7ab04e5 chore(release): build 05.48.00 [skip ci] 2026-06-06 14:50:05 +00:00
jmiller d6dc7533ff Merge pull request 'release: v1.26.1-moko.06.05' (#511) from rc/v1.26.1-moko.06.05 into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Deploy MokoGitea / deploy (push) Failing after 27s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-06 14:49:11 +00:00
43 changed files with 2948 additions and 1904 deletions
+1 -1
View File
@@ -39,4 +39,4 @@ GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e # Single Playwright test
- 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)
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki/Home)
+1 -1
View File
@@ -8,7 +8,7 @@ contact_links:
url: https://mokoconsulting.tech/
about: Get help or ask questions through our website
- name: 📚 MokoStandards Documentation
url: https://code.mokoconsulting.tech/MokoConsulting/moko-platform
url: https://code.mokoconsulting.tech/MokoConsulting/mokoplatform
about: View our coding standards and best practices
- name: 🔒 Report a Security Vulnerability
url: https://code.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new
+3 -3
View File
@@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# REPO: https://code.mokoconsulting.tech/MokoConsulting/moko-platform
# INGROUP: mokoplatform.Automation
# REPO: https://code.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.gitea/workflows/branch-protection.yml
# BRIEF: Apply standardised branch protection rules to all governed repositories
#
@@ -62,7 +62,7 @@ jobs:
API="${GITEA_URL}/api/v1"
# Platform/standards/infra repos to exclude
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards moko-platform MokoTesting"
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards mokoplatform MokoTesting"
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
if [ -n "${{ inputs.repos }}" ]; then
+6 -5
View File
@@ -1,20 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0" schema-version="1.0">
<mokoplatform xmlns="https://standards.mokoconsulting.tech/mokoplatform/1.0" schema-version="1.0">
<identity>
<name>MokoGitea</name>
<org>MokoConsulting</org>
<description>Moko fork of Gitea -- adding project board REST API endpoints and custom enhancements</description>
<version>05.47.00</version>
<description>Moko fork of Gitea - adding project board REST API endpoints and custom enhancements</description>
<version>06.12.03</version>
<version-prefix>v1.26.1+MOKO</version-prefix>
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
</identity>
<governance>
<platform>go</platform>
<standards-version>05.00.00</standards-version>
<standards-source>https://code.mokoconsulting.tech/MokoConsulting/moko-platform</standards-source>
<standards-source>https://code.mokoconsulting.tech/MokoConsulting/mokoplatform</standards-source>
</governance>
<build>
<language>Go</language>
<package-type>application</package-type>
<entry-point>./</entry-point>
</build>
</moko-platform>
</mokoplatform>
+1 -324
View File
@@ -1,324 +1 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/universal/auto-release.yml.template
# VERSION: 05.00.00
# BRIEF: Universal build & release detects platform from manifest.xml
#
# +========================================================================+
# | UNIVERSAL BUILD & RELEASE PIPELINE |
# +========================================================================+
# | |
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
# | |
# | Platform-specific: |
# | joomla: XML manifest, type-prefixed packages |
# | dolibarr: mod*.class.php, update.txt, dev version reset |
# | generic: README-only, no update stream |
# | |
# +========================================================================+
name: "Universal: Build & Release"
on:
pull_request:
types: [opened, closed]
branches:
- main
workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: false
type: choice
default: release
options:
- release
- promote-rc
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
permissions:
contents: write
jobs:
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
promote-rc:
name: Promote to RC
runs-on: release
if: >-
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- name: Rename branch to rc
run: |
php ${MOKO_CLI}/branch_rename.php \
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
--pr "${{ github.event.pull_request.number }}"
- name: Checkout rc and configure git
run: |
git fetch origin rc
git checkout rc
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Publish RC release
run: |
php ${MOKO_CLI}/release_publish.php \
--path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Summary
if: always()
run: |
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
release:
name: Build & Release Pipeline
runs-on: release
if: >-
github.event.pull_request.merged == true ||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0
- name: Configure git for bot pushes
run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Check for merge conflict markers
run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
if [ -n "$CONFLICTS" ]; then
echo "::error::Merge conflict markers found — aborting release"
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "No conflict markers found"
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: |
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- name: "Publish stable release"
run: |
php ${MOKO_CLI}/release_publish.php \
--path . --stability stable --bump minor --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Update release notes from CHANGELOG.md
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# Extract [Unreleased] section from changelog
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Stable release"
else
NOTES="Stable release"
fi
# Update release body via API
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_mirror.php \
--version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
--branch main 2>&1 || true
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
- name: "Step 10: Push main to GitHub mirror"
if: >-
steps.version.outputs.skip != 'true' &&
secrets.GH_MIRROR_TOKEN != ''
continue-on-error: true
run: |
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
git fetch origin main --depth=1
git push github origin/main:refs/heads/main --force 2>/dev/null \
&& echo "main branch pushed to GitHub mirror" \
|| echo "WARNING: GitHub mirror push failed"
- name: "Step 11: Delete rc branch and recreate dev from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Delete rc branch (ephemeral — created by promote-rc)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/rc" 2>/dev/null \
&& echo "Deleted rc branch" || echo "rc branch not found"
# Delete dev branch
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
# Recreate dev from main (now includes version bump + changelog promotion)
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API_BASE}/branches" \
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
- name: "Step 12: Create version branch from main"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
BRANCH_NAME="version/${VERSION}"
MAIN_SHA=$(git rev-parse HEAD)
# Delete old version branch if it exists (same version re-release)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
# Create version/XX.YY.ZZ from main
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
# -- Dolibarr post-release: Reset dev version -----------------------------
- name: "Post-release: Reset dev version"
if: steps.version.outputs.skip != 'true'
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/version_reset_dev.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
--branch dev --path . 2>&1 || true
# -- Summary --------------------------------------------------------------
- name: Pipeline Summary
if: always()
run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi
placeholder
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Universal
# REPO: https://code.mokoconsulting.tech/MokoConsulting/moko-platform
# REPO: https://code.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.mokogitea/workflows/branch-cleanup.yml
# VERSION: 01.00.00
# BRIEF: Delete feature branches after PR merge
+15 -4
View File
@@ -11,7 +11,7 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Version tag (e.g. v1.26.1-moko.05.01.00)'
description: 'Version tag (e.g. v1.26.1+MOKO06.12.00)'
required: true
default: 'latest'
environment:
@@ -180,10 +180,21 @@ jobs:
exit 0
fi
# Extract moko version from tag (e.g. v1.26.1-moko.05.01.01 -> 05.01.01)
MOKO_VER=$(echo "$TAG" | sed -n 's/.*-moko\.\(.*\)/\1/p')
# Extract project version by stripping the version prefix from the tag.
# Reads prefix from manifest API (e.g. "v1.26.1+MOKO"), falls back to legacy pattern.
API_BASE="https://${REGISTRY}/api/v1/repos/MokoConsulting/MokoGitea"
PREFIX=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/manifest" | python3 -c "import json,sys; print(json.load(sys.stdin).get('version_prefix',''))" 2>/dev/null || true)
if [ -n "$PREFIX" ]; then
MOKO_VER="${TAG#$PREFIX}"
else
# Legacy fallback: strip everything up to and including "-moko."
MOKO_VER=$(echo "$TAG" | sed -n 's/.*-moko\.\(.*\)/\1/p')
fi
if [ -z "$MOKO_VER" ]; then
echo "Could not extract moko version from tag: $TAG"
echo "Could not extract version from tag: $TAG (prefix: ${PREFIX:-none})"
exit 0
fi
+2 -2
View File
@@ -4,8 +4,8 @@
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Automation
# VERSION: 05.47.00
# INGROUP: mokoplatform.Automation
# VERSION: 06.12.03
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
File diff suppressed because it is too large Load Diff
+245 -243
View File
@@ -1,243 +1,245 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.01.00
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
name: "Universal: Pre-Release"
on:
pull_request:
types: [closed]
branches:
- dev
pull_request_target:
types: [synchronize, opened, reopened]
branches:
- main
workflow_dispatch:
inputs:
stability:
description: 'Pre-release channel'
required: true
type: choice
options:
- development
- alpha
- beta
- release-candidate
permissions:
contents: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
jobs:
build:
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
runs-on: release
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') ||
(github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main')
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.MOKOGITEA_TOKEN }}
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }}
- name: Setup moko-platform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
# Use pre-installed /opt/moko-platform if available (updated by cron every 6h)
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi
- name: Detect platform
id: platform
run: |
php ${MOKO_CLI}/manifest_read.php --path . --github-output
- name: Resolve metadata and bump version
id: meta
run: |
# Auto-detect stability: RC for PRs targeting main, else use input or default to development
if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then
STABILITY="release-candidate"
else
STABILITY="${{ inputs.stability || 'development' }}"
fi
case "$STABILITY" in
development) SUFFIX="-dev"; TAG="development" ;;
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
beta) SUFFIX="-beta"; TAG="beta" ;;
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac
# Bump version via CLI: patch for dev/alpha/beta, minor for RC
case "$STABILITY" in
release-candidate) BUMP="minor" ;;
*) BUMP="patch" ;;
esac
php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
# Set stability suffix and verify consistency
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
# Append suffix for output
if [ -n "$SUFFIX" ]; then
VERSION="${VERSION}${SUFFIX}"
fi
# Commit version bump
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
git add -A
git diff --cached --quiet || {
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
git push origin HEAD 2>&1
}
# Auto-detect element via manifest_element.php
php ${MOKO_CLI}/manifest_element.php \
--path . --version "$VERSION" --stability "$STABILITY" \
--repo "${GITEA_REPO}" --github-output
# Read back element outputs
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
- name: Create release
id: release
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_create.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch dev --prerelease
- name: Update release notes from CHANGELOG.md
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
else
NOTES="Release ${VERSION}"
fi
# Update release body via API
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
- name: Build package and upload
id: package
run: |
VERSION="${{ steps.meta.outputs.version }}"
TAG="${{ steps.meta.outputs.tag }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_package.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --output /tmp || true
# updates.xml is generated dynamically by MokoGitea license server
# No need to build, commit, or sync updates.xml from workflows
- name: "Delete lesser pre-release channels (cascade)"
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
php ${MOKO_CLI}/release_cascade.php \
--stability "${{ steps.meta.outputs.stability }}" \
--token "${TOKEN}" \
--api-base "${API_BASE}"
- name: Summary
if: always()
run: |
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
SHA256="${{ steps.package.outputs.sha256_zip }}"
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.01.00
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
name: "Universal: Pre-Release"
on:
push:
branches:
- dev
- alpha
- beta
- rc
workflow_dispatch:
inputs:
stability:
description: 'Pre-release channel'
required: true
type: choice
options:
- development
- alpha
- beta
- release-candidate
permissions:
contents: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
jobs:
build:
name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
runs-on: release
if: >-
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.MOKOGITEA_TOKEN }}
ref: ${{ github.ref_name }}
- name: Setup mokoplatform tools
env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
# Use pre-installed /opt/mokoplatform if available (updated by cron every 6h)
if [ -f /opt/mokoplatform/cli/version_bump.php ] && [ -f /opt/mokoplatform/cli/manifest_element.php ] && [ -f /opt/mokoplatform/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokoplatform
echo MOKO_CLI=/opt/mokoplatform/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokoplatform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokoplatform.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokoplatform-api
cd /tmp/mokoplatform-api && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokoplatform-api/cli >> $GITHUB_ENV
fi
- name: Detect platform
id: platform
run: |
php ${MOKO_CLI}/manifest_read.php --path . --github-output
- name: Resolve metadata and bump version
id: meta
run: |
# Auto-detect stability from branch name on push, or use input on dispatch
if [ "${{ github.event_name }}" = "push" ]; then
case "${{ github.ref_name }}" in
rc) STABILITY="release-candidate" ;;
alpha) STABILITY="alpha" ;;
beta) STABILITY="beta" ;;
*) STABILITY="development" ;;
esac
else
STABILITY="${{ inputs.stability || 'development' }}"
fi
case "$STABILITY" in
development) SUFFIX="-dev"; TAG="development" ;;
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
beta) SUFFIX="-beta"; TAG="beta" ;;
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac
# Bump version via CLI: patch for dev/alpha/beta, minor for RC
case "$STABILITY" in
release-candidate) BUMP="minor" ;;
*) BUMP="patch" ;;
esac
php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
# Set stability suffix and verify consistency
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
# Append suffix for output
if [ -n "$SUFFIX" ]; then
VERSION="${VERSION}${SUFFIX}"
fi
# Commit version bump
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
git add -A
git diff --cached --quiet || {
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
git push origin HEAD 2>&1
}
# Auto-detect element via manifest_element.php
php ${MOKO_CLI}/manifest_element.php \
--path . --version "$VERSION" --stability "$STABILITY" \
--repo "${GITEA_REPO}" --github-output
# Read back element outputs
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
- name: Create release
id: release
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_create.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
- name: Update release notes from CHANGELOG.md
run: |
TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
else
NOTES="Release ${VERSION}"
fi
# Update release body via API
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
python3 -c "
import json, urllib.request
body = open('/dev/stdin').read()
payload = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH',
headers={
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "Release notes updated from CHANGELOG.md"
fi
- name: Build package and upload
id: package
run: |
VERSION="${{ steps.meta.outputs.version }}"
TAG="${{ steps.meta.outputs.tag }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php ${MOKO_CLI}/release_package.php \
--path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --output /tmp || true
# updates.xml is generated dynamically by MokoGitea license server
# No need to build, commit, or sync updates.xml from workflows
- name: "Delete lesser pre-release channels (cascade)"
continue-on-error: true
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
php ${MOKO_CLI}/release_cascade.php \
--stability "${{ steps.meta.outputs.stability }}" \
--token "${TOKEN}" \
--api-base "${API_BASE}"
- name: Summary
if: always()
run: |
VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
SHA256="${{ steps.package.outputs.sha256_zip }}"
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
+66
View File
@@ -0,0 +1,66 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoPlatform.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.mokogitea/workflows/rc-revert.yml
# VERSION: 09.23.00
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
name: "RC Revert"
on:
pull_request:
types: [closed]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
revert:
name: Rename rc/ back to dev/
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == false &&
startsWith(github.event.pull_request.head.ref, 'rc/')
steps:
- name: Rename branch
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
SUFFIX="${BRANCH#rc/}"
DEV_BRANCH="dev/${SUFFIX}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Create dev/ branch from rc/ branch
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
"${API}" 2>/dev/null || true)
if [ "$STATUS" = "201" ]; then
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"
exit 1
fi
# Delete rc/ branch
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${TOKEN}" \
"${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
fi
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
File diff suppressed because it is too large Load Diff
+19 -23
View File
@@ -3,6 +3,24 @@
All notable changes to MokoGitea are documented here. Versions follow the format
`v{upstream}-moko.{major}.{minor}` (e.g. `v1.26.1-moko.06.03`).
## [v1.26.1-moko.06.12] - 2026-06-07
* FEATURES
* feat(security): dependency vulnerability scanner - parses go.mod, package.json, composer.json, requirements.txt and checks against OSV.dev API (#551)
* feat(cdn): built-in CDN for release asset delivery via cdn.mokoconsulting.tech with per-asset public/private toggles (#561)
* feat(cdn): IP/CIDR and referrer domain allowlists for CDN abuse prevention
* feat(cdn): releases in update streams excluded from CDN (update server takes precedence)
* FIXES
* fix(licensing): hide "Require license key" option for Joomla update servers (Joomla limitation)
* fix(settings): remove duplicate description from manifest page (#559)
* INFRASTRUCTURE
* chore: rename moko-platform to MokoPlatform across codebase (#548)
* CDN CNAME: cdn.mokoconsulting.tech with auto-TLS via Let's Encrypt
* Nginx reverse proxy for CDN hostname on production server
* DreamHost MCP server path and API key configured
## [v1.26.1-moko.06.10] - 2026-06-06
* FEATURES
@@ -38,7 +56,7 @@ All notable changes to MokoGitea are documented here. Versions follow the format
* INFRASTRUCTURE
* npm: @mokoconsulting/mokogitea-mcp@1.1.0 and @mokoconsulting/mokowaas-mcp@1.0.0
* MCP servers consolidated under moko-platform/mcp/servers/
* MCP servers consolidated under mokoplatform/mcp/servers/
* Remote MCP repos renamed to hyphens
* Wiki restructured into features/, api/, operations/ folders
* Swagger API docs enabled at /api/swagger
@@ -205,25 +223,3 @@ All notable changes to MokoGitea are documented here. Versions follow the format
* Reopened 9 closed issues lacking documented testing proof
* Created `pending: testing` label for features awaiting verification
* Established policy: issues must not be closed without documented testing proof
## [1.26.1](https://github.com/go-gitea/gitea/releases/tag/v1.26.1) - 2026-04-21
* BUGFIXES
* Add event.schedule context for schedule actions task (#37320) (#37348)
* Fix an issue where changing an organization's visibility caused problems when users had forked its repositories. (#37324) (#37344)
* Use modern "git update-index --cacheinfo" syntax to support more file names (#37338) (#37343)
* Fix URL related escaping for oauth2 (#37334) (#37340)
* When the requested arch rpm is missing fall back to noarch (#37236) (#37339)
* Fix actions concurrency groups cross-branch leak (#37311) (#37331)
* Fix bug when accessing user badges (#37321) (#37329)
* Fix AppFullLink (#37325) (#37328)
* Fix container auth for public instance (#37290) (#37294)
* Enhance GetActionWorkflow to support fallback references (#37189) (#37283)
* Fix vite manifest update masking build errors (#37279) (#37310)
* Fix Mermaid diagrams failing when node labels contain line breaks (#37296) (#37299)
* Use TriggerEvent instead of Event in workflow runs API response for scheduled runs (#37288) #37360
* Add URL to Learn more about blocking a user. (#37355) #37367
* Fix button layout shift when collapsing file tree in editor (#37363) #37375
* Fix org team assignee/reviewer lookups for team member permissions (#37365) #37391
* Fix repo init README EOL (#37388) #37399
* Fix: dump with default zip type produces uncompressed zip (#37401)#37402
+2 -2
View File
@@ -18,7 +18,7 @@ Custom Gitea fork with Project Board API
---
**Category:** Infrastructure | **Platform:** [moko-platform wiki](https://code.mokoconsulting.tech/MokoConsulting/moko-platform/wiki)
**Category:** Infrastructure | **Platform:** [MokoPlatform wiki](https://code.mokoconsulting.tech/MokoConsulting/MokoPlatform/wiki)
---
@@ -40,4 +40,4 @@ This project is licensed under the GNU General Public License v3.0 or later -- s
---
*[Moko Consulting](https://mokoconsulting.tech) -- [MokoStandards](https://code.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)*
*[Moko Consulting](https://mokoconsulting.tech) -- [MokoStandards](https://code.mokoconsulting.tech/MokoConsulting/MokoPlatform/wiki/Home)*
+134
View File
@@ -0,0 +1,134 @@
// Copyright 2026 The MokoGitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package ai
import (
"context"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil"
)
// OrgSetting stores AI configuration for an organization.
type OrgSetting struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"UNIQUE NOT NULL"`
Enabled bool `xorm:"NOT NULL DEFAULT true"`
APIKeyEncrypted string `xorm:"TEXT"`
Model string `xorm:"VARCHAR(50) NOT NULL DEFAULT 'claude-sonnet-4-6'"`
RateLimitRequests int `xorm:"NOT NULL DEFAULT 100"`
RateLimitTokensMonth int64 `xorm:"NOT NULL DEFAULT 5000000"`
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}
func init() {
db.RegisterModel(new(OrgSetting))
db.RegisterModel(new(RepoSetting))
db.RegisterModel(new(UsageLog))
}
// TableName returns the table name for OrgSetting.
func (OrgSetting) TableName() string {
return "ai_org_setting"
}
// RepoSetting stores AI configuration for a repository.
type RepoSetting struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE NOT NULL"`
Enabled bool `xorm:"NOT NULL DEFAULT true"`
AutoReview bool `xorm:"NOT NULL DEFAULT true"`
Strictness string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'standard'"`
IgnorePatterns string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}
// TableName returns the table name for RepoSetting.
func (RepoSetting) TableName() string {
return "ai_repo_setting"
}
// UsageLog records AI token usage per action.
type UsageLog struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX NOT NULL"`
RepoID int64 `xorm:"INDEX NOT NULL"`
TriggeredByID int64
ActionType string `xorm:"VARCHAR(20) NOT NULL"` // review, chat, agent
Model string `xorm:"VARCHAR(50)"`
TokensInput int64
TokensOutput int64
DurationMs int64
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
}
// TableName returns the table name for UsageLog.
func (UsageLog) TableName() string {
return "ai_usage_log"
}
// GetOrgSetting returns the AI settings for an org, or nil if not configured.
func GetOrgSetting(ctx context.Context, orgID int64) (*OrgSetting, error) {
setting := &OrgSetting{OrgID: orgID}
has, err := db.GetEngine(ctx).Where("org_id = ?", orgID).Get(setting)
if err != nil {
return nil, err
}
if !has {
return nil, nil
}
return setting, nil
}
// GetRepoSetting returns the AI settings for a repo, or nil if not configured.
func GetRepoSetting(ctx context.Context, repoID int64) (*RepoSetting, error) {
setting := &RepoSetting{RepoID: repoID}
has, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Get(setting)
if err != nil {
return nil, err
}
if !has {
return nil, nil
}
return setting, nil
}
// CreateOrgSetting inserts a new org AI setting.
func CreateOrgSetting(ctx context.Context, setting *OrgSetting) error {
setting.CreatedUnix = timeutil.TimeStampNow()
setting.UpdatedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(ctx).Insert(setting)
return err
}
// UpdateOrgSetting updates an existing org AI setting.
func UpdateOrgSetting(ctx context.Context, setting *OrgSetting) error {
setting.UpdatedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(ctx).ID(setting.ID).AllCols().Update(setting)
return err
}
// CreateRepoSetting inserts a new repo AI setting.
func CreateRepoSetting(ctx context.Context, setting *RepoSetting) error {
setting.CreatedUnix = timeutil.TimeStampNow()
setting.UpdatedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(ctx).Insert(setting)
return err
}
// UpdateRepoSetting updates an existing repo AI setting.
func UpdateRepoSetting(ctx context.Context, setting *RepoSetting) error {
setting.UpdatedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(ctx).ID(setting.ID).AllCols().Update(setting)
return err
}
// LogUsage records an AI usage event.
func LogUsage(ctx context.Context, log *UsageLog) error {
log.CreatedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(ctx).Insert(log)
return err
}
+6
View File
@@ -82,6 +82,12 @@ func (c *UpdateStreamConfig) GetCustomStreams() []StreamDef {
return streams
}
// DeleteRepoConfig removes the repo-level update stream config override.
func DeleteRepoConfig(ctx context.Context, repoID int64) error {
_, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(new(UpdateStreamConfig))
return err
}
// GetActiveStreams returns the effective streams for this config.
func (c *UpdateStreamConfig) GetActiveStreams() []StreamDef {
if c.StreamMode == "custom" {
+2
View File
@@ -428,6 +428,8 @@ func prepareMigrationTasks() []*migration {
newMigration(348, "Add issue priority definitions table", v1_27.AddIssuePriorityDefTable),
newMigration(349, "Add security scanning tables", v1_27.AddSecurityScanningTables),
newMigration(350, "Add issue type definitions table", v1_27.AddIssueTypeDefTable),
newMigration(351, "Add CDN public flag to attachments", v1_27.AddAttachmentCDNPublic),
newMigration(352, "Add version prefix and element name to repo manifest", v1_27.AddManifestVersionPrefixAndElement),
}
return preparedMigrations
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2026 The MokoGitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_27
import (
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil"
"xorm.io/xorm"
)
type aiOrgSetting struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"UNIQUE NOT NULL"`
Enabled bool `xorm:"NOT NULL DEFAULT true"`
APIKeyEncrypted string `xorm:"TEXT"`
Model string `xorm:"VARCHAR(50) NOT NULL DEFAULT 'claude-sonnet-4-6'"`
RateLimitRequests int `xorm:"NOT NULL DEFAULT 100"`
RateLimitTokensMonth int64 `xorm:"NOT NULL DEFAULT 5000000"`
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}
func (aiOrgSetting) TableName() string {
return "ai_org_setting"
}
type aiRepoSetting struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE NOT NULL"`
Enabled bool `xorm:"NOT NULL DEFAULT true"`
AutoReview bool `xorm:"NOT NULL DEFAULT true"`
Strictness string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'standard'"`
IgnorePatterns string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}
func (aiRepoSetting) TableName() string {
return "ai_repo_setting"
}
type aiUsageLog struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX NOT NULL"`
RepoID int64 `xorm:"INDEX NOT NULL"`
TriggeredByID int64
ActionType string `xorm:"VARCHAR(20) NOT NULL"`
Model string `xorm:"VARCHAR(50)"`
TokensInput int64
TokensOutput int64
DurationMs int64
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
}
func (aiUsageLog) TableName() string {
return "ai_usage_log"
}
func AddAITables(x *xorm.Engine) error {
return x.Sync(new(aiOrgSetting), new(aiRepoSetting), new(aiUsageLog))
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
)
// AddRepoManifestTable creates the repo_manifest table for storing
// moko-platform manifest settings per repository.
// mokoplatform manifest settings per repository.
func AddRepoManifestTable(x *xorm.Engine) error {
type RepoManifest struct {
ID int64 `xorm:"pk autoincr"`
+14
View File
@@ -0,0 +1,14 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package v1_27
import "xorm.io/xorm"
// AddAttachmentCDNPublic adds the cdn_public column to the attachment table.
func AddAttachmentCDNPublic(x *xorm.Engine) error {
type Attachment struct {
CDNPublic bool `xorm:"NOT NULL DEFAULT false 'cdn_public'"`
}
return x.Sync(new(Attachment))
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package v1_27
import "xorm.io/xorm"
// AddManifestVersionPrefixAndElement adds version_prefix and element_name columns to repo_manifest.
func AddManifestVersionPrefixAndElement(x *xorm.Engine) error {
type RepoManifest struct {
VersionPrefix string `xorm:"TEXT 'version_prefix'"`
ElementName string `xorm:"TEXT 'element_name'"`
}
return x.Sync(new(RepoManifest))
}
+1
View File
@@ -31,6 +31,7 @@ type Attachment struct {
Name string
DownloadCount int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
CDNPublic bool `xorm:"NOT NULL DEFAULT false 'cdn_public'"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
CustomDownloadURL string `xorm:"-"`
}
+46 -3
View File
@@ -14,9 +14,9 @@ func init() {
db.RegisterModel(new(RepoManifest))
}
// RepoManifest stores moko-platform manifest settings for a repository.
// RepoManifest stores mokoplatform manifest settings for a repository.
// These fields correspond to the .mokogitea/manifest.xml schema and are
// exposed via API for use by Actions workflows and the moko-platform CLI.
// exposed via API for use by Actions workflows and the mokoplatform CLI.
type RepoManifest struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"UNIQUE INDEX NOT NULL 'repo_id'"`
@@ -31,9 +31,13 @@ type RepoManifest struct {
// governance section
Platform string `xorm:"VARCHAR(50) 'platform'"` // go, php, node, python, etc.
StandardsVersion string `xorm:"VARCHAR(20) 'standards_version'"` // moko-platform standards version
StandardsVersion string `xorm:"VARCHAR(20) 'standards_version'"` // mokoplatform standards version
StandardsSource string `xorm:"TEXT 'standards_source'"` // URL to standards repo
// versioning
VersionPrefix string `xorm:"TEXT 'version_prefix'"` // tag prefix stripped for version display, e.g. "v1.26.1-moko."
ElementName string `xorm:"TEXT 'element_name'"` // full element name override, e.g. "pkg_mokowaas" (auto-constructed if empty)
// build section
Language string `xorm:"VARCHAR(50) 'language'"` // Go, PHP, TypeScript, etc.
PackageType string `xorm:"VARCHAR(50) 'package_type'"` // application, library, plugin, module, component, package
@@ -47,6 +51,45 @@ func (RepoManifest) TableName() string {
return "repo_manifest"
}
// joomlaTypePrefix maps Joomla extension types to their element name prefixes.
var joomlaTypePrefix = map[string]string{
"component": "com_",
"module": "mod_",
"plugin": "plg_",
"package": "pkg_",
"template": "tpl_",
"library": "lib_",
"file": "file_",
}
// AutoElementName returns the auto-constructed Joomla element name (e.g. pkg_mokowaas).
func (m *RepoManifest) AutoElementName() string {
if m.Name == "" || m.PackageType == "" {
return ""
}
if prefix, ok := joomlaTypePrefix[m.PackageType]; ok {
return prefix + m.Name
}
return m.Name
}
// FullElementName returns the effective element name: override if set, otherwise auto-constructed.
func (m *RepoManifest) FullElementName() string {
if m.ElementName != "" {
return m.ElementName
}
return m.AutoElementName()
}
// ElementNameMismatch returns true if an override is set that differs from the auto-constructed name.
func (m *RepoManifest) ElementNameMismatch() bool {
if m.ElementName == "" {
return false
}
auto := m.AutoElementName()
return auto != "" && m.ElementName != auto
}
// GetRepoManifest returns the manifest for a repo, or nil if none exists.
func GetRepoManifest(ctx context.Context, repoID int64) (*RepoManifest, error) {
m := new(RepoManifest)
+25
View File
@@ -0,0 +1,25 @@
// Copyright 2026 The MokoGitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package setting
// AI settings
var (
AI = struct {
Enabled bool
DefaultModel string `ini:"DEFAULT_MODEL"`
DefaultKey string `ini:"DEFAULT_API_KEY"`
ClaudeBinPath string `ini:"CLAUDE_BIN_PATH"`
}{
Enabled: false,
DefaultModel: "claude-sonnet-4-6",
}
)
func loadAIFrom(rootCfg ConfigProvider) {
sec := rootCfg.Section("ai")
AI.Enabled = sec.Key("ENABLED").MustBool(AI.Enabled)
AI.DefaultModel = sec.Key("DEFAULT_MODEL").MustString(AI.DefaultModel)
AI.DefaultKey = sec.Key("DEFAULT_API_KEY").String()
AI.ClaudeBinPath = sec.Key("CLAUDE_BIN_PATH").MustString("claude")
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package setting
import "time"
// CDN holds configuration for the built-in CDN asset delivery system.
var CDN = struct {
Enabled bool
Domain string // e.g. "cdn.mokoconsulting.tech"
CacheTTL time.Duration // Cache-Control max-age for CDN responses
AllowedOrigins []string // CORS origins allowed to fetch CDN assets
AllowedIPs []string // IP/CIDR allowlist (empty = allow all)
AllowedDomains []string // Referrer domain allowlist (empty = allow all)
MaxFileSize int64 // max file size to serve (bytes)
}{
Enabled: false,
Domain: "",
CacheTTL: 24 * time.Hour,
MaxFileSize: 100 * 1024 * 1024, // 100MB
}
func loadCDNFrom(cfg ConfigProvider) {
sec := cfg.Section("cdn")
CDN.Enabled = sec.Key("ENABLED").MustBool(false)
CDN.Domain = sec.Key("DOMAIN").String()
CDN.CacheTTL = sec.Key("CACHE_TTL").MustDuration(CDN.CacheTTL)
CDN.MaxFileSize = sec.Key("MAX_FILE_SIZE").MustInt64(CDN.MaxFileSize)
CDN.AllowedOrigins = sec.Key("ALLOWED_ORIGINS").Strings(",")
CDN.AllowedIPs = sec.Key("ALLOWED_IPS").Strings(",")
CDN.AllowedDomains = sec.Key("ALLOWED_DOMAINS").Strings(",")
}
+1
View File
@@ -178,6 +178,7 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error {
loadOtherFrom(cfg)
loadUpdateCheckerFrom(cfg)
loadNtfyFrom(cfg)
loadCDNFrom(cfg)
loadLoginNotificationFrom(cfg)
return nil
}
+10 -1
View File
@@ -2734,12 +2734,19 @@
"repo.settings.support_url_help": "Shown when downloads are gated. Can point to your wiki, product page, or external support site.",
"repo.settings.custom_fields": "Custom Fields",
"repo.settings.manifest": "Manifest",
"repo.settings.manifest_desc": "Project identity, governance, and build settings from the moko-platform manifest. These are accessible via API for Actions workflows and the moko-platform CLI.",
"repo.settings.manifest_desc": "Project identity, governance, and build settings from the MokoPlatform manifest. These are accessible via API for Actions workflows and the MokoPlatform CLI.",
"repo.settings.manifest_identity": "Identity",
"repo.settings.manifest_name": "Project Name",
"repo.settings.manifest_element_name": "Element Name",
"repo.settings.manifest_element_name_help": "Base name used to construct the Joomla element identifier (e.g. 'mokowaas'). Combined with the extension type to produce the full element name.",
"repo.settings.manifest_element_full": "Full Element Name",
"repo.settings.manifest_element_full_help": "Auto-constructed from type + name. Leave blank to use the default, or override for non-standard naming.",
"repo.settings.manifest_element_mismatch": "Warning: this overrides the auto-constructed name '%s'. Make sure this matches your Joomla extension's element identifier.",
"repo.settings.manifest_package_type_help": "Maps to the Joomla extension type and determines the element prefix (com_, mod_, plg_, pkg_, tpl_, lib_, file_).",
"repo.settings.manifest_org": "Organization",
"repo.settings.manifest_description": "Description",
"repo.settings.manifest_version": "Version",
"repo.settings.manifest_version_prefix": "Version Prefix",
"repo.settings.manifest_license_spdx": "License (SPDX)",
"repo.settings.manifest_license_name": "License Name",
"repo.settings.manifest_governance": "Governance",
@@ -2831,6 +2838,8 @@
"repo.release.message": "Describe this release",
"repo.release.prerelease_desc": "Mark as Pre-Release",
"repo.release.prerelease_helper": "Mark this release unsuitable for production use.",
"repo.release.cdn_public": "CDN",
"repo.release.cdn_public_tooltip": "Make this asset available via the CDN. Disabled when the release is assigned to an update stream.",
"repo.release.cancel": "Cancel",
"repo.release.publish": "Publish Release",
"repo.release.save_draft": "Save Draft",
+8
View File
@@ -19,6 +19,8 @@ type apiManifest struct {
Version string `json:"version"`
LicenseSPDX string `json:"license_spdx"`
LicenseName string `json:"license_name"`
VersionPrefix string `json:"version_prefix"`
ElementName string `json:"element_name"`
Platform string `json:"platform"`
StandardsVersion string `json:"standards_version"`
StandardsSource string `json:"standards_source"`
@@ -60,6 +62,8 @@ func GetRepoManifest(ctx *context.APIContext) {
Version: m.Version,
LicenseSPDX: m.LicenseSPDX,
LicenseName: m.LicenseName,
VersionPrefix: m.VersionPrefix,
ElementName: m.FullElementName(),
Platform: m.Platform,
StandardsVersion: m.StandardsVersion,
StandardsSource: m.StandardsSource,
@@ -95,6 +99,8 @@ func UpdateRepoManifest(ctx *context.APIContext) {
Version: req.Version,
LicenseSPDX: req.LicenseSPDX,
LicenseName: req.LicenseName,
VersionPrefix: req.VersionPrefix,
ElementName: req.ElementName,
Platform: req.Platform,
StandardsVersion: req.StandardsVersion,
StandardsSource: req.StandardsSource,
@@ -115,6 +121,8 @@ func UpdateRepoManifest(ctx *context.APIContext) {
Version: m.Version,
LicenseSPDX: m.LicenseSPDX,
LicenseName: m.LicenseName,
VersionPrefix: m.VersionPrefix,
ElementName: m.FullElementName(),
Platform: m.Platform,
StandardsVersion: m.StandardsVersion,
StandardsSource: m.StandardsSource,
+278
View File
@@ -0,0 +1,278 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package repo
import (
"fmt"
"net"
"net/http"
"strings"
licenses_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/licenses"
repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/httplib"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/storage"
)
// CDNHandler serves release assets via the CDN hostname.
// URL format: /:owner/:repo/releases/:tag/:filename
// Only assets with cdn_public=true are served.
func CDNHandler(w http.ResponseWriter, req *http.Request) {
if !setting.CDN.Enabled {
http.NotFound(w, req)
return
}
if !cdnCheckIPAllowed(req) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
if !cdnCheckReferrerAllowed(req) {
http.Error(w, "Forbidden: referrer not allowed", http.StatusForbidden)
return
}
// Parse: /:owner/:repo/releases/:tag/:filename
urlPath := strings.TrimPrefix(req.URL.Path, "/")
parts := strings.SplitN(urlPath, "/", 6)
// Minimum: owner/repo/releases/tag/filename = 5 parts
if len(parts) < 5 || parts[2] != "releases" {
http.Error(w, "Not Found: expected /:owner/:repo/releases/:tag/:filename", http.StatusNotFound)
return
}
ownerName := parts[0]
repoName := parts[1]
tagName := parts[3]
fileName := parts[4]
// Allow filenames with slashes (parts[5] if present)
if len(parts) == 6 {
fileName = parts[4] + "/" + parts[5]
}
// Load repository
repo, err := repo_model.GetRepositoryByOwnerAndName(req.Context(), ownerName, repoName)
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
http.NotFound(w, req)
} else {
log.Error("CDN: GetRepositoryByOwnerAndName %s/%s: %v", ownerName, repoName, err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
return
}
// Look up the release by tag
release, err := repo_model.GetRelease(req.Context(), repo.ID, tagName)
if err != nil {
if repo_model.IsErrReleaseNotExist(err) {
http.NotFound(w, req)
} else {
log.Error("CDN: GetRelease %s/%s tag=%s: %v", ownerName, repoName, tagName, err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
return
}
// Don't serve draft releases via CDN
if release.IsDraft {
http.NotFound(w, req)
return
}
// If the release is assigned to an update stream, CDN is disabled -
// the update server handles distribution for streamed releases.
if stream := licenses_model.GetReleaseStream(req.Context(), release.ID); stream != "" {
http.Error(w, "Forbidden: release is served via update stream", http.StatusForbidden)
return
}
// Find the specific attachment by filename
attach, err := repo_model.GetAttachmentByReleaseIDFileName(req.Context(), release.ID, fileName)
if err != nil || attach == nil {
http.NotFound(w, req)
return
}
// Only serve assets marked as CDN public
if !attach.CDNPublic {
http.Error(w, "Forbidden: asset is not CDN-enabled", http.StatusForbidden)
return
}
// Check file size limit
if setting.CDN.MaxFileSize > 0 && attach.Size > setting.CDN.MaxFileSize {
http.Error(w, "File too large for CDN delivery", http.StatusRequestEntityTooLarge)
return
}
// CORS headers
if len(setting.CDN.AllowedOrigins) > 0 {
origin := req.Header.Get("Origin")
for _, allowed := range setting.CDN.AllowedOrigins {
if allowed == "*" || allowed == origin {
w.Header().Set("Access-Control-Allow-Origin", allowed)
break
}
}
} else {
w.Header().Set("Access-Control-Allow-Origin", "*")
}
w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS")
w.Header().Set("Access-Control-Max-Age", "86400")
if req.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
// ETag based on attachment UUID (immutable for same content)
etag := `"` + attach.UUID + `"`
w.Header().Set("Etag", etag)
// 304 Not Modified check
if inm := req.Header.Get("If-None-Match"); inm != "" {
for item := range strings.SplitSeq(inm, ",") {
item = strings.TrimPrefix(strings.TrimSpace(item), "W/")
if item == etag {
w.WriteHeader(http.StatusNotModified)
return
}
}
}
// Last-Modified
lastModified := attach.CreatedUnix.AsTimePtr()
if lastModified != nil {
w.Header().Set("Last-Modified", lastModified.UTC().Format(http.TimeFormat))
}
// CDN cache headers
cacheTTL := int(setting.CDN.CacheTTL.Seconds())
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d, no-transform", cacheTTL))
// Increment download count
if err := attach.IncreaseDownloadCount(req.Context()); err != nil {
log.Error("CDN: IncreaseDownloadCount: %v", err)
}
// Try direct storage URL (S3/object storage)
if setting.Attachment.Storage.ServeDirect() {
u, err := storage.Attachments.ServeDirectURL(attach.RelativePath(), attach.Name, req.Method, nil)
if u != nil && err == nil {
http.Redirect(w, req, u.String(), http.StatusTemporaryRedirect)
return
}
}
// Serve from local storage
fr, err := storage.Attachments.Open(attach.RelativePath())
if err != nil {
log.Error("CDN: storage.Open %s: %v", attach.RelativePath(), err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
defer fr.Close()
httplib.ServeUserContentByFile(req, w, fr, httplib.ServeHeaderOptions{
Filename: attach.Name,
CacheIsPublic: true,
CacheDuration: setting.CDN.CacheTTL,
})
}
// cdnCheckIPAllowed checks if the request IP is in the configured allowlist.
func cdnCheckIPAllowed(req *http.Request) bool {
if len(setting.CDN.AllowedIPs) == 0 {
return true
}
remoteIP := cdnGetRemoteIP(req)
if remoteIP == nil {
return false
}
for _, cidr := range setting.CDN.AllowedIPs {
cidr = strings.TrimSpace(cidr)
if cidr == "" {
continue
}
if !strings.Contains(cidr, "/") {
if remoteIP.Equal(net.ParseIP(cidr)) {
return true
}
continue
}
_, network, err := net.ParseCIDR(cidr)
if err != nil {
log.Warn("CDN: invalid CIDR in AllowedIPs: %s", cidr)
continue
}
if network.Contains(remoteIP) {
return true
}
}
return false
}
// cdnCheckReferrerAllowed checks if the request referrer domain is allowed.
func cdnCheckReferrerAllowed(req *http.Request) bool {
if len(setting.CDN.AllowedDomains) == 0 {
return true
}
referer := req.Header.Get("Referer")
if referer == "" {
return true // direct requests always allowed
}
for _, domain := range setting.CDN.AllowedDomains {
domain = strings.TrimSpace(strings.ToLower(domain))
if domain == "" {
continue
}
if domain == "*" {
return true
}
refLower := strings.ToLower(referer)
if strings.Contains(refLower, "://"+domain+"/") || strings.Contains(refLower, "://"+domain+":") ||
strings.HasSuffix(refLower, "://"+domain) {
return true
}
if strings.HasPrefix(domain, "*.") {
baseDomain := domain[2:]
if strings.Contains(refLower, "."+baseDomain+"/") || strings.Contains(refLower, "."+baseDomain+":") ||
strings.HasSuffix(refLower, "."+baseDomain) {
return true
}
}
}
return false
}
// cdnGetRemoteIP extracts the client IP, checking proxy headers.
func cdnGetRemoteIP(req *http.Request) net.IP {
if xff := req.Header.Get("X-Forwarded-For"); xff != "" {
parts := strings.SplitN(xff, ",", 2)
if ip := net.ParseIP(strings.TrimSpace(parts[0])); ip != nil {
return ip
}
}
if xri := req.Header.Get("X-Real-IP"); xri != "" {
if ip := net.ParseIP(strings.TrimSpace(xri)); ip != nil {
return ip
}
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
return net.ParseIP(req.RemoteAddr)
}
return net.ParseIP(host)
}
+26 -1
View File
@@ -596,7 +596,10 @@ func EditRelease(ctx *context.Context) {
ctx.Data["content"] = rel.Note
ctx.Data["prerelease"] = rel.IsPrerelease
ctx.Data["IsDraft"] = rel.IsDraft
ctx.Data["ReleaseStream"] = licenses_model.GetReleaseStream(ctx, rel.ID)
releaseStream := licenses_model.GetReleaseStream(ctx, rel.ID)
ctx.Data["ReleaseStream"] = releaseStream
ctx.Data["ReleaseHasStream"] = releaseStream != ""
ctx.Data["CDNEnabled"] = setting.CDN.Enabled
rel.Repo = ctx.Repo.Repository
if err := rel.LoadAttributes(ctx); err != nil {
@@ -683,6 +686,28 @@ func EditReleasePost(ctx *context.Context) {
} else {
_ = licenses_model.DeleteReleaseStream(ctx, rel.ID)
}
// Update per-asset CDN visibility flags.
if setting.CDN.Enabled {
const cdnPrefix = "attachment-cdn-"
cdnUUIDs := make(map[string]bool)
for k := range ctx.Req.Form {
if strings.HasPrefix(k, cdnPrefix) {
cdnUUIDs[k[len(cdnPrefix):]] = true
}
}
// Load all attachments for this release to update cdn_public
if err := repo_model.GetReleaseAttachments(ctx, rel); err == nil {
for _, attach := range rel.Attachments {
wantCDN := cdnUUIDs[attach.UUID]
if attach.CDNPublic != wantCDN {
attach.CDNPublic = wantCDN
_ = repo_model.UpdateAttachmentByUUID(ctx, attach, "cdn_public")
}
}
}
}
ctx.Redirect(ctx.Repo.RepoLink + "/releases")
}
+29 -20
View File
@@ -34,27 +34,36 @@ func LicensingSettingsPost(ctx *context.Context) {
updatePlatform = "joomla"
}
updateCfg := &licenses_model.UpdateStreamConfig{
OwnerID: repo.OwnerID,
RepoID: repo.ID,
Platform: updatePlatform,
LicensingEnabled: ctx.FormString("enable_licensing") == "on",
RequireKey: ctx.FormString("require_update_key") == "on",
DownloadGating: ctx.FormString("download_gating"),
SupportURL: ctx.FormString("support_url"),
ExtensionName: ctx.FormString("extension_name"),
DisplayName: ctx.FormString("display_name"),
ExtensionType: ctx.FormString("extension_type"),
TargetVersion: ctx.FormString("target_version"),
Maintainer: ctx.FormString("maintainer"),
PHPMinimum: ctx.FormString("php_minimum"),
StreamMode: "joomla",
}
enabled := ctx.FormString("enable_licensing") == "on"
if err := licenses_model.SaveConfig(ctx, updateCfg); err != nil {
log.Error("SaveConfig: %v", err)
ctx.ServerError("SaveConfig", err)
return
if !enabled {
// Remove repo-level override so org config takes effect
if err := licenses_model.DeleteRepoConfig(ctx, repo.ID); err != nil {
log.Error("DeleteRepoConfig: %v", err)
}
} else {
updateCfg := &licenses_model.UpdateStreamConfig{
OwnerID: repo.OwnerID,
RepoID: repo.ID,
Platform: updatePlatform,
LicensingEnabled: true,
RequireKey: ctx.FormString("require_update_key") == "on",
DownloadGating: ctx.FormString("download_gating"),
SupportURL: ctx.FormString("support_url"),
ExtensionName: ctx.FormString("extension_name"),
DisplayName: ctx.FormString("display_name"),
ExtensionType: ctx.FormString("extension_type"),
TargetVersion: ctx.FormString("target_version"),
Maintainer: ctx.FormString("maintainer"),
PHPMinimum: ctx.FormString("php_minimum"),
StreamMode: "joomla",
}
if err := licenses_model.SaveConfig(ctx, updateCfg); err != nil {
log.Error("SaveConfig: %v", err)
ctx.ServerError("SaveConfig", err)
return
}
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+11 -7
View File
@@ -18,18 +18,19 @@ const tplSettingsManifest templates.TplName = "repo/settings/manifest"
// manifestXML mirrors the .mokogitea/manifest.xml schema for XML parsing.
type manifestXML struct {
XMLName xml.Name `xml:"moko-platform"`
XMLName xml.Name `xml:"mokoplatform"`
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"`
Name string `xml:"name"`
Org string `xml:"org"`
Description string `xml:"description"`
Version string `xml:"version"`
VersionPrefix string `xml:"version-prefix"`
License manifestLicense `xml:"license"`
}
type manifestLicense struct {
@@ -88,10 +89,12 @@ func ManifestSettingsPost(ctx *context.Context) {
RepoID: ctx.Repo.Repository.ID,
Name: ctx.FormString("name"),
Org: ctx.FormString("org"),
Description: ctx.FormString("description"),
Description: ctx.Repo.Repository.Description,
Version: ctx.FormString("version"),
LicenseSPDX: ctx.FormString("license_spdx"),
LicenseName: ctx.FormString("license_name"),
VersionPrefix: ctx.FormString("version_prefix"),
ElementName: ctx.FormString("element_name"),
Platform: ctx.FormString("platform"),
StandardsVersion: ctx.FormString("standards_version"),
StandardsSource: ctx.FormString("standards_source"),
@@ -142,6 +145,7 @@ func tryMigrateManifestXML(ctx *context.Context) *repo_model.RepoManifest {
Version: mxml.Identity.Version,
LicenseSPDX: mxml.Identity.License.SPDX,
LicenseName: mxml.Identity.License.Name,
VersionPrefix: mxml.Identity.VersionPrefix,
Platform: mxml.Governance.Platform,
StandardsVersion: mxml.Governance.StandardsVersion,
StandardsSource: mxml.Governance.StandardsSource,
+31 -23
View File
@@ -675,29 +675,37 @@ func handleSettingsPostAdvanced(ctx *context.Context) {
return
}
}
// Save update server platform and require-key settings.
updatePlatform := form.UpdatePlatform
if updatePlatform == "" {
updatePlatform = "joomla"
}
updateCfg := &licenses_model.UpdateStreamConfig{
OwnerID: repo.OwnerID,
RepoID: repo.ID,
Platform: updatePlatform,
LicensingEnabled: form.EnableLicensing,
RequireKey: form.RequireUpdateKey,
DownloadGating: form.DownloadGating,
SupportURL: form.SupportURL,
ExtensionName: form.ExtensionName,
DisplayName: form.DisplayName,
ExtensionType: form.ExtensionType,
TargetVersion: form.TargetVersion,
Maintainer: form.Maintainer,
PHPMinimum: form.PHPMinimum,
StreamMode: "joomla", // inherit org default
}
if err := licenses_model.SaveConfig(ctx, updateCfg); err != nil {
log.Error("SaveConfig: %v", err)
// Save update server settings. If disabled, delete repo-level config
// so it falls through to org defaults cleanly.
if !form.EnableLicensing {
// Remove repo-level override so org config takes effect
if err := licenses_model.DeleteRepoConfig(ctx, repo.ID); err != nil {
log.Error("DeleteRepoConfig: %v", err)
}
} else {
updatePlatform := form.UpdatePlatform
if updatePlatform == "" {
updatePlatform = "joomla"
}
updateCfg := &licenses_model.UpdateStreamConfig{
OwnerID: repo.OwnerID,
RepoID: repo.ID,
Platform: updatePlatform,
LicensingEnabled: form.EnableLicensing,
RequireKey: form.RequireUpdateKey,
DownloadGating: form.DownloadGating,
SupportURL: form.SupportURL,
ExtensionName: form.ExtensionName,
DisplayName: form.DisplayName,
ExtensionType: form.ExtensionType,
TargetVersion: form.TargetVersion,
Maintainer: form.Maintainer,
PHPMinimum: form.PHPMinimum,
StreamMode: "joomla",
}
if err := licenses_model.SaveConfig(ctx, updateCfg); err != nil {
log.Error("SaveConfig: %v", err)
}
}
log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+14
View File
@@ -260,6 +260,20 @@ func Routes() *web.Router {
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
routes.BeforeRouting(chi_middleware.GetHead)
// CDN hostname handler - intercepts requests on the CDN domain before any
// session/auth middleware runs, serving only CDN-public release assets.
if setting.CDN.Enabled && setting.CDN.Domain != "" {
routes.BeforeRouting(func(resp http.ResponseWriter, req *http.Request) {
host := req.Host
if idx := strings.Index(host, ":"); idx >= 0 {
host = host[:idx]
}
if strings.EqualFold(host, setting.CDN.Domain) {
repo.CDNHandler(resp, req)
}
})
}
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
routes.Methods("GET, HEAD", "/assets/site-manifest.json", misc.SiteManifest)
routes.Methods("GET, HEAD, OPTIONS", "/assets/*", routing.MarkLogLevelTrace, public.AssetsCors(), public.FileHandlerFunc())
+10 -6
View File
@@ -14,18 +14,20 @@ import (
// manifestXML mirrors the .mokogitea/manifest.xml schema for XML parsing.
type manifestXML struct {
XMLName xml.Name `xml:"moko-platform"`
XMLName xml.Name `xml:"mokoplatform"`
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"`
Name string `xml:"name"`
Org string `xml:"org"`
Description string `xml:"description"`
Version string `xml:"version"`
VersionPrefix string `xml:"version-prefix"`
ElementName string `xml:"element-name"`
License manifestLicense `xml:"license"`
}
type manifestLicense struct {
@@ -79,6 +81,8 @@ func SyncManifestFromCommit(ctx context.Context, repo *repo_model.Repository, co
Org: mxml.Identity.Org,
Description: mxml.Identity.Description,
Version: mxml.Identity.Version,
VersionPrefix: mxml.Identity.VersionPrefix,
ElementName: mxml.Identity.ElementName,
LicenseSPDX: mxml.Identity.License.SPDX,
LicenseName: mxml.Identity.License.Name,
Platform: mxml.Governance.Platform,
+541
View File
@@ -0,0 +1,541 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package security
import (
"bytes"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
security_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/security"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/git"
"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
)
// ──────────────────────────────────────────────────────────────────────
// Dependency manifest parsers
// ──────────────────────────────────────────────────────────────────────
// dependency represents a single package with version.
type dependency struct {
Name string
Version string
Ecosystem string // "Go", "npm", "PyPI", "Packagist"
FilePath string // which manifest file it came from
}
// manifestParser extracts dependencies from a file's contents.
type manifestParser struct {
FileName string
Ecosystem string
Parse func(content string, filePath string) []dependency
}
var manifestParsers = []manifestParser{
{"go.mod", "Go", parseGoMod},
{"package.json", "npm", parsePackageJSON},
{"composer.json", "Packagist", parseComposerJSON},
{"requirements.txt", "PyPI", parseRequirementsTxt},
}
// parseGoMod extracts dependencies from go.mod.
func parseGoMod(content, filePath string) []dependency {
var deps []dependency
inRequire := false
for _, line := range strings.Split(content, "\n") {
line = strings.TrimSpace(line)
if line == ")" {
inRequire = false
continue
}
if strings.HasPrefix(line, "require (") || strings.HasPrefix(line, "require(") {
inRequire = true
continue
}
if inRequire {
// Lines like: github.com/foo/bar v1.2.3
parts := strings.Fields(line)
if len(parts) >= 2 && !strings.HasPrefix(parts[0], "//") {
deps = append(deps, dependency{
Name: parts[0],
Version: parts[1],
Ecosystem: "Go",
FilePath: filePath,
})
}
continue
}
// Single-line require: require github.com/foo/bar v1.2.3
if strings.HasPrefix(line, "require ") && !strings.Contains(line, "(") {
parts := strings.Fields(line)
if len(parts) >= 3 {
deps = append(deps, dependency{
Name: parts[1],
Version: parts[2],
Ecosystem: "Go",
FilePath: filePath,
})
}
}
}
return deps
}
// parsePackageJSON extracts dependencies from package.json.
func parsePackageJSON(content, filePath string) []dependency {
var pkg struct {
Dependencies map[string]string `json:"dependencies"`
DevDependencies map[string]string `json:"devDependencies"`
}
if err := json.Unmarshal([]byte(content), &pkg); err != nil {
return nil
}
var deps []dependency
for name, version := range pkg.Dependencies {
deps = append(deps, dependency{
Name: name,
Version: cleanSemver(version),
Ecosystem: "npm",
FilePath: filePath,
})
}
for name, version := range pkg.DevDependencies {
deps = append(deps, dependency{
Name: name,
Version: cleanSemver(version),
Ecosystem: "npm",
FilePath: filePath,
})
}
return deps
}
// parseComposerJSON extracts dependencies from composer.json.
func parseComposerJSON(content, filePath string) []dependency {
var pkg struct {
Require map[string]string `json:"require"`
RequireDev map[string]string `json:"require-dev"`
}
if err := json.Unmarshal([]byte(content), &pkg); err != nil {
return nil
}
var deps []dependency
for name, version := range pkg.Require {
if name == "php" || strings.HasPrefix(name, "ext-") {
continue // skip platform requirements
}
deps = append(deps, dependency{
Name: name,
Version: cleanSemver(version),
Ecosystem: "Packagist",
FilePath: filePath,
})
}
for name, version := range pkg.RequireDev {
if name == "php" || strings.HasPrefix(name, "ext-") {
continue
}
deps = append(deps, dependency{
Name: name,
Version: cleanSemver(version),
Ecosystem: "Packagist",
FilePath: filePath,
})
}
return deps
}
// parseRequirementsTxt extracts dependencies from requirements.txt.
func parseRequirementsTxt(content, filePath string) []dependency {
var deps []dependency
for _, line := range strings.Split(content, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "-") {
continue
}
// Handle: package==1.0.0, package>=1.0.0, package~=1.0.0
for _, sep := range []string{"==", ">=", "~=", "<=", "!="} {
if idx := strings.Index(line, sep); idx > 0 {
name := strings.TrimSpace(line[:idx])
version := strings.TrimSpace(line[idx+len(sep):])
// Strip any trailing constraints like ",<2.0"
if ci := strings.Index(version, ","); ci > 0 {
version = version[:ci]
}
deps = append(deps, dependency{
Name: name,
Version: version,
Ecosystem: "PyPI",
FilePath: filePath,
})
break
}
}
}
return deps
}
// cleanSemver strips npm/composer range prefixes (^, ~, >=) to get a plain version.
func cleanSemver(v string) string {
v = strings.TrimSpace(v)
v = strings.TrimLeft(v, "^~>=<!")
v = strings.TrimSpace(v)
// If it has " || " or " - " (ranges), take the first version
if idx := strings.Index(v, " "); idx > 0 {
v = v[:idx]
}
return v
}
// ──────────────────────────────────────────────────────────────────────
// OSV.dev API client
// ──────────────────────────────────────────────────────────────────────
const osvBatchURL = "https://api.osv.dev/v1/querybatch"
const osvMaxBatch = 1000 // OSV batch limit
var osvClient = &http.Client{Timeout: 30 * time.Second}
// osvQuery is a single query in a batch request.
type osvQuery struct {
Package *osvPackage `json:"package"`
Version string `json:"version"`
}
type osvPackage struct {
Name string `json:"name"`
Ecosystem string `json:"ecosystem"`
}
// osvBatchRequest is the batch query body.
type osvBatchRequest struct {
Queries []osvQuery `json:"queries"`
}
// osvBatchResponse is the batch response.
type osvBatchResponse struct {
Results []osvResult `json:"results"`
}
type osvResult struct {
Vulns []osvVuln `json:"vulns"`
}
type osvVuln struct {
ID string `json:"id"`
Summary string `json:"summary"`
Details string `json:"details"`
Severity []osvSeverity `json:"severity"`
Aliases []string `json:"aliases"`
}
type osvSeverity struct {
Type string `json:"type"` // "CVSS_V3", "CVSS_V2"
Score string `json:"score"` // CVSS vector string
}
// queryOSV sends a batch of dependencies to OSV.dev and returns vulnerabilities.
func queryOSV(deps []dependency) (*osvBatchResponse, error) {
queries := make([]osvQuery, 0, len(deps))
for _, d := range deps {
if d.Version == "" || d.Version == "*" || d.Version == "latest" {
continue // can't query without a concrete version
}
queries = append(queries, osvQuery{
Package: &osvPackage{Name: d.Name, Ecosystem: d.Ecosystem},
Version: d.Version,
})
}
if len(queries) == 0 {
return &osvBatchResponse{}, nil
}
body, err := json.Marshal(osvBatchRequest{Queries: queries})
if err != nil {
return nil, fmt.Errorf("marshal OSV request: %w", err)
}
resp, err := osvClient.Post(osvBatchURL, "application/json", bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("OSV API request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
return nil, fmt.Errorf("OSV API returned %d: %s", resp.StatusCode, string(respBody))
}
var result osvBatchResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decode OSV response: %w", err)
}
return &result, nil
}
// ──────────────────────────────────────────────────────────────────────
// Severity mapping
// ──────────────────────────────────────────────────────────────────────
// mapCVSSSeverity converts a CVSS v3 base score to an AlertSeverity.
func mapCVSSSeverity(vulnSeverities []osvSeverity) security_model.AlertSeverity {
for _, s := range vulnSeverities {
if s.Type == "CVSS_V3" {
score := extractCVSSBaseScore(s.Score)
switch {
case score >= 9.0:
return security_model.SeverityCritical
case score >= 7.0:
return security_model.SeverityHigh
case score >= 4.0:
return security_model.SeverityMedium
case score > 0:
return security_model.SeverityLow
}
}
}
// No CVSS score available - default to medium
return security_model.SeverityMedium
}
// extractCVSSBaseScore parses the base score from a CVSS v3 vector string.
// Vector format: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
// We compute a simplified score from the vector metrics.
func extractCVSSBaseScore(vector string) float64 {
if vector == "" {
return 0
}
// CVSS v3 vectors encode severity in metrics. Use a simplified
// lookup based on the most impactful metrics.
parts := make(map[string]string)
for _, segment := range strings.Split(vector, "/") {
kv := strings.SplitN(segment, ":", 2)
if len(kv) == 2 {
parts[kv[0]] = kv[1]
}
}
// Simplified scoring based on key CVSS v3 metrics
var score float64
// Attack Vector (AV)
switch parts["AV"] {
case "N": // Network
score += 3.0
case "A": // Adjacent
score += 2.0
case "L": // Local
score += 1.0
case "P": // Physical
score += 0.5
}
// Attack Complexity (AC)
switch parts["AC"] {
case "L": // Low
score += 1.5
case "H": // High
score += 0.5
}
// Privileges Required (PR)
switch parts["PR"] {
case "N": // None
score += 1.5
case "L": // Low
score += 1.0
case "H": // High
score += 0.5
}
// Impact metrics (C/I/A)
for _, metric := range []string{"C", "I", "A"} {
switch parts[metric] {
case "H":
score += 1.2
case "L":
score += 0.5
}
}
// Cap at 10.0
if score > 10.0 {
score = 10.0
}
return score
}
// ──────────────────────────────────────────────────────────────────────
// DependencyScanner
// ──────────────────────────────────────────────────────────────────────
// DependencyScanner checks project dependencies against known vulnerabilities.
type DependencyScanner struct{}
// NewDependencyScanner creates a new dependency vulnerability scanner.
func NewDependencyScanner() *DependencyScanner {
return &DependencyScanner{}
}
func (s *DependencyScanner) Type() security_model.ScannerType {
return security_model.ScannerDependency
}
func (s *DependencyScanner) ScanCommit(commit *git.Commit) ([]Finding, error) {
return s.ScanTree(commit)
}
func (s *DependencyScanner) ScanTree(commit *git.Commit) ([]Finding, error) {
if commit == nil {
return nil, nil
}
// Step 1: Find and parse manifest files
entries, err := commit.ListEntriesRecursiveFast()
if err != nil {
return nil, fmt.Errorf("ListEntriesRecursiveFast: %w", err)
}
var allDeps []dependency
for _, entry := range entries {
if !entry.IsRegular() {
continue
}
path := entry.Name()
baseName := path
if idx := strings.LastIndex(path, "/"); idx >= 0 {
baseName = path[idx+1:]
}
// Skip vendored/nested files
lower := strings.ToLower(path)
if strings.Contains(lower, "vendor/") || strings.Contains(lower, "node_modules/") ||
strings.Contains(lower, "testdata/") {
continue
}
for _, parser := range manifestParsers {
if baseName == parser.FileName {
reader, err := entry.Blob().DataAsync()
if err != nil {
log.Trace("DependencyScanner: skip %s: %v", path, err)
continue
}
content, err := io.ReadAll(io.LimitReader(reader, 5*1024*1024)) // 5MB limit
reader.Close()
if err != nil {
continue
}
deps := parser.Parse(string(content), path)
allDeps = append(allDeps, deps...)
break
}
}
}
if len(allDeps) == 0 {
return nil, nil
}
log.Info("DependencyScanner: found %d dependencies across manifest files", len(allDeps))
// Step 2: Query OSV in batches
var findings []Finding
for i := 0; i < len(allDeps); i += osvMaxBatch {
end := i + osvMaxBatch
if end > len(allDeps) {
end = len(allDeps)
}
batch := allDeps[i:end]
resp, err := queryOSV(batch)
if err != nil {
log.Error("DependencyScanner: OSV query failed: %v", err)
continue
}
// Step 3: Map results to findings
// OSV batch response indices correspond 1:1 with the query indices.
// But we may have skipped deps with empty versions, so build the
// queryable subset to align indices.
queryable := make([]dependency, 0, len(batch))
for _, d := range batch {
if d.Version != "" && d.Version != "*" && d.Version != "latest" {
queryable = append(queryable, d)
}
}
for j, result := range resp.Results {
if j >= len(queryable) {
break
}
dep := queryable[j]
for _, vuln := range result.Vulns {
severity := mapCVSSSeverity(vuln.Severity)
// Build CVE alias for rule ID (prefer CVE over GHSA)
ruleID := vuln.ID
for _, alias := range vuln.Aliases {
if strings.HasPrefix(alias, "CVE-") {
ruleID = alias
break
}
}
title := fmt.Sprintf("%s in %s@%s", ruleID, dep.Name, dep.Version)
description := vuln.Summary
if description == "" {
description = vuln.Details
}
// Truncate long descriptions
if len(description) > 500 {
description = description[:497] + "..."
}
// Metadata JSON
meta, _ := json.Marshal(map[string]string{
"vuln_id": vuln.ID,
"ecosystem": dep.Ecosystem,
"package": dep.Name,
"version": dep.Version,
})
fingerprint := fmt.Sprintf("%x", sha256.Sum256([]byte(vuln.ID+":"+dep.Name+":"+dep.Version)))
findings = append(findings, Finding{
Scanner: security_model.ScannerDependency,
Severity: severity,
RuleID: ruleID,
Title: title,
Description: description,
FilePath: dep.FilePath,
CommitSHA: commit.ID.String(),
Fingerprint: fingerprint[:32],
Metadata: string(meta),
})
}
}
}
return findings, nil
}
+3 -1
View File
@@ -32,8 +32,10 @@ func ScanOnPush(ctx context.Context, repo *repo_model.Repository, commit *git.Co
if cfg.SecretScanner {
scanners = append(scanners, NewSecretScanner())
}
if cfg.DependScanner {
scanners = append(scanners, NewDependencyScanner())
}
// Future scanners added here:
// if cfg.DependScanner { scanners = append(scanners, NewDependencyScanner()) }
// if cfg.CodeScanner { scanners = append(scanners, NewCodeScanner()) }
if len(scanners) == 0 {
@@ -19,6 +19,7 @@
<p class="help">{{ctx.Locale.Tr "org.settings.enable_licensing_help"}}</p>
</div>
{{if and (ne .StreamConfig.Platform "joomla") (ne .StreamConfig.Platform "both") (ne .StreamConfig.Platform "")}}
<div class="inline field">
<div class="ui checkbox">
<input name="require_key" type="checkbox" {{if .StreamConfig.RequireKey}}checked{{end}}>
@@ -26,6 +27,7 @@
</div>
<p class="help">{{ctx.Locale.Tr "org.settings.require_key_help"}}</p>
</div>
{{end}}
<div class="field">
<label>{{ctx.Locale.Tr "org.settings.feed_visibility"}}</label>
+6
View File
@@ -86,6 +86,12 @@
<span data-tooltip-content="{{ctx.Locale.Tr "repo.release.download_count" (ctx.Locale.PrettyNumber .DownloadCount)}}">
{{svg "octicon-info"}}
</span>
{{if $.CDNEnabled}}
<label class="tw-flex tw-items-center tw-gap-1 tw-ml-2" data-tooltip-content="{{ctx.Locale.Tr "repo.release.cdn_public_tooltip"}}">
<input type="checkbox" name="attachment-cdn-{{.UUID}}" {{if .CDNPublic}}checked{{end}} {{if $.ReleaseHasStream}}disabled{{end}}>
<span class="tw-text-text-light tw-text-xs">{{ctx.Locale.Tr "repo.release.cdn_public"}}</span>
</label>
{{end}}
</div>
<a class="ui mini compact red button" data-global-click="onReleaseEditAttachmentDelete" data-id="{{.ID}}" data-uuid="{{.UUID}}">
{{ctx.Locale.Tr "remove"}}
+3 -1
View File
@@ -31,13 +31,15 @@
<p class="help">{{ctx.Locale.Tr "repo.settings.update_platform_help"}}</p>
</div>
{{if and .RepoUpdateConfig (ne .RepoUpdateConfig.Platform "joomla") (ne .RepoUpdateConfig.Platform "both") (ne .RepoUpdateConfig.Platform "")}}
<div class="inline field">
<div class="ui checkbox">
<input name="require_update_key" type="checkbox" {{if and .RepoUpdateConfig .RepoUpdateConfig.RequireKey}}checked{{end}}>
<input name="require_update_key" type="checkbox" {{if .RepoUpdateConfig.RequireKey}}checked{{end}}>
<label>{{ctx.Locale.Tr "repo.settings.require_update_key"}}</label>
</div>
<p class="help">{{ctx.Locale.Tr "repo.settings.require_update_key_help"}}</p>
</div>
{{end}}
<div class="inline field">
<label>{{ctx.Locale.Tr "repo.settings.download_gating"}}</label>
+45 -10
View File
@@ -11,23 +11,29 @@
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.manifest_identity"}}</h5>
<div class="two fields">
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_name"}}</label>
<input name="name" value="{{.Manifest.Name}}" placeholder="Project name">
{{if eq .Manifest.Platform "joomla"}}
<label>{{ctx.Locale.Tr "repo.settings.manifest_element_name"}}</label>
<input name="name" value="{{.Manifest.Name}}" placeholder="e.g. mokowaas">
<p class="help">{{ctx.Locale.Tr "repo.settings.manifest_element_name_help"}}</p>
{{else}}
<label>{{ctx.Locale.Tr "repo.settings.manifest_name"}}</label>
<input name="name" value="{{.Manifest.Name}}" placeholder="Project name">
{{end}}
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_org"}}</label>
<input name="org" value="{{.Manifest.Org}}" placeholder="Organization">
</div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_description"}}</label>
<input name="description" value="{{.Manifest.Description}}" placeholder="Project description">
</div>
<div class="three fields">
<div class="four fields">
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_version"}}</label>
<input name="version" value="{{.Manifest.Version}}" placeholder="e.g. 06.00.00">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_version_prefix"}}</label>
<input name="version_prefix" value="{{.Manifest.VersionPrefix}}" placeholder="e.g. v1.26.1-moko.">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_license_spdx"}}</label>
<input name="license_spdx" value="{{.Manifest.LicenseSPDX}}" placeholder="e.g. GPL-3.0-or-later">
@@ -45,7 +51,7 @@
<select name="platform" class="ui dropdown">
<option value="">—</option>
{{$platform := .Manifest.Platform}}
{{range $val := StringUtils.Split "go,php,node,python,ruby,java,dotnet,rust" ","}}
{{range $val := StringUtils.Split "joomla,wordpress,dolibarr,go,mcp,platform,generic" ","}}
<option value="{{$val}}" {{if eq $val $platform}}selected{{end}}>{{$val}}</option>
{{end}}
</select>
@@ -64,18 +70,47 @@
<div class="three fields">
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_language"}}</label>
<input name="language" value="{{.Manifest.Language}}" placeholder="e.g. Go, PHP, TypeScript">
<select name="language" class="ui dropdown">
<option value="">—</option>
{{$lang := .Manifest.Language}}
{{range $val := StringUtils.Split "Go,PHP,TypeScript,JavaScript,Python,Ruby,Java,C#,Rust,Shell,SQL,CSS,HTML" ","}}
<option value="{{$val}}" {{if eq $val $lang}}selected{{end}}>{{$val}}</option>
{{end}}
</select>
</div>
{{if eq .Manifest.Platform "joomla"}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_package_type"}}</label>
<select name="package_type" class="ui dropdown">
<option value="">—</option>
{{$pkgType := .Manifest.PackageType}}
{{range $val := StringUtils.Split "application,library,plugin,module,component,package,template" ","}}
{{range $val := StringUtils.Split "component,module,plugin,package,template,library,file" ","}}
<option value="{{$val}}" {{if eq $val $pkgType}}selected{{end}}>{{$val}}</option>
{{end}}
</select>
<p class="help">{{ctx.Locale.Tr "repo.settings.manifest_package_type_help"}}</p>
</div>
{{end}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_element_full"}}</label>
<input name="element_name" value="{{.Manifest.ElementName}}" placeholder="{{.Manifest.AutoElementName}}">
{{if .Manifest.ElementNameMismatch}}
<p class="help tw-text-yellow-600">{{ctx.Locale.Tr "repo.settings.manifest_element_mismatch" .Manifest.AutoElementName}}</p>
{{else}}
<p class="help">{{ctx.Locale.Tr "repo.settings.manifest_element_full_help"}}</p>
{{end}}
</div>
{{end}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_element_full"}}</label>
<input name="element_name" value="{{.Manifest.ElementName}}" placeholder="{{.Manifest.AutoElementName}}">
{{if .Manifest.ElementNameMismatch}}
<p class="help tw-text-yellow-600">{{ctx.Locale.Tr "repo.settings.manifest_element_mismatch" .Manifest.AutoElementName}}</p>
{{else}}
<p class="help">{{ctx.Locale.Tr "repo.settings.manifest_element_full_help"}}</p>
{{end}}
</div>
{{end}}
<div class="field">
<label>{{ctx.Locale.Tr "repo.settings.manifest_entry_point"}}</label>
<input name="entry_point" value="{{.Manifest.EntryPoint}}" placeholder="e.g. ./ or src/index.ts">
+1 -1
View File
@@ -7,7 +7,7 @@ Moko Consulting's custom fork of [Gitea](https://gitea.com), extending the self-
| **Language** | Go |
| **License** | MIT |
| **Upstream** | Gitea 1.26.1 |
| **Version** | v1.26.1-moko.06.10.00 |
| **Version** | v1.26.1-moko.06.11.01 |
| **Platform** | [Gitea](https://git.mokoconsulting.tech/MokoConsulting/MokoGitea) |
---
+8 -1
View File
@@ -1,6 +1,6 @@
# MokoGitea Roadmap
## Recently Completed (v1.26.1-moko.06.10)
## Recently Completed (v1.26.1-moko.06.11)
- First-class Type field (12 types) replacing labels and custom fields
- First-class Status field (13 statuses) with auto close/reopen
@@ -15,11 +15,18 @@
- MCP server published to npm (@mokoconsulting/mokogitea-mcp) with SSE transport
- Dashboard issue count badges fixed
- Status dropdown replaces close/reopen button
- Org settings page for Issue Types
- MCP SSE endpoint hosted at git.mokoconsulting.tech/mcp/
- npm auto-publish workflow on MCP source changes
- OAuth providers on 403/404 error pages
- All stale branches cleaned up (main + dev only)
## In Progress
- Rename moko-platform to MokoPlatform
- Granular role-based permissions for all features (#9)
- Wire moko-platform CLI to manifest API (#505)
- Bulk migrate remaining 41 flat wikis to folders
## Planned