Compare commits

...

14 Commits

Author SHA1 Message Date
jmiller 0b84da03ec chore: sync updates.xml 05.14.00 from main [skip ci] 2026-05-31 03:49:03 +00:00
jmiller 295493d1df chore: sync updates.xml 05.13.00 from main [skip ci] 2026-05-31 03:38:22 +00:00
jmiller ce7e5166ab chore: sync updates.xml 05.12.00 from main [skip ci] 2026-05-31 03:35:22 +00:00
jmiller 31d178c903 chore: sync updates.xml 05.11.00 from main [skip ci] 2026-05-31 03:19:01 +00:00
jmiller 0b43a24c75 chore: sync updates.xml 05.10.00 from main [skip ci] 2026-05-31 03:08:42 +00:00
jmiller 54ca50e1a6 chore: sync updates.xml 05.09.00 from main [skip ci] 2026-05-31 02:49:20 +00:00
jmiller 87cffa903b chore: sync updates.xml 05.05.00 from main [skip ci] 2026-05-30 18:03:04 +00:00
jmiller 72ec749355 chore: sync updates.xml 05.04.00 from main [skip ci] 2026-05-30 17:35:08 +00:00
Jonathan Miller 7884e2f141 feat(permissions): section-based visibility — public units on private repos
Universal: PR Check / Build RC Package (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 2s
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m7s
Fix three gaps that prevented per-unit public access from working on
private repositories:

1. Git HTTP handler (githttp.go): allow anonymous git pull for private
   repos when the target unit (code or wiki) has AnonymousAccessMode
   set to read. Previously only checked repo.IsPrivate.

2. Permission engine (repo_permission.go): call
   finalProcessRepoUnitPermission for anonymous users on private repos
   so that unit-level anonymous access modes are populated. Previously
   returned early with AccessModeNone, skipping anonymous mode setup.

3. Search/explore (repo_list.go): include private repos that have at
   least one unit with anonymous_access_mode > 0 in search results,
   so anonymous users can discover repos with public sections.

The existing settings UI at /settings/public_access already allows
configuring per-unit visibility. The home page redirect to the first
readable unit (e.g. wiki) also already works via checkHomeCodeViewable.

Closes #238

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 12:26:24 -05:00
jmiller 4b6df79ae0 Merge pull request 'feat(admin): configurable default landing page from site administration' (#241) from feat/admin-landing-page into dev
Universal: Auto Version Bump / Version Bump (push) Has been skipped
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Branch Policy Check / Verify merge target (pull_request) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Branch Cleanup / Delete merged branch (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Build & Release / Promote to RC (pull_request) Failing after 12s
PR RC Release / Build RC Release (pull_request) Successful in 29s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 8m23s
feat(admin): configurable default landing page (#240) (#241)
2026-05-30 17:18:16 +00:00
Jonathan Miller 1d1482a3dc feat(admin): configurable default landing page from site administration
Universal: PR Check / Build RC Package (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
PR RC Release / Build RC Release (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request) Successful in 1m6s
Add a dynamic config option to set the default landing page for
unauthenticated visitors from Site Administration > Settings. Options:
- Home (default explore page)
- Explore (repository explore)
- Organizations (org explore)
- Login (redirect to login)
- Custom path (any internal URL like /MokoConsulting)

The setting takes effect immediately without restart, using the same
dynamic config system as maintenance mode and web banner. Falls back
to the static LANDING_PAGE setting from app.ini if not configured.

Closes #240

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 12:17:40 -05:00
jmiller fba9c7eed9 chore: sync updates.xml 05.02.00 from main [skip ci] 2026-05-30 16:12:32 +00:00
jmiller 086c506a10 Merge pull request 'docs: update CHANGELOG for actions bot rebrand and deploy trigger' (#236) from chore/changelog-actions-bot into dev
Universal: Auto Version Bump / Version Bump (push) Has been skipped
Universal: PR Check / Build RC Package (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
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) Failing after 13s
PR RC Release / Build RC Release (pull_request) Successful in 33s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 8m28s
docs: update CHANGELOG for actions bot rebrand and deploy trigger (#236)
2026-05-30 16:07:31 +00:00
jmiller d3bca854e5 chore: add .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] 2026-05-30 16:03:30 +00:00
11 changed files with 423 additions and 62 deletions
+236
View File
@@ -0,0 +1,236 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.CI
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/universal/pr-check.yml.template
# VERSION: 05.00.00
# BRIEF: PR gate — branch policy + code validation before merge
name: "Universal: PR Check"
on:
pull_request:
types: [opened, synchronize, reopened, edited]
permissions:
contents: read
pull-requests: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# ── Branch Policy ──────────────────────────────────────────────────────
branch-policy:
name: Branch Policy
runs-on: ubuntu-latest
steps:
- name: Check branch merge target
run: |
HEAD="${{ github.head_ref }}"
BASE="${{ github.base_ref }}"
echo "PR: ${HEAD} → ${BASE}"
ALLOWED=true
REASON=""
case "$HEAD" in
feature/*|feat/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Feature branches must target 'dev', not '${BASE}'"
fi
;;
fix/*|bugfix/*)
if [ "$BASE" != "dev" ]; then
ALLOWED=false
REASON="Fix branches must target 'dev', not '${BASE}'"
fi
;;
patch/*)
if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then
ALLOWED=false
REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'"
fi
;;
hotfix/*)
if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'"
fi
;;
rc)
if [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="RC branch can only merge into 'main', not '${BASE}'"
fi
;;
dev)
if [ "$BASE" != "main" ]; then
ALLOWED=false
REASON="Dev branch can only merge into 'main', not '${BASE}'"
fi
;;
esac
if [ "$ALLOWED" = false ]; then
echo "::error::${REASON}"
echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "Branch policy: OK (${HEAD} → ${BASE})"
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
# ── Code Validation ────────────────────────────────────────────────────
validate:
name: Validate PR
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect platform
id: platform
run: |
# Read platform from XML manifest (<platform> tag) or plain text fallback
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1)
[ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]')
[ -z "$PLATFORM" ] && PLATFORM="generic"
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
- name: Setup PHP
if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
- name: PHP syntax check
if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -print0)
echo "PHP lint: ${ERRORS} error(s)"
[ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; }
- name: Validate platform manifest
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
case "$PLATFORM" in
joomla)
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -z "$MANIFEST" ]; then
echo "::warning::No Joomla manifest found (WaaS site)"
exit 0
fi
echo "Manifest: ${MANIFEST}"
if command -v php &> /dev/null; then
php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$MANIFEST'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::Manifest XML is malformed"; exit 1; }
fi
for ELEMENT in name version description; do
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
done
echo "Joomla manifest valid"
;;
dolibarr)
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
if [ -z "$MOD_FILE" ]; then
echo "::error::No mod*.class.php found"
exit 1
fi
echo "Dolibarr module: ${MOD_FILE}"
;;
*)
echo "Generic platform — no manifest validation"
;;
esac
- name: Check update stream format
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
case "$PLATFORM" in
joomla)
if [ -f "updates.xml" ]; then
if command -v php &> /dev/null; then
php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('updates.xml'); if(!\$x){foreach(libxml_get_errors() as \$e) echo \$e->message; exit(1);}" || { echo "::error::updates.xml is malformed"; exit 1; }
fi
echo "updates.xml valid"
fi
;;
dolibarr)
[ -f "update.txt" ] && echo "update.txt present" || echo "::warning::No update.txt"
;;
esac
- name: Check changelog has unreleased entry
run: |
if [ ! -f "CHANGELOG.md" ]; then
echo "::warning::No CHANGELOG.md found"
exit 0
fi
# Check for content under [Unreleased] section
if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then
echo "::error::CHANGELOG.md missing [Unreleased] section"
exit 1
fi
# Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased
UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true)
if [ "$UNRELEASED_CONTENT" -eq 0 ]; then
echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes."
echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY
echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY
echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]"
- name: Verify package source
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ ! -d "$SOURCE_DIR" ]; then
echo "::warning::No src/ or htdocs/ directory"
exit 0
fi
FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l)
echo "Source: ${FILE_COUNT} files"
[ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; }
# ── Pre-Release RC Build ─────────────────────────────────────────────────
pre-release:
name: Build RC Package
runs-on: ubuntu-latest
needs: [branch-policy, validate]
steps:
- name: Trigger RC pre-release
env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: ${{ github.head_ref }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
+3
View File
@@ -405,8 +405,11 @@ func GetIndividualUserRepoPermission(ctx context.Context, repo *repo_model.Repos
perm.units = repo.Units
// anonymous user visit private repo.
// Still process unit-level anonymous access so that units with
// AnonymousAccessMode (e.g. public wiki on a private repo) are visible.
if user == nil && repo.IsPrivate {
perm.AccessMode = perm_model.AccessModeNone
finalProcessRepoUnitPermission(user, &perm)
return perm, nil
}
+8
View File
@@ -673,6 +673,14 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu
cond = userAllPublicRepoCond(cond, orgVisibilityLimit)
}
// Include private repos that have at least one unit with public anonymous access.
// This enables discovery of repos where e.g. wiki or releases are public.
cond = cond.Or(builder.In("`repository`.id",
builder.Select("repo_id").From("repo_unit").Where(
builder.Gt{"anonymous_access_mode": 0},
),
))
if user != nil {
// 2. Be able to see all repositories that we have unit independent access to
// 3. Be able to see all repositories through team membership(s)
+1
View File
@@ -81,6 +81,7 @@ func initDefaultConfig() {
Instance: &InstanceStruct{
WebBanner: config.NewOption[WebBannerType]("instance.web_banner"),
MaintenanceMode: config.NewOption[MaintenanceModeType]("instance.maintenance_mode"),
LandingPage: config.NewOption[LandingPageType]("instance.landing_page").WithFileConfig(config.CfgSecKey{Sec: "server", Key: "LANDING_PAGE"}),
},
}
}
+28
View File
@@ -52,7 +52,35 @@ func (m MaintenanceModeType) IsActive() bool {
return true
}
// LandingPageType configures the default page for unauthenticated visitors.
// Mode values: "home", "explore", "organizations", "login", or "custom".
// When Mode is "custom", CustomPath holds the redirect target (e.g. "/MokoConsulting").
type LandingPageType struct {
Mode string // home, explore, organizations, login, custom
CustomPath string // only used when Mode == "custom"
}
// URL returns the redirect path for the configured landing page.
func (lp LandingPageType) URL() string {
switch lp.Mode {
case "explore":
return "/explore"
case "organizations":
return "/explore/organizations"
case "login":
return "/user/login"
case "custom":
if lp.CustomPath != "" {
return lp.CustomPath
}
return "/"
default:
return "/"
}
}
type InstanceStruct struct {
WebBanner *config.Option[WebBannerType]
MaintenanceMode *config.Option[MaintenanceModeType]
LandingPage *config.Option[LandingPageType]
}
+8
View File
@@ -3328,6 +3328,14 @@
"admin.config.common.start_time": "Start time",
"admin.config.common.end_time": "End time",
"admin.config.common.skip_time_check": "Leave time empty (clear the field) to skip time check",
"admin.config.instance_landing_page": "Default Landing Page",
"admin.config.landing_page.home": "Home — default home page",
"admin.config.landing_page.explore": "Explore — repository explore page",
"admin.config.landing_page.organizations": "Organizations — organization explore page",
"admin.config.landing_page.login": "Login — redirect to login page",
"admin.config.landing_page.custom": "Custom path — redirect to a specific URL path",
"admin.config.landing_page.custom_path": "Custom path",
"admin.config.landing_page.custom_path_help": "Internal path to redirect unauthenticated visitors to (e.g. /MokoConsulting or /MokoConsulting/MokoGitea/wiki).",
"admin.config.instance_maintenance": "Instance Maintenance",
"admin.config.instance_maintenance_mode.admin_web_access_only": "Only allow admin to access the web UI",
"admin.config.instance_web_banner.enabled": "Show banner",
+12 -3
View File
@@ -48,9 +48,18 @@ func Home(ctx *context.Context) {
}
return
// Check non-logged users landing page.
} else if setting.LandingPageURL != setting.LandingPageHome {
ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL))
return
} else {
// Dynamic landing page from admin config takes priority.
landingPage := setting.Config().Instance.LandingPage.Value(ctx)
if landingPage.Mode != "" && landingPage.Mode != "home" {
ctx.Redirect(setting.AppSubURL + landingPage.URL())
return
}
// Fall back to static app.ini setting.
if setting.LandingPageURL != setting.LandingPageHome {
ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL))
return
}
}
// Check auto-login.
+9 -1
View File
@@ -128,7 +128,15 @@ func httpBase(ctx *context.Context, optGitService ...string) *serviceHandler {
}
// Only public pull don't need auth.
isPublicPull := repoExist && !repo.IsPrivate && isPull
// For private repos, also allow anonymous pull if the specific unit
// (code or wiki) has AnonymousAccessMode >= Read.
isPublicPull := repoExist && isPull && !repo.IsPrivate
if repoExist && isPull && repo.IsPrivate {
repoUnit := repo.MustGetUnit(ctx, unitType)
if repoUnit.AnonymousAccessMode >= perm.AccessModeRead {
isPublicPull = true
}
}
askAuth := !isPublicPull || setting.Service.RequireSignInViewStrict
// don't allow anonymous pulls if organization is not public
@@ -2,6 +2,7 @@
{{template "admin/config_settings/avatars" .}}
{{template "admin/config_settings/repository" .}}
{{template "admin/config_settings/landing_page" .}}
{{template "admin/config_settings/instance" .}}
{{template "admin/layout_footer" .}}
@@ -0,0 +1,49 @@
<h4 class="ui top attached header">{{ctx.Locale.Tr "admin.config.instance_landing_page"}}</h4>
<div class="ui attached segment">
<form class="ui form ignore-dirty system-config-form" method="post" action="{{AppSubUrl}}/-/admin/config">
{{$cfgOpt := $.SystemConfig.Instance.LandingPage}}
{{$cfgKey := $cfgOpt.DynKey}}
{{$landingPage := $cfgOpt.Value ctx}}
<input type="hidden" data-config-dyn-key="{{$cfgKey}}" data-config-value-json="{{JsonUtils.EncodeToString $landingPage}}">
<div class="grouped fields">
<div class="field">
<div class="ui radio checkbox">
<input name="{{$cfgKey}}.Mode" type="radio" value="home" {{if or (eq $landingPage.Mode "") (eq $landingPage.Mode "home")}}checked{{end}}>
<label>{{ctx.Locale.Tr "admin.config.landing_page.home"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="{{$cfgKey}}.Mode" type="radio" value="explore" {{if eq $landingPage.Mode "explore"}}checked{{end}}>
<label>{{ctx.Locale.Tr "admin.config.landing_page.explore"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="{{$cfgKey}}.Mode" type="radio" value="organizations" {{if eq $landingPage.Mode "organizations"}}checked{{end}}>
<label>{{ctx.Locale.Tr "admin.config.landing_page.organizations"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="{{$cfgKey}}.Mode" type="radio" value="login" {{if eq $landingPage.Mode "login"}}checked{{end}}>
<label>{{ctx.Locale.Tr "admin.config.landing_page.login"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="{{$cfgKey}}.Mode" type="radio" value="custom" {{if eq $landingPage.Mode "custom"}}checked{{end}}>
<label>{{ctx.Locale.Tr "admin.config.landing_page.custom"}}</label>
</div>
</div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "admin.config.landing_page.custom_path"}}</label>
<input type="text" name="{{$cfgKey}}.CustomPath" value="{{$landingPage.CustomPath}}" placeholder="/MokoConsulting">
<div class="help">{{ctx.Locale.Tr "admin.config.landing_page.custom_path_help"}}</div>
</div>
<div class="field">
<button class="ui primary button">{{ctx.Locale.Tr "save"}}</button>
</div>
</form>
</div>
+68 -58
View File
@@ -1,93 +1,103 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later
VERSION: 05.01.02
VERSION: 05.14.00
-->
<updates>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<description>MokoGitea dev build.</description>
<element>mokogitea</element>
<type>application</type>
<version>05.01.02</version>
<client>server</client>
<tags><tag>stable</tag></tags>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.01.02</infourl>
<client>site</client>
<version>05.05.00-dev</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/development</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.01.02</downloadurl>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/development/mokogitea-05.05.00-dev.zip</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>dev</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<description>MokoGitea alpha build.</description>
<element>mokogitea</element>
<type>application</type>
<version>05.01.02</version>
<client>server</client>
<tags><tag>rc</tag></tags>
<infourl title="MokoGitea RC">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.01.02</infourl>
<client>site</client>
<version>05.05.00-alpha</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/alpha</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.01.02</downloadurl>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/alpha/mokogitea-05.05.00-alpha.zip</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<version>05.00.00</version>
<client>server</client>
<tags><tag>beta</tag></tags>
<infourl title="MokoGitea Beta">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<element>mokogitea</element>
<type>application</type>
<version>05.00.00</version>
<client>server</client>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>alpha</tag></tags>
<infourl title="MokoGitea Alpha">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/v1.26.1-moko.05.00.00</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.05.00.00</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea update</description>
<description>MokoGitea beta build.</description>
<element>mokogitea</element>
<type>application</type>
<version>06.00.00-dev</version>
<client>server</client>
<tags><tag>development</tag></tags>
<infourl title="MokoGitea Dev">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/src/branch/dev</infourl>
<client>site</client>
<version>05.05.00-beta</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/beta</infourl>
<downloads>
<downloadurl type="full" format="docker">git.mokoconsulting.tech/mokoconsulting/mokogitea:v1.26.1-moko.06.00.00-dev</downloadurl>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/beta/mokogitea-05.05.00-beta.zip</downloadurl>
</downloads>
<sha256></sha256>
<targetplatform name="mokogitea" version="((1\.25\.)|(1\.26\.))" />
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>beta</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea rc build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.05.00-rc</version>
<creationDate>2026-05-30</creationDate>
<infourl title="MokoGitea">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/release-candidate</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/release-candidate/mokogitea-05.05.00-rc.zip</downloadurl>
</downloads>
<sha256>4fee9eb03e4b819a63bce2ceb54fdce0d3eb8bf5b31460fcc42e5ecd75cc856e</sha256>
<tags><tag>rc</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*"/>
</update>
<update>
<name>MokoGitea</name>
<description>MokoGitea stable build.</description>
<element>mokogitea</element>
<type>application</type>
<client>site</client>
<version>05.14.00</version>
<creationDate>2026-05-31</creationDate>
<infourl title='MokoGitea'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/tag/stable</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/releases/download/stable/mokogitea-05.14.00.zip</downloadurl>
</downloads>
<sha256>bec4bf5a1a841f8e72d9826451004db5d8afc70144231dfedc7fb01a6695955c</sha256>
<tags><tag>stable</tag></tags>
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoGitea/raw/branch/main/CHANGELOG.md</changelogurl>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name="go" version=".*" />
</update>
</updates>