Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25257b9e31 | |||
| a5bdc89faa | |||
| 0ecba968a0 | |||
| bed7adcf1c | |||
| df59b5f6d5 | |||
| 5786f0dfc4 | |||
| 2de87d8ff4 | |||
| b241acf650 | |||
| 173dfd0f26 | |||
| 1ad277cd73 | |||
| 4624385501 | |||
| e084c7f4b4 | |||
| 9721728b45 | |||
| 38ed8eaeea | |||
| 1608b5c4b9 | |||
| eb15990510 | |||
| 6654d7605d | |||
| 411ba858f5 | |||
| a7e39fa992 | |||
| bd899bcbb1 | |||
| 7c014dc4da | |||
| d0ca5eff28 | |||
| e946d49bf3 |
@@ -0,0 +1,251 @@
|
|||||||
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# FILE INFORMATION
|
||||||
|
# DEFGROUP: Gitea.Workflow
|
||||||
|
# INGROUP: moko-platform.Automation
|
||||||
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
|
# PATH: /.gitea/workflows/branch-protection.yml
|
||||||
|
# BRIEF: Apply standardised branch protection rules to all governed repositories
|
||||||
|
#
|
||||||
|
# +========================================================================+
|
||||||
|
# | BRANCH PROTECTION SETUP |
|
||||||
|
# +========================================================================+
|
||||||
|
# | |
|
||||||
|
# | Applies protection rules for: main, dev, rc, beta, alpha |
|
||||||
|
# | |
|
||||||
|
# | main — Require PR, block rejected reviews, no force push |
|
||||||
|
# | dev — Allow push, no force push, no delete |
|
||||||
|
# | rc — Allow push, no force push, no delete |
|
||||||
|
# | beta — Allow push, no force push, no delete |
|
||||||
|
# | alpha — Allow push, no force push, no delete |
|
||||||
|
# | |
|
||||||
|
# | jmiller has override authority on all branches. |
|
||||||
|
# | |
|
||||||
|
# +========================================================================+
|
||||||
|
|
||||||
|
name: Branch Protection Setup
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * 1' # Weekly Monday 02:00 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
dry_run:
|
||||||
|
description: 'Preview mode (no changes)'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
repos:
|
||||||
|
description: 'Comma-separated repo names (empty = all governed repos)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITEA_URL: https://git.mokoconsulting.tech
|
||||||
|
GITEA_ORG: MokoConsulting
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
protect:
|
||||||
|
name: Apply Branch Protection Rules
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Determine target repos
|
||||||
|
id: repos
|
||||||
|
env:
|
||||||
|
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||||
|
run: |
|
||||||
|
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="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
|
||||||
|
|
||||||
|
if [ -n "${{ inputs.repos }}" ]; then
|
||||||
|
# User-specified repos
|
||||||
|
REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ')
|
||||||
|
else
|
||||||
|
# Fetch all org repos
|
||||||
|
PAGE=1
|
||||||
|
REPOS=""
|
||||||
|
while true; do
|
||||||
|
BATCH=$(curl -sS \
|
||||||
|
-H "Authorization: token ${GA_TOKEN}" \
|
||||||
|
"${API}/orgs/${GITEA_ORG}/repos?page=${PAGE}&limit=50" \
|
||||||
|
| jq -r '.[].name // empty')
|
||||||
|
[ -z "$BATCH" ] && break
|
||||||
|
REPOS="$REPOS $BATCH"
|
||||||
|
PAGE=$((PAGE + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Filter out excluded repos
|
||||||
|
FILTERED=""
|
||||||
|
for REPO in $REPOS; do
|
||||||
|
SKIP=false
|
||||||
|
for EX in $EXCLUDE; do
|
||||||
|
if [ "$REPO" = "$EX" ]; then
|
||||||
|
SKIP=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$SKIP" = "false" ]; then
|
||||||
|
FILTERED="$FILTERED $REPO"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
REPOS="$FILTERED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "repos=$REPOS" >> "$GITHUB_OUTPUT"
|
||||||
|
COUNT=$(echo "$REPOS" | wc -w)
|
||||||
|
echo "📋 Target repos (${COUNT}): $REPOS"
|
||||||
|
|
||||||
|
- name: Apply protection rules
|
||||||
|
env:
|
||||||
|
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||||
|
DRY_RUN: ${{ inputs.dry_run || 'false' }}
|
||||||
|
run: |
|
||||||
|
API="${GITEA_URL}/api/v1"
|
||||||
|
REPOS="${{ steps.repos.outputs.repos }}"
|
||||||
|
|
||||||
|
SUCCESS=0
|
||||||
|
FAILED=0
|
||||||
|
SKIPPED=0
|
||||||
|
|
||||||
|
# ── Rule definitions ──────────────────────────────────────
|
||||||
|
# Only the CI bot (jmiller token) can push directly.
|
||||||
|
# All human contributors must use PRs.
|
||||||
|
# Force push disabled on all branches.
|
||||||
|
|
||||||
|
RULE_MAIN='{
|
||||||
|
"rule_name": "main",
|
||||||
|
"enable_push": true,
|
||||||
|
"enable_push_whitelist": true,
|
||||||
|
"push_whitelist_usernames": ["jmiller"],
|
||||||
|
"enable_force_push": false,
|
||||||
|
"enable_force_push_allowlist": false,
|
||||||
|
"force_push_allowlist_usernames": [],
|
||||||
|
"enable_merge_whitelist": false,
|
||||||
|
"required_approvals": 0,
|
||||||
|
"dismiss_stale_approvals": true,
|
||||||
|
"block_on_rejected_reviews": true,
|
||||||
|
"block_on_outdated_branch": false,
|
||||||
|
"priority": 1
|
||||||
|
}'
|
||||||
|
|
||||||
|
RULE_DEV='{
|
||||||
|
"rule_name": "dev",
|
||||||
|
"enable_push": true,
|
||||||
|
"enable_push_whitelist": true,
|
||||||
|
"push_whitelist_usernames": ["jmiller"],
|
||||||
|
"enable_force_push": false,
|
||||||
|
"enable_force_push_allowlist": false,
|
||||||
|
"force_push_allowlist_usernames": [],
|
||||||
|
"enable_merge_whitelist": false,
|
||||||
|
"required_approvals": 0,
|
||||||
|
"block_on_rejected_reviews": false,
|
||||||
|
"priority": 2
|
||||||
|
}'
|
||||||
|
|
||||||
|
RULE_RC='{
|
||||||
|
"rule_name": "rc",
|
||||||
|
"enable_push": true,
|
||||||
|
"enable_push_whitelist": true,
|
||||||
|
"push_whitelist_usernames": ["jmiller"],
|
||||||
|
"enable_force_push": false,
|
||||||
|
"enable_force_push_allowlist": false,
|
||||||
|
"force_push_allowlist_usernames": [],
|
||||||
|
"enable_merge_whitelist": false,
|
||||||
|
"required_approvals": 0,
|
||||||
|
"block_on_rejected_reviews": false,
|
||||||
|
"priority": 3
|
||||||
|
}'
|
||||||
|
|
||||||
|
RULE_BETA='{
|
||||||
|
"rule_name": "beta",
|
||||||
|
"enable_push": true,
|
||||||
|
"enable_push_whitelist": true,
|
||||||
|
"push_whitelist_usernames": ["jmiller"],
|
||||||
|
"enable_force_push": false,
|
||||||
|
"enable_force_push_allowlist": false,
|
||||||
|
"force_push_allowlist_usernames": [],
|
||||||
|
"enable_merge_whitelist": false,
|
||||||
|
"required_approvals": 0,
|
||||||
|
"block_on_rejected_reviews": false,
|
||||||
|
"priority": 4
|
||||||
|
}'
|
||||||
|
|
||||||
|
RULE_ALPHA='{
|
||||||
|
"rule_name": "alpha",
|
||||||
|
"enable_push": true,
|
||||||
|
"enable_push_whitelist": true,
|
||||||
|
"push_whitelist_usernames": ["jmiller"],
|
||||||
|
"enable_force_push": false,
|
||||||
|
"enable_force_push_allowlist": false,
|
||||||
|
"force_push_allowlist_usernames": [],
|
||||||
|
"enable_merge_whitelist": false,
|
||||||
|
"required_approvals": 0,
|
||||||
|
"block_on_rejected_reviews": false,
|
||||||
|
"priority": 5
|
||||||
|
}'
|
||||||
|
|
||||||
|
RULES=("$RULE_MAIN" "$RULE_DEV" "$RULE_RC" "$RULE_BETA" "$RULE_ALPHA")
|
||||||
|
RULE_NAMES=("main" "dev" "rc" "beta" "alpha")
|
||||||
|
|
||||||
|
# ── Apply rules to each repo ──────────────────────────────
|
||||||
|
for REPO in $REPOS; do
|
||||||
|
echo ""
|
||||||
|
echo "═══ ${REPO} ═══"
|
||||||
|
|
||||||
|
for i in "${!RULES[@]}"; do
|
||||||
|
RULE="${RULES[$i]}"
|
||||||
|
NAME="${RULE_NAMES[$i]}"
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = "true" ]; then
|
||||||
|
echo " [DRY RUN] Would apply rule: ${NAME}"
|
||||||
|
SKIPPED=$((SKIPPED + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete existing rule if present (idempotent recreate)
|
||||||
|
ENCODED_NAME=$(echo "$NAME" | sed 's|/|%2F|g')
|
||||||
|
curl -sS -o /dev/null -w "" \
|
||||||
|
-X DELETE \
|
||||||
|
-H "Authorization: token ${GA_TOKEN}" \
|
||||||
|
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections/${ENCODED_NAME}" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create rule
|
||||||
|
RESPONSE=$(curl -sS -w "\n%{http_code}" \
|
||||||
|
-X POST \
|
||||||
|
-H "Authorization: token ${GA_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$RULE" \
|
||||||
|
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections")
|
||||||
|
|
||||||
|
HTTP=$(echo "$RESPONSE" | tail -1)
|
||||||
|
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||||
|
|
||||||
|
if [ "$HTTP" = "201" ]; then
|
||||||
|
echo " ✅ ${NAME}"
|
||||||
|
SUCCESS=$((SUCCESS + 1))
|
||||||
|
else
|
||||||
|
echo " ❌ ${NAME} (HTTP ${HTTP}): $(echo "$BODY" | jq -r '.message // .' 2>/dev/null | head -1)"
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Summary ───────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════"
|
||||||
|
echo " ✅ Success: ${SUCCESS}"
|
||||||
|
echo " ❌ Failed: ${FAILED}"
|
||||||
|
echo " ⏭️ Skipped: ${SKIPPED}"
|
||||||
|
echo "════════════════════════════════════════"
|
||||||
|
|
||||||
|
if [ "$FAILED" -gt 0 ]; then
|
||||||
|
echo "::warning::${FAILED} rule(s) failed to apply"
|
||||||
|
fi
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /.mokogitea/workflows/auto-bump.yml
|
# PATH: /.mokogitea/workflows/auto-bump.yml
|
||||||
# VERSION: 09.02.00
|
# VERSION: 09.02.00
|
||||||
# BRIEF: Auto patch-bump version on every push to dev
|
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
||||||
|
|
||||||
name: "Universal: Auto Version Bump"
|
name: "Universal: Auto Version Bump"
|
||||||
|
|
||||||
@@ -16,9 +16,13 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
- main
|
- alpha
|
||||||
|
- beta
|
||||||
|
- rc
|
||||||
|
- 'feature/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -26,17 +30,18 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bump:
|
bump:
|
||||||
name: Patch Bump
|
name: Version Bump
|
||||||
runs-on: release
|
runs-on: release
|
||||||
if: >-
|
if: >-
|
||||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||||
!contains(github.event.head_commit.message, '[skip bump]')
|
!contains(github.event.head_commit.message, '[skip bump]') &&
|
||||||
|
!startsWith(github.event.head_commit.message, 'Merge pull request')
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Setup moko-platform tools
|
- name: Setup moko-platform tools
|
||||||
@@ -48,7 +53,7 @@ jobs:
|
|||||||
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
||||||
else
|
else
|
||||||
git clone --depth 1 --branch main --quiet \
|
git clone --depth 1 --branch main --quiet \
|
||||||
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
||||||
/tmp/moko-platform-api
|
/tmp/moko-platform-api
|
||||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||||
@@ -56,39 +61,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
run: |
|
run: |
|
||||||
BRANCH="${{ github.ref_name }}"
|
php ${MOKO_CLI}/version_auto_bump.php \
|
||||||
|
--path . --branch "${GITHUB_REF_NAME}" \
|
||||||
# main = minor bump, dev = patch bump
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
if [ "$BRANCH" = "main" ]; then
|
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
BUMP_TYPE="--minor"
|
|
||||||
BUMP_LABEL="minor"
|
|
||||||
else
|
|
||||||
BUMP_TYPE=""
|
|
||||||
BUMP_LABEL="patch"
|
|
||||||
fi
|
|
||||||
|
|
||||||
BUMP=$(php ${MOKO_CLI}/version_bump.php --path . $BUMP_TYPE 2>&1) || true
|
|
||||||
echo "$BUMP"
|
|
||||||
|
|
||||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) || true
|
|
||||||
[ -z "$VERSION" ] && { echo "No version found — skipping"; exit 0; }
|
|
||||||
|
|
||||||
# Propagate to platform manifests
|
|
||||||
php ${MOKO_CLI}/version_set_platform.php \
|
|
||||||
--path . --version "$VERSION" --branch "$BRANCH" 2>/dev/null || true
|
|
||||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
|
||||||
|
|
||||||
# Commit if anything changed
|
|
||||||
if git diff --quiet && git diff --cached --quiet; then
|
|
||||||
echo "No version changes to commit"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
|
||||||
git add -A
|
|
||||||
git commit -m "chore(version): ${BUMP_LABEL} bump to ${VERSION} [skip ci]" \
|
|
||||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
|
||||||
git push origin "$BRANCH"
|
|
||||||
echo "Bumped to ${VERSION} (${BUMP_LABEL})" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|||||||
@@ -63,31 +63,75 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Setup moko-platform tools
|
- name: Setup moko-platform tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
run: |
|
run: |
|
||||||
if ! command -v composer &> /dev/null; then
|
if ! command -v composer &> /dev/null; 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
|
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
|
fi
|
||||||
|
# Always fetch latest CLI tools — never use stale cache from previous runs
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
git clone --depth 1 --branch main --quiet \
|
git clone --depth 1 --branch main --quiet \
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||||
/tmp/moko-platform-api
|
/tmp/moko-platform-api
|
||||||
cd /tmp/moko-platform-api
|
cd /tmp/moko-platform-api
|
||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
- name: Promote to release-candidate
|
- name: Rename source branch to rc
|
||||||
|
run: |
|
||||||
|
SOURCE_BRANCH="${{ github.event.pull_request.head.ref || 'dev' }}"
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
PR_NUM="${{ github.event.pull_request.number }}"
|
||||||
|
php /tmp/moko-platform-api/cli/branch_rename.php \
|
||||||
|
--from "$SOURCE_BRANCH" --to rc \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
|
--api-base "${API_BASE}" \
|
||||||
|
--pr "$PR_NUM"
|
||||||
|
|
||||||
|
- name: Set RC version on renamed branch
|
||||||
|
run: |
|
||||||
|
# Checkout the new rc branch
|
||||||
|
git fetch origin rc
|
||||||
|
git checkout rc
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
MOKO_CLI="/tmp/moko-platform-api/cli"
|
||||||
|
|
||||||
|
VERSION=$(php ${MOKO_CLI}/version_read.php --path .) || true
|
||||||
|
[ -z "$VERSION" ] && { echo "No version — skipping"; exit 0; }
|
||||||
|
|
||||||
|
php ${MOKO_CLI}/version_set_platform.php \
|
||||||
|
--path . --version "$VERSION" --branch rc --stability rc 2>/dev/null || true
|
||||||
|
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||||
|
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||||
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
|
git config --local user.name "gitea-actions[bot]"
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore(version): set RC stability suffix [skip ci]" \
|
||||||
|
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||||
|
git push origin rc
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build RC release
|
||||||
run: |
|
run: |
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_promote.php \
|
MOKO_CLI="/tmp/moko-platform-api/cli"
|
||||||
--from auto --to release-candidate \
|
VERSION=$(php ${MOKO_CLI}/version_read.php --path .) || true
|
||||||
--token "${{ secrets.GA_TOKEN }}" \
|
|
||||||
--api-base "${API_BASE}" \
|
php ${MOKO_CLI}/release_create.php \
|
||||||
--branch "${{ github.event.pull_request.head.ref }}"
|
--path . --version "$VERSION" --tag "release-candidate" \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
|
--repo "${GITEA_REPO}" --branch rc 2>&1 || true
|
||||||
|
|
||||||
|
php ${MOKO_CLI}/release_package.php \
|
||||||
|
--path . --version "$VERSION" --tag "release-candidate" \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
|
--repo "${GITEA_REPO}" --output /tmp 2>&1 || true
|
||||||
|
|
||||||
- name: Cascade lesser channels
|
- name: Cascade lesser channels
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@@ -95,14 +139,14 @@ jobs:
|
|||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_cascade.php \
|
php /tmp/moko-platform-api/cli/release_cascade.php \
|
||||||
--stability release-candidate \
|
--stability release-candidate \
|
||||||
--token "${{ secrets.GA_TOKEN }}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
--api-base "${API_BASE}"
|
--api-base "${API_BASE}"
|
||||||
|
|
||||||
- name: Summary
|
- name: Summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Draft PR opened — promoted highest pre-release to RC" >> $GITHUB_STEP_SUMMARY
|
echo "Draft PR opened — branch renamed to rc, RC release built" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
|
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
|
||||||
release:
|
release:
|
||||||
@@ -116,19 +160,27 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 0
|
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: Setup moko-platform tools
|
- name: Setup moko-platform tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
|
||||||
run: |
|
run: |
|
||||||
# Ensure PHP + Composer are available
|
# Ensure PHP + Composer are available
|
||||||
if ! command -v composer &> /dev/null; then
|
if ! command -v composer &> /dev/null; 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
|
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
|
fi
|
||||||
|
# Always fetch latest CLI tools — never use stale cache from previous runs
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
git clone --depth 1 --branch main --quiet \
|
git clone --depth 1 --branch main --quiet \
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||||
/tmp/moko-platform-api
|
/tmp/moko-platform-api
|
||||||
@@ -155,7 +207,8 @@ jobs:
|
|||||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
# version_set_platform strips suffixes internally when --stability stable
|
||||||
|
MAJOR=$(echo "$VERSION" | cut -d. -f1 | sed 's/-.*//')
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
|
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
|
||||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||||
@@ -167,9 +220,9 @@ jobs:
|
|||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
run: |
|
run: |
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
RC_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
RC_JSON=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
"${API_BASE}/releases/tags/release-candidate" 2>/dev/null || echo "{}")
|
"${API_BASE}/releases/tags/release-candidate" 2>/dev/null || echo "{}")
|
||||||
RC_ID=$(echo "$RC_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
|
RC_ID=$(echo "$RC_JSON" | php -r "\$d=json_decode(file_get_contents('php://stdin'),true); echo \$d['id'] ?? '';" 2>/dev/null || true)
|
||||||
|
|
||||||
if [ -n "$RC_ID" ] && [ "$RC_ID" != "None" ] && [ "$RC_ID" != "" ]; then
|
if [ -n "$RC_ID" ] && [ "$RC_ID" != "None" ] && [ "$RC_ID" != "" ]; then
|
||||||
echo "promote=true" >> "$GITHUB_OUTPUT"
|
echo "promote=true" >> "$GITHUB_OUTPUT"
|
||||||
@@ -180,7 +233,18 @@ jobs:
|
|||||||
echo "::notice::No RC release — full build pipeline"
|
echo "::notice::No RC release — full build pipeline"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Version bump handled by auto-bump.yml (minor on main, patch on dev)
|
- name: "Step 1b: Minor bump version"
|
||||||
|
id: bump
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.rc.outputs.promote != 'true'
|
||||||
|
run: |
|
||||||
|
MOKO_API="/tmp/moko-platform-api/cli"
|
||||||
|
php ${MOKO_API}/version_bump.php --path . --minor 2>&1 || true
|
||||||
|
VERSION=$(php ${MOKO_API}/version_read.php --path .)
|
||||||
|
# version_set_platform handles suffix stripping — just pass clean base version
|
||||||
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Bumped to: ${VERSION}"
|
||||||
|
|
||||||
- name: Check if already released
|
- name: Check if already released
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
@@ -207,7 +271,7 @@ jobs:
|
|||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.check.outputs.already_released != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
php /tmp/moko-platform-api/cli/release_validate.php \
|
php /tmp/moko-platform-api/cli/release_validate.php \
|
||||||
--path . --version "$VERSION" --output-summary --github-output || true
|
--path . --version "$VERSION" --output-summary --github-output || true
|
||||||
|
|
||||||
@@ -218,7 +282,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
BRANCH="${{ steps.version.outputs.branch }}"
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
IS_MINOR="${{ steps.version.outputs.is_minor }}"
|
IS_MINOR="${{ steps.version.outputs.is_minor }}"
|
||||||
PATCH="${{ steps.version.outputs.version }}"
|
PATCH="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}')
|
PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}')
|
||||||
|
|
||||||
# Check if branch exists
|
# Check if branch exists
|
||||||
@@ -237,7 +301,7 @@ jobs:
|
|||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.check.outputs.already_released != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
php /tmp/moko-platform-api/cli/version_set_platform.php \
|
php /tmp/moko-platform-api/cli/version_set_platform.php \
|
||||||
--path . --version "$VERSION" --branch main
|
--path . --version "$VERSION" --branch main
|
||||||
|
|
||||||
@@ -245,12 +309,24 @@ jobs:
|
|||||||
- name: "Step 4: Update version badges"
|
- name: "Step 4: Update version badges"
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
|
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
|
||||||
php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true
|
php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true
|
||||||
|
|
||||||
# Step 5 (updates.xml) moved after Step 8 to include SHA-256 checksum
|
# Step 5 (updates.xml) moved after Step 8 to include SHA-256 checksum
|
||||||
|
|
||||||
|
- name: "Step 4b: Promote and prune CHANGELOG"
|
||||||
|
if: >-
|
||||||
|
steps.version.outputs.skip != 'true' &&
|
||||||
|
steps.check.outputs.already_released != 'true'
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
|
MOKO_API="/tmp/moko-platform-api/cli"
|
||||||
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
|
php ${MOKO_API}/changelog_promote.php --path . --version "$VERSION" 2>&1 || true
|
||||||
|
php ${MOKO_API}/changelog_prune.php --path . --keep 5 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Commit release changes
|
- name: Commit release changes
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
@@ -260,15 +336,12 @@ jobs:
|
|||||||
echo "No changes to commit"
|
echo "No changes to commit"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
# Set push URL with token for branch-protected repos
|
|
||||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
|
||||||
git add -A
|
git add -A
|
||||||
git commit -m "chore(release): build ${VERSION} [skip ci]" \
|
git commit -m "chore(release): build ${VERSION} [skip ci]" \
|
||||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||||
git push -u origin HEAD
|
# Detached HEAD on PR merge — push explicitly to main
|
||||||
|
git push origin HEAD:refs/heads/main
|
||||||
|
|
||||||
# -- STEP 6: Create tag ---------------------------------------------------
|
# -- STEP 6: Create tag ---------------------------------------------------
|
||||||
- name: "Step 6: Create git tag"
|
- name: "Step 6: Create git tag"
|
||||||
@@ -292,11 +365,11 @@ jobs:
|
|||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.rc.outputs.promote == 'true'
|
steps.rc.outputs.promote == 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_promote.php \
|
php /tmp/moko-platform-api/cli/release_promote.php \
|
||||||
--from release-candidate --to stable \
|
--from release-candidate --to stable \
|
||||||
--token "${{ secrets.GA_TOKEN }}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
--api-base "${API_BASE}" \
|
--api-base "${API_BASE}" \
|
||||||
--path . --branch main
|
--path . --branch main
|
||||||
echo "Promoted RC → stable (${VERSION})" >> $GITHUB_STEP_SUMMARY
|
echo "Promoted RC → stable (${VERSION})" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -307,12 +380,12 @@ jobs:
|
|||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.rc.outputs.promote != 'true'
|
steps.rc.outputs.promote != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_create.php \
|
php /tmp/moko-platform-api/cli/release_create.php \
|
||||||
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
|
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
|
||||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
--repo "${GITEA_REPO}" --branch main
|
--repo "${GITEA_REPO}" --branch main
|
||||||
echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
@@ -323,27 +396,27 @@ jobs:
|
|||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.rc.outputs.promote != 'true'
|
steps.rc.outputs.promote != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_package.php \
|
php /tmp/moko-platform-api/cli/release_package.php \
|
||||||
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
|
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
|
||||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
--repo "${GITEA_REPO}" --output /tmp || true
|
--repo "${GITEA_REPO}" --output /tmp || true
|
||||||
|
|
||||||
# -- STEP 5: Write update stream (after build so SHA-256 is available) -----
|
# -- STEP 5: Write update stream (after build so SHA-256 is available) -----
|
||||||
- name: "Step 5: Write update stream"
|
- name: "Step 5: Write update stream"
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||||
|
|
||||||
# Fetch latest updates.xml from main so preserve logic has all channels
|
# Fetch latest updates.xml from main so preserve logic has all channels
|
||||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||||
curl -sf -H "Authorization: token ${GA_TOKEN}" \
|
curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
"${API}/contents/updates.xml?ref=main" 2>/dev/null | \
|
"${API}/contents/updates.xml?ref=main" 2>/dev/null | \
|
||||||
python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())" \
|
php -r "\$d=json_decode(file_get_contents('php://stdin'),true); echo base64_decode(\$d['content'] ?? '');" \
|
||||||
> updates.xml 2>/dev/null || true
|
> updates.xml 2>/dev/null || true
|
||||||
|
|
||||||
SHA_FLAG=""
|
SHA_FLAG=""
|
||||||
@@ -356,58 +429,41 @@ jobs:
|
|||||||
|
|
||||||
# Commit updates.xml if changed
|
# Commit updates.xml if changed
|
||||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
|
||||||
git add updates.xml
|
git add updates.xml
|
||||||
git commit -m "chore: update stable channel ${VERSION} [skip ci]" \
|
git commit -m "chore: update stable channel ${VERSION} [skip ci]" \
|
||||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||||
git push origin HEAD 2>&1 || true
|
git push origin HEAD:refs/heads/main 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -- STEP 8b: Update release description with changelog ----------------------
|
# -- STEP 8b: Update release description with changelog ----------------------
|
||||||
- name: "Step 8b: Update release body"
|
- name: "Step 8b: Update release body"
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
MOKO_CLI="/tmp/moko-platform-api/cli"
|
php /tmp/moko-platform-api/cli/release_body_update.php \
|
||||||
|
|
||||||
php ${MOKO_CLI}/release_body_update.php \
|
|
||||||
--path . --version "${VERSION}" --tag "${RELEASE_TAG}" \
|
--path . --version "${VERSION}" --tag "${RELEASE_TAG}" \
|
||||||
--token "${{ secrets.GA_TOKEN }}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||||
2>/dev/null || {
|
2>&1 || true
|
||||||
# Fallback: simple body update if CLI not available
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
|
||||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \
|
|
||||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
|
||||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
|
||||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\nChecksum files attached as \`*.sha256\` assets."
|
|
||||||
curl -sf -X PATCH -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
"${API_BASE}/releases/${RELEASE_ID}" \
|
|
||||||
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
echo "Release body updated" >> $GITHUB_STEP_SUMMARY
|
echo "Release body updated" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
||||||
- name: "Step 9: Mirror release to GitHub"
|
- name: "Step 9: Mirror release to GitHub"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
secrets.GH_TOKEN != ''
|
secrets.GH_MIRROR_TOKEN != ''
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_mirror.php \
|
php /tmp/moko-platform-api/cli/release_mirror.php \
|
||||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||||
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
--gh-token "${{ secrets.GH_TOKEN }}" --gh-repo "$GH_REPO" \
|
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
|
||||||
--branch main 2>&1 || true
|
--branch main 2>&1 || true
|
||||||
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
|
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
@@ -415,14 +471,14 @@ jobs:
|
|||||||
- name: "Step 10: Push main to GitHub mirror"
|
- name: "Step 10: Push main to GitHub mirror"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
secrets.GH_TOKEN != ''
|
secrets.GH_MIRROR_TOKEN != ''
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||||
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
|
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
|
||||||
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
|
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
|
||||||
git remote add github "https://x-access-token:${{ secrets.GH_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
|
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_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
|
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 fetch origin main --depth=1
|
||||||
git push github origin/main:refs/heads/main --force 2>/dev/null \
|
git push github origin/main:refs/heads/main --force 2>/dev/null \
|
||||||
&& echo "main branch pushed to GitHub mirror" \
|
&& echo "main branch pushed to GitHub mirror" \
|
||||||
@@ -433,20 +489,28 @@ jobs:
|
|||||||
- name: "Delete lesser pre-release channels"
|
- name: "Delete lesser pre-release channels"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_cascade.php \
|
php /tmp/moko-platform-api/cli/release_cascade.php \
|
||||||
--stability stable \
|
--stability stable \
|
||||||
--version "${VERSION}" \
|
--version "${VERSION}" \
|
||||||
--token "${{ secrets.GA_TOKEN }}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
--api-base "${API_BASE}" 2>/dev/null || true
|
--api-base "${API_BASE}" 2>/dev/null || true
|
||||||
|
|
||||||
- name: "Step 11: Delete and recreate dev branch from main"
|
- name: "Step 11: Clean up pre-release branches and recreate dev from main"
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
|
# Delete ephemeral pre-release branches (rc, alpha, beta)
|
||||||
|
for EPHEMERAL in rc alpha beta; do
|
||||||
|
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||||
|
"${API_BASE}/branches/${EPHEMERAL}" 2>/dev/null \
|
||||||
|
&& echo "Deleted ${EPHEMERAL} branch" \
|
||||||
|
|| echo "${EPHEMERAL} branch not found"
|
||||||
|
done
|
||||||
|
|
||||||
# Delete dev branch
|
# Delete dev branch
|
||||||
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||||
@@ -458,7 +522,26 @@ jobs:
|
|||||||
"${API_BASE}/branches" \
|
"${API_BASE}/branches" \
|
||||||
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
|
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
|
||||||
|
|
||||||
echo "Dev branch reset from main (keeps dev ahead after release)" >> $GITHUB_STEP_SUMMARY
|
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 -----------------------------
|
# -- Dolibarr post-release: Reset dev version -----------------------------
|
||||||
@@ -468,14 +551,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/version_reset_dev.php \
|
php /tmp/moko-platform-api/cli/version_reset_dev.php \
|
||||||
--token "${{ secrets.GA_TOKEN }}" --api-base "${API_BASE}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
||||||
--branch dev --path . 2>&1 || true
|
--branch dev --path . 2>&1 || true
|
||||||
|
|
||||||
# -- Summary --------------------------------------------------------------
|
# -- Summary --------------------------------------------------------------
|
||||||
- name: Pipeline Summary
|
- name: Pipeline Summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||||
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
||||||
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
|
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# FILE INFORMATION
|
||||||
|
# DEFGROUP: Gitea.Workflow
|
||||||
|
# INGROUP: MokoStandards.Universal
|
||||||
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
|
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
||||||
|
# VERSION: 01.00.00
|
||||||
|
# BRIEF: Delete feature branches after PR merge
|
||||||
|
|
||||||
|
name: "Branch Cleanup"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cleanup:
|
||||||
|
name: Delete merged branch
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: >-
|
||||||
|
github.event.pull_request.merged == true &&
|
||||||
|
github.event.pull_request.head.ref != 'dev' &&
|
||||||
|
github.event.pull_request.head.ref != 'main'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Delete source branch
|
||||||
|
run: |
|
||||||
|
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||||
|
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||||
|
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
||||||
|
|
||||||
|
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||||
|
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
|
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [ "$STATUS" = "204" ]; then
|
||||||
|
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [ "$STATUS" = "404" ]; then
|
||||||
|
echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
|
||||||
|
fi
|
||||||
@@ -1,313 +1,233 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: moko-platform.Release
|
# INGROUP: moko-platform.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||||
# VERSION: 05.01.00
|
# VERSION: 05.01.00
|
||||||
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||||
|
|
||||||
name: "Universal: Pre-Release"
|
name: "Universal: Pre-Release"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
stability:
|
stability:
|
||||||
description: 'Pre-release channel'
|
description: 'Pre-release channel'
|
||||||
required: true
|
required: true
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- development
|
- development
|
||||||
- alpha
|
- alpha
|
||||||
- beta
|
- beta
|
||||||
- release-candidate
|
- release-candidate
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
||||||
runs-on: release
|
runs-on: release
|
||||||
if: >-
|
if: >-
|
||||||
github.event_name == 'workflow_dispatch' ||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
|
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Setup tools
|
- name: Setup moko-platform tools
|
||||||
run: |
|
env:
|
||||||
# Update moko-platform CLI tools if available; install PHP if missing
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
if command -v moko-platform-update &> /dev/null; then
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
moko-platform-update
|
run: |
|
||||||
elif [ -d "/opt/moko-platform" ]; then
|
if ! command -v composer &> /dev/null; then
|
||||||
cd /opt/moko-platform && git pull origin main --quiet 2>/dev/null || true
|
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
|
||||||
else
|
fi
|
||||||
if ! command -v php &> /dev/null; then
|
# Always fetch latest CLI tools — never use stale cache from previous runs
|
||||||
sudo apt-get update -qq
|
rm -rf /tmp/moko-platform-api
|
||||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
|
git clone --depth 1 --branch main --quiet \
|
||||||
fi
|
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||||
git clone --depth 1 --branch main --quiet \
|
/tmp/moko-platform-api
|
||||||
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||||
/tmp/moko-platform-api
|
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||||
fi
|
|
||||||
# Set MOKO_CLI to whichever path exists
|
- name: Detect platform
|
||||||
if [ -d "/opt/moko-platform/cli" ]; then
|
id: platform
|
||||||
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
run: |
|
||||||
else
|
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
|
||||||
fi
|
- name: Resolve metadata and bump version
|
||||||
|
id: meta
|
||||||
- name: Detect platform
|
run: |
|
||||||
id: platform
|
STABILITY="${{ inputs.stability || 'development' }}"
|
||||||
run: |
|
|
||||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
case "$STABILITY" in
|
||||||
|
development) SUFFIX="-dev"; TAG="development" ;;
|
||||||
- name: Resolve metadata and bump version
|
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
||||||
id: meta
|
beta) SUFFIX="-beta"; TAG="beta" ;;
|
||||||
run: |
|
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||||
STABILITY="${{ inputs.stability || 'development' }}"
|
esac
|
||||||
|
|
||||||
case "$STABILITY" in
|
# Read current version (bump already handled by push workflow)
|
||||||
development) SUFFIX="-dev"; TAG="development" ;;
|
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
||||||
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
[ -z "$VERSION" ] && VERSION="00.00.01"
|
||||||
beta) SUFFIX="-beta"; TAG="beta" ;;
|
|
||||||
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
# Strip any existing suffix from version before applying stability
|
||||||
esac
|
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||||
|
|
||||||
# Read current version (bump already handled by push workflow)
|
php ${MOKO_CLI}/version_set_platform.php \
|
||||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
|
||||||
[ -z "$VERSION" ] && VERSION="00.00.01"
|
|
||||||
|
# Verify version consistency across all files
|
||||||
php ${MOKO_CLI}/version_set_platform.php \
|
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||||
--path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true
|
|
||||||
|
# Update VERSION variable with suffix
|
||||||
# Verify version consistency across all files
|
if [ -n "$SUFFIX" ]; then
|
||||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
VERSION="${VERSION}${SUFFIX}"
|
||||||
|
fi
|
||||||
# Commit version bump
|
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
# Commit version bump
|
||||||
git config --local user.name "gitea-actions[bot]"
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
git config --local user.name "gitea-actions[bot]"
|
||||||
git add -A
|
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
git diff --cached --quiet || {
|
git add -A
|
||||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
git diff --cached --quiet || {
|
||||||
git push origin HEAD 2>&1
|
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 \
|
# Auto-detect element via manifest_element.php
|
||||||
--path . --version "$VERSION" --stability "$STABILITY" \
|
php ${MOKO_CLI}/manifest_element.php \
|
||||||
--repo "${GITEA_REPO}" --github-output
|
--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)
|
# Read back element outputs
|
||||||
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||||
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
[ -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 "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||||
|
|
||||||
- name: Build package
|
- name: Create release
|
||||||
run: |
|
id: release
|
||||||
SOURCE_DIR="src"
|
run: |
|
||||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
if [ ! -d "$SOURCE_DIR" ]; then
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
echo "::error::No src/ or htdocs/ directory"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
exit 1
|
php ${MOKO_CLI}/release_create.php \
|
||||||
fi
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
MANIFEST="${{ steps.meta.outputs.manifest }}"
|
--repo "${GITEA_REPO}" --branch dev --prerelease
|
||||||
EXT_TYPE=""
|
|
||||||
if [ -n "$MANIFEST" ]; then
|
- name: Build package and upload
|
||||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
id: package
|
||||||
fi
|
run: |
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger"
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
mkdir -p build/package
|
php ${MOKO_CLI}/release_package.php \
|
||||||
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
echo "=== Building Joomla PACKAGE (multi-extension) ==="
|
--repo "${GITEA_REPO}" --output /tmp || true
|
||||||
for ext_dir in "${SOURCE_DIR}"/packages/*/; do
|
|
||||||
[ ! -d "$ext_dir" ] && continue
|
- name: Update updates.xml
|
||||||
EXT_NAME=$(basename "$ext_dir")
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
echo " Packaging sub-extension: ${EXT_NAME}"
|
run: |
|
||||||
cd "$ext_dir"
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
zip -r "../../build/package/${EXT_NAME}.zip" . -x $EXCLUDES
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
cd "$OLDPWD"
|
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||||
done
|
|
||||||
for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do
|
if [ ! -f "updates.xml" ]; then
|
||||||
[ -f "$f" ] && cp "$f" build/package/
|
echo "No updates.xml -- skipping"
|
||||||
done
|
exit 0
|
||||||
else
|
fi
|
||||||
echo "=== Building standard extension ==="
|
|
||||||
rsync -a \
|
SHA_FLAG=""
|
||||||
--exclude='sftp-config*' \
|
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
||||||
--exclude='.ftpignore' \
|
|
||||||
--exclude='*.ppk' \
|
php ${MOKO_CLI}/updates_xml_build.php \
|
||||||
--exclude='*.pem' \
|
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||||
--exclude='*.key' \
|
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||||
--exclude='.env*' \
|
${SHA_FLAG}
|
||||||
--exclude='*.local' \
|
|
||||||
--exclude='.build-trigger' \
|
# Commit and push
|
||||||
"${SOURCE_DIR}/" build/package/
|
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||||
fi
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
|
git config --local user.name "gitea-actions[bot]"
|
||||||
- name: Create ZIP
|
git add updates.xml
|
||||||
id: zip
|
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||||
run: |
|
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
||||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
fi
|
||||||
cd build/package
|
|
||||||
zip -r "../${ZIP_NAME}" .
|
- name: "Sync updates.xml to all branches"
|
||||||
cd ..
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
|
run: |
|
||||||
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
|
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
|
git config --local user.name "gitea-actions[bot]"
|
||||||
|
|
||||||
- name: Create or replace Gitea release
|
for BRANCH in main dev; do
|
||||||
id: release
|
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
||||||
run: |
|
echo "Syncing updates.xml -> ${BRANCH}"
|
||||||
TAG="${{ steps.meta.outputs.tag }}"
|
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
||||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
git add updates.xml
|
||||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
||||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
||||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
fi
|
||||||
BRANCH=$(git branch --show-current)
|
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
||||||
|
done
|
||||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))
|
|
||||||
**Channel:** ${STABILITY}
|
- name: "Delete lesser pre-release channels (cascade)"
|
||||||
**SHA-256:** \`${SHA256}\`"
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
# Delete existing release
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
"${API}/releases/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null)
|
|
||||||
if [ -n "$EXISTING_ID" ]; then
|
php ${MOKO_CLI}/release_cascade.php \
|
||||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
--stability "${{ steps.meta.outputs.stability }}" \
|
||||||
"${API}/releases/${EXISTING_ID}" 2>/dev/null || true
|
--token "${TOKEN}" \
|
||||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
--api-base "${API_BASE}"
|
||||||
"${API}/tags/${TAG}" 2>/dev/null || true
|
|
||||||
fi
|
- name: Summary
|
||||||
|
if: always()
|
||||||
# Create release
|
run: |
|
||||||
RELEASE_ID=$(curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
-H "Content-Type: application/json" \
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
"${API}/releases" \
|
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||||
-d "$(jq -n \
|
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||||
--arg tag "$TAG" \
|
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
--arg target "$BRANCH" \
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
--arg name "${EXT_ELEMENT} ${VERSION} (${STABILITY})" \
|
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||||
--arg body "$BODY" \
|
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||||
'{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: true}'
|
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
)" | jq -r '.id')
|
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT"
|
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# Upload ZIP
|
|
||||||
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
|
||||||
-H "Content-Type: application/octet-stream" \
|
|
||||||
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
|
|
||||||
--data-binary "@build/${ZIP_NAME}"
|
|
||||||
|
|
||||||
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
|
|
||||||
|
|
||||||
- name: Update updates.xml
|
|
||||||
if: steps.platform.outputs.platform == 'joomla'
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
|
||||||
|
|
||||||
if [ ! -f "updates.xml" ]; then
|
|
||||||
echo "No updates.xml -- skipping"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
php ${MOKO_CLI}/updates_xml_build.php \
|
|
||||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
|
||||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}"
|
|
||||||
|
|
||||||
# Commit and push
|
|
||||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
git add updates.xml
|
|
||||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
|
||||||
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: "Sync updates.xml to all branches"
|
|
||||||
if: steps.platform.outputs.platform == 'joomla'
|
|
||||||
run: |
|
|
||||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
|
|
||||||
for BRANCH in main dev; do
|
|
||||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
|
||||||
echo "Syncing updates.xml -> ${BRANCH}"
|
|
||||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
|
||||||
git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
|
|
||||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
|
||||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
|
||||||
git add updates.xml
|
|
||||||
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
|
||||||
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
|
||||||
fi
|
|
||||||
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
|
||||||
done
|
|
||||||
|
|
||||||
- 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.GA_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.zip.outputs.sha256 }}"
|
|
||||||
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
|
|
||||||
|
|||||||
@@ -1,660 +1,312 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: MokoStandards.Universal
|
# INGROUP: moko-platform.Universal
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /templates/workflows/update-server.yml
|
# PATH: /templates/workflows/update-server.yml
|
||||||
# VERSION: 04.07.00
|
# VERSION: 05.00.00
|
||||||
# BRIEF: Update server XML feed with stable/rc/beta/alpha/dev entries (universal)
|
# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches
|
||||||
#
|
#
|
||||||
# Writes updates.xml with multiple <update> entries:
|
# Thin wrapper around moko-platform CLI tools.
|
||||||
# - <tag>stable</tag> on push to main (from auto-release)
|
# Builds packages, updates updates.xml, and optionally deploys via SFTP.
|
||||||
# - <tag>rc</tag> on push to rc/**
|
#
|
||||||
# - <tag>development</tag> on push to dev or dev/**
|
# Joomla filters update entries by the user's "Minimum Stability" setting.
|
||||||
#
|
|
||||||
# Joomla filters by user's "Minimum Stability" setting.
|
name: "Update Server"
|
||||||
|
|
||||||
name: "Update Server"
|
on:
|
||||||
|
push:
|
||||||
on:
|
branches:
|
||||||
push:
|
- 'dev'
|
||||||
branches:
|
- 'dev/**'
|
||||||
- 'dev'
|
- 'alpha/**'
|
||||||
- 'dev/**'
|
- 'beta/**'
|
||||||
- 'alpha/**'
|
- 'rc/**'
|
||||||
- 'beta/**'
|
paths:
|
||||||
- 'rc/**'
|
- 'src/**'
|
||||||
paths:
|
- 'htdocs/**'
|
||||||
- 'src/**'
|
pull_request:
|
||||||
- 'htdocs/**'
|
types: [closed]
|
||||||
pull_request:
|
branches:
|
||||||
types: [closed]
|
- 'dev'
|
||||||
branches:
|
- 'dev/**'
|
||||||
- 'dev'
|
- 'alpha/**'
|
||||||
- 'dev/**'
|
- 'beta/**'
|
||||||
- 'alpha/**'
|
- 'rc/**'
|
||||||
- 'beta/**'
|
paths:
|
||||||
- 'rc/**'
|
- 'src/**'
|
||||||
paths:
|
- 'htdocs/**'
|
||||||
- 'src/**'
|
workflow_dispatch:
|
||||||
- 'htdocs/**'
|
inputs:
|
||||||
workflow_dispatch:
|
stability:
|
||||||
inputs:
|
description: 'Stability tag'
|
||||||
stability:
|
required: true
|
||||||
description: 'Stability tag'
|
default: 'development'
|
||||||
required: true
|
type: choice
|
||||||
default: 'development'
|
options:
|
||||||
type: choice
|
- development
|
||||||
options:
|
- alpha
|
||||||
- development
|
- beta
|
||||||
- alpha
|
- rc
|
||||||
- beta
|
- stable
|
||||||
- rc
|
|
||||||
- stable
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
env:
|
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
|
||||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
permissions:
|
||||||
|
contents: write
|
||||||
permissions:
|
|
||||||
contents: write
|
jobs:
|
||||||
|
update-xml:
|
||||||
jobs:
|
name: Update Server
|
||||||
update-xml:
|
runs-on: release
|
||||||
name: Update updates.xml
|
if: >-
|
||||||
runs-on: release
|
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
||||||
if: >-
|
|
||||||
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
steps:
|
uses: actions/checkout@v4
|
||||||
- name: Checkout repository
|
with:
|
||||||
uses: actions/checkout@v4
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
with:
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GA_TOKEN }}
|
|
||||||
fetch-depth: 0
|
- name: Setup moko-platform tools
|
||||||
|
env:
|
||||||
- name: Setup moko-platform tools
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
env:
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}'
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
run: |
|
||||||
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.GA_TOKEN }}"}}}'
|
if ! command -v composer &> /dev/null; then
|
||||||
run: |
|
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
|
||||||
if ! command -v composer &> /dev/null; then
|
fi
|
||||||
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
|
# Always fetch latest CLI tools — never use stale cache from previous runs
|
||||||
fi
|
rm -rf /tmp/moko-platform
|
||||||
if [ -d "/tmp/moko-platform" ]; then
|
git clone --depth 1 --branch main --quiet \
|
||||||
echo "moko-platform already available — skipping clone"
|
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||||
else
|
/tmp/moko-platform 2>/dev/null || true
|
||||||
git clone --depth 1 --branch main --quiet \
|
if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||||
/tmp/moko-platform 2>/dev/null || true
|
fi
|
||||||
fi
|
echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV"
|
||||||
if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
|
|
||||||
cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
- name: Detect platform
|
||||||
fi
|
id: platform
|
||||||
|
run: php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||||
- name: Generate updates.xml entry
|
|
||||||
id: update
|
- name: Resolve stability and bump version
|
||||||
run: |
|
id: meta
|
||||||
BRANCH="${{ github.ref_name }}"
|
run: |
|
||||||
REPO="${{ github.repository }}"
|
BRANCH="${{ github.ref_name }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
# Configure git for bot pushes
|
||||||
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
# Auto-bump patch on all branches (dev, alpha, beta, rc)
|
git config --local user.name "gitea-actions[bot]"
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
BUMPED=$(php /tmp/moko-platform/cli/version_bump.php --path . 2>/dev/null || true)
|
# Auto-bump patch version
|
||||||
if [ -n "$BUMPED" ]; then
|
php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
|
||||||
VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
|
|
||||||
git add -A
|
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
||||||
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
|
|
||||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" 2>/dev/null || true
|
# Strip any existing suffix before applying stability
|
||||||
git push 2>/dev/null || true
|
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||||
fi
|
|
||||||
|
# Determine stability from branch or manual input
|
||||||
# Determine stability from branch or input
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
STABILITY="${{ inputs.stability }}"
|
||||||
STABILITY="${{ inputs.stability }}"
|
elif [[ "$BRANCH" == rc/* ]]; then
|
||||||
elif [[ "$BRANCH" == rc/* ]]; then
|
STABILITY="rc"
|
||||||
STABILITY="rc"
|
elif [[ "$BRANCH" == beta/* ]]; then
|
||||||
elif [[ "$BRANCH" == beta/* ]]; then
|
STABILITY="beta"
|
||||||
STABILITY="beta"
|
elif [[ "$BRANCH" == alpha/* ]]; then
|
||||||
elif [[ "$BRANCH" == alpha/* ]]; then
|
STABILITY="alpha"
|
||||||
STABILITY="alpha"
|
else
|
||||||
elif [[ "$BRANCH" == dev/* ]] || [[ "$BRANCH" == "dev" ]]; then
|
STABILITY="development"
|
||||||
STABILITY="development"
|
fi
|
||||||
else
|
|
||||||
STABILITY="stable"
|
# Version suffix per stability stream
|
||||||
fi
|
case "$STABILITY" in
|
||||||
|
development) SUFFIX="-dev"; TAG="development" ;;
|
||||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
||||||
|
beta) SUFFIX="-beta"; TAG="beta" ;;
|
||||||
# Parse manifest (portable — no grep -P)
|
rc) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "./build/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
*) SUFFIX=""; TAG="stable" ;;
|
||||||
if [ -z "$MANIFEST" ]; then
|
esac
|
||||||
echo "No Joomla manifest found — skipping"
|
|
||||||
exit 0
|
# Propagate version with stability suffix to all manifest files
|
||||||
fi
|
php ${MOKO_CLI}/version_set_platform.php \
|
||||||
|
--path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
|
||||||
# Extract fields using sed (works on all runners)
|
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||||
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
|
|
||||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
# Re-read version (now includes suffix from version_set_platform)
|
||||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
|
if [ -n "$SUFFIX" ]; then
|
||||||
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
VERSION="${VERSION}${SUFFIX}"
|
||||||
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
fi
|
||||||
EXT_VERSION=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
|
|
||||||
TARGET_PLATFORM=$(sed -n 's/.*\(<targetplatform[^/]*\/>\).*/\1/p' "$MANIFEST" | head -1)
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
PHP_MINIMUM=$(sed -n 's/.*<php_minimum>\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
|
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||||
# Fallbacks
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||||
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
|
echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
[ -z "$EXT_TYPE" ] && EXT_TYPE="component"
|
|
||||||
|
# Commit version bump if changed
|
||||||
# Derive element if not in manifest: try XML filename, then repo name
|
git add -A
|
||||||
if [ -z "$EXT_ELEMENT" ]; then
|
git diff --cached --quiet || {
|
||||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \
|
||||||
case "$EXT_ELEMENT" in
|
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
||||||
templatedetails|manifest|*.xml) EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
|
git push
|
||||||
esac
|
}
|
||||||
fi
|
|
||||||
|
- name: Create release and upload package
|
||||||
# Use manifest version if README version is empty
|
id: package
|
||||||
[ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION"
|
run: |
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" %s>' "/")
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
# Joomla requires <client> on ALL extension types for update matching
|
|
||||||
if [ -n "$EXT_CLIENT" ]; then
|
# Create or update Gitea release
|
||||||
CLIENT_TAG="<client>${EXT_CLIENT}</client>"
|
php ${MOKO_CLI}/release_create.php \
|
||||||
else
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
CLIENT_TAG="<client>site</client>"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
fi
|
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
||||||
|
|
||||||
FOLDER_TAG=""
|
# Build package and upload
|
||||||
[ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ] && FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
php ${MOKO_CLI}/release_package.php \
|
||||||
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
PHP_TAG=""
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
[ -n "$PHP_MINIMUM" ] && PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
--repo "${GITEA_REPO}" --output /tmp || true
|
||||||
|
|
||||||
# Version suffix for non-stable
|
- name: Update updates.xml
|
||||||
DISPLAY_VERSION="$VERSION"
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
case "$STABILITY" in
|
run: |
|
||||||
development) DISPLAY_VERSION="${VERSION}-dev" ;;
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
alpha) DISPLAY_VERSION="${VERSION}-alpha" ;;
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
beta) DISPLAY_VERSION="${VERSION}-beta" ;;
|
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||||
rc) DISPLAY_VERSION="${VERSION}-rc" ;;
|
|
||||||
esac
|
if [ ! -f "updates.xml" ]; then
|
||||||
|
echo "No updates.xml — skipping"
|
||||||
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
|
exit 0
|
||||||
|
fi
|
||||||
# Each stability level has its own release tag
|
|
||||||
case "$STABILITY" in
|
SHA_FLAG=""
|
||||||
development) RELEASE_TAG="development" ;;
|
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
||||||
alpha) RELEASE_TAG="alpha" ;;
|
|
||||||
beta) RELEASE_TAG="beta" ;;
|
php ${MOKO_CLI}/updates_xml_build.php \
|
||||||
rc) RELEASE_TAG="release-candidate" ;;
|
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||||
*) RELEASE_TAG="v${MAJOR}" ;;
|
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||||
esac
|
${SHA_FLAG}
|
||||||
|
|
||||||
PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
|
# Commit and push updates.xml
|
||||||
DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
git add updates.xml
|
||||||
INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}"
|
git diff --cached --quiet || {
|
||||||
|
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||||
# -- Build install packages (ZIP + tar.gz) --------------------
|
git push
|
||||||
SOURCE_DIR="src"
|
}
|
||||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
|
||||||
if [ -d "$SOURCE_DIR" ]; then
|
- name: Sync updates.xml to main
|
||||||
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
|
if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla'
|
||||||
TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
|
run: |
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
cd "$SOURCE_DIR"
|
GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
|
|
||||||
cd ..
|
FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
|
"${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
|
||||||
--exclude='.ftpignore' --exclude='sftp-config*' \
|
|
||||||
--exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
|
if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
|
||||||
|
python3 -c "
|
||||||
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
|
import base64, json, urllib.request, sys
|
||||||
|
with open('updates.xml', 'rb') as f:
|
||||||
# Ensure release exists on Gitea
|
content = base64.b64encode(f.read()).decode()
|
||||||
RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
payload = json.dumps({
|
||||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
|
'content': content,
|
||||||
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
'sha': '${FILE_SHA}',
|
||||||
|
'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]',
|
||||||
if [ -z "$RELEASE_ID" ]; then
|
'branch': 'main'
|
||||||
# Create release
|
}).encode()
|
||||||
RELEASE_JSON=$(curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
req = urllib.request.Request(
|
||||||
-H "Content-Type: application/json" \
|
'${API_BASE}/contents/updates.xml',
|
||||||
"${API_BASE}/releases" \
|
data=payload, method='PUT',
|
||||||
-d "$(python3 -c "import json; print(json.dumps({
|
headers={
|
||||||
'tag_name': '${RELEASE_TAG}',
|
'Authorization': 'token ${GITEA_TOKEN}',
|
||||||
'name': '${RELEASE_TAG} (${DISPLAY_VERSION})',
|
'Content-Type': 'application/json'
|
||||||
'body': '${STABILITY} release',
|
})
|
||||||
'prerelease': True,
|
try:
|
||||||
'target_commitish': 'main'
|
urllib.request.urlopen(req)
|
||||||
}))")" 2>/dev/null || true)
|
print('updates.xml synced to main')
|
||||||
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
except Exception as e:
|
||||||
fi
|
print(f'WARNING: sync to main failed: {e}', file=sys.stderr)
|
||||||
|
"
|
||||||
if [ -n "$RELEASE_ID" ]; then
|
fi
|
||||||
# Delete existing assets with same name before uploading
|
|
||||||
ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
- name: SFTP deploy to dev server
|
||||||
"${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
|
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
|
||||||
for ASSET_FILE in "$PACKAGE_NAME" "$TAR_NAME"; do
|
env:
|
||||||
ASSET_ID=$(echo "$ASSETS" | python3 -c "
|
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
|
||||||
import sys,json
|
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
|
||||||
assets = json.load(sys.stdin)
|
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
||||||
for a in assets:
|
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||||
if a['name'] == '${ASSET_FILE}':
|
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
|
||||||
print(a['id']); break
|
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||||
" 2>/dev/null || true)
|
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||||
if [ -n "$ASSET_ID" ]; then
|
run: |
|
||||||
curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
# Permission check: admin or maintain role required
|
||||||
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
|
ACTOR="${{ github.actor }}"
|
||||||
fi
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
done
|
|
||||||
|
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
# Upload both formats
|
"${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
|
||||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
|
||||||
-H "Content-Type: application/octet-stream" \
|
case "$PERMISSION" in
|
||||||
--data-binary @"/tmp/${PACKAGE_NAME}" \
|
admin|maintain|write) ;;
|
||||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${PACKAGE_NAME}" > /dev/null 2>&1 || true
|
*)
|
||||||
|
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
|
||||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
exit 0
|
||||||
-H "Content-Type: application/octet-stream" \
|
;;
|
||||||
--data-binary @"/tmp/${TAR_NAME}" \
|
esac
|
||||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}" > /dev/null 2>&1 || true
|
|
||||||
fi
|
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
|
||||||
|
|
||||||
echo "Packages: ${PACKAGE_NAME} + ${TAR_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
|
SOURCE_DIR="src"
|
||||||
else
|
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||||
SHA256=""
|
[ ! -d "$SOURCE_DIR" ] && exit 0
|
||||||
fi
|
|
||||||
|
PORT="${DEV_PORT:-22}"
|
||||||
# -- Build the new entry (canonical format matching release.yml) --
|
REMOTE="${DEV_PATH%/}"
|
||||||
NEW_ENTRY=""
|
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
|
||||||
NEW_ENTRY="${NEW_ENTRY} <update>\n"
|
|
||||||
NEW_ENTRY="${NEW_ENTRY} <name>${EXT_NAME}</name>\n"
|
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||||
NEW_ENTRY="${NEW_ENTRY} <description>${EXT_NAME} ${STABILITY} build.</description>\n"
|
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
|
||||||
NEW_ENTRY="${NEW_ENTRY} <element>${EXT_ELEMENT}</element>\n"
|
if [ -n "$DEV_KEY" ]; then
|
||||||
NEW_ENTRY="${NEW_ENTRY} <type>${EXT_TYPE}</type>\n"
|
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
|
||||||
[ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n"
|
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||||
[ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n"
|
else
|
||||||
NEW_ENTRY="${NEW_ENTRY} <version>${VERSION}</version>\n"
|
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
||||||
NEW_ENTRY="${NEW_ENTRY} <creationDate>$(date +%Y-%m-%d)</creationDate>\n"
|
fi
|
||||||
NEW_ENTRY="${NEW_ENTRY} <infourl title='${EXT_NAME}'>https://git.mokoconsulting.tech/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${RELEASE_TAG}</infourl>\n"
|
|
||||||
NEW_ENTRY="${NEW_ENTRY} <downloads>\n"
|
PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true)
|
||||||
NEW_ENTRY="${NEW_ENTRY} <downloadurl type='full' format='zip'>${DOWNLOAD_URL}</downloadurl>\n"
|
if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then
|
||||||
NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
|
php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||||
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>${SHA256}</sha256>\n"
|
elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then
|
||||||
NEW_ENTRY="${NEW_ENTRY} <tags><tag>${STABILITY}</tag></tags>\n"
|
php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||||
NEW_ENTRY="${NEW_ENTRY} <maintainer>Moko Consulting</maintainer>\n"
|
fi
|
||||||
NEW_ENTRY="${NEW_ENTRY} <maintainerurl>https://mokoconsulting.tech</maintainerurl>\n"
|
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||||
NEW_ENTRY="${NEW_ENTRY} <targetplatform name='joomla' version='(5|6).*'/>\n"
|
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
|
||||||
[ -n "$PHP_MINIMUM" ] && NEW_ENTRY="${NEW_ENTRY} <php_minimum>${PHP_MINIMUM}</php_minimum>\n"
|
|
||||||
NEW_ENTRY="${NEW_ENTRY} </update>"
|
- name: Summary
|
||||||
|
if: always()
|
||||||
# -- Write new entry to temp file --------------------------------
|
run: |
|
||||||
printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
# -- Merge into updates.xml ----------------------------------------
|
DISPLAY="${{ steps.meta.outputs.display_version }}"
|
||||||
# Cascade: stable→all | rc→rc+lower | beta→beta+lower | alpha→alpha+dev | dev→dev
|
echo "## Update Server" >> $GITHUB_STEP_SUMMARY
|
||||||
CASCADE_MAP="stable:development,alpha,beta,rc,stable rc:development,alpha,beta,rc beta:development,alpha,beta alpha:development,alpha development:development"
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
TARGETS=""
|
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||||
for entry in $CASCADE_MAP; do
|
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||||
key="${entry%%:*}"
|
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
vals="${entry#*:}"
|
echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
if [ "$key" = "${STABILITY}" ]; then
|
|
||||||
TARGETS="$vals"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
[ -z "$TARGETS" ] && TARGETS="${STABILITY}"
|
|
||||||
|
|
||||||
echo "Cascade: ${STABILITY} → ${TARGETS}"
|
|
||||||
|
|
||||||
# Create updates.xml if missing
|
|
||||||
if [ ! -f "updates.xml" ]; then
|
|
||||||
printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>" > updates.xml
|
|
||||||
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting -->" >> updates.xml
|
|
||||||
printf '%s\n' "<updates>" >> updates.xml
|
|
||||||
printf '%s\n' "</updates>" >> updates.xml
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update existing blocks or create missing ones
|
|
||||||
export PY_TARGETS="$TARGETS" PY_VERSION="$VERSION" PY_DATE="$(date +%Y-%m-%d)"
|
|
||||||
python3 << 'PYEOF'
|
|
||||||
import re, os
|
|
||||||
|
|
||||||
targets = os.environ["PY_TARGETS"].split(",")
|
|
||||||
version = os.environ["PY_VERSION"]
|
|
||||||
date = os.environ["PY_DATE"]
|
|
||||||
|
|
||||||
with open("updates.xml") as f:
|
|
||||||
content = f.read()
|
|
||||||
with open("/tmp/new_entry.xml") as f:
|
|
||||||
new_entry_template = f.read()
|
|
||||||
|
|
||||||
for tag in targets:
|
|
||||||
tag = tag.strip()
|
|
||||||
# Build entry with this tag's name
|
|
||||||
new_entry = re.sub(r"<tag>[^<]*</tag>", f"<tag>{tag}</tag>", new_entry_template)
|
|
||||||
|
|
||||||
# Try to find existing block (handles both single-line and multi-line <tags>)
|
|
||||||
block_pattern = r"(<update>(?:(?!</update>).)*?<tag>" + re.escape(tag) + r"</tag>.*?</update>)"
|
|
||||||
match = re.search(block_pattern, content, re.DOTALL)
|
|
||||||
|
|
||||||
if match:
|
|
||||||
# Update in place — replace entire block
|
|
||||||
content = content.replace(match.group(1), new_entry.strip())
|
|
||||||
print(f" UPDATED: <tag>{tag}</tag> → {version}")
|
|
||||||
else:
|
|
||||||
# Create — insert before </updates>
|
|
||||||
content = content.replace("</updates>", "\n" + new_entry.strip() + "\n\n</updates>")
|
|
||||||
print(f" CREATED: <tag>{tag}</tag> → {version}")
|
|
||||||
|
|
||||||
# Clean up excessive blank lines
|
|
||||||
content = re.sub(r"\n{3,}", "\n\n", content)
|
|
||||||
|
|
||||||
with open("updates.xml", "w") as f:
|
|
||||||
f.write(content)
|
|
||||||
PYEOF
|
|
||||||
|
|
||||||
# Commit
|
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
git add updates.xml
|
|
||||||
git diff --cached --quiet || {
|
|
||||||
git commit -m "chore: update updates.xml (${STABILITY}: ${DISPLAY_VERSION}) [skip ci]" \
|
|
||||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
|
||||||
git push
|
|
||||||
}
|
|
||||||
|
|
||||||
# -- Sync updates.xml to main (for non-main branches) ----------------------
|
|
||||||
- name: Sync updates.xml to main
|
|
||||||
if: github.ref_name != 'main'
|
|
||||||
run: |
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
|
||||||
|
|
||||||
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
|
|
||||||
"${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
|
|
||||||
python3 -c "
|
|
||||||
import base64, json, urllib.request, sys
|
|
||||||
with open('updates.xml', 'rb') as f:
|
|
||||||
content = base64.b64encode(f.read()).decode()
|
|
||||||
payload = json.dumps({
|
|
||||||
'content': content,
|
|
||||||
'sha': '${FILE_SHA}',
|
|
||||||
'message': 'chore: sync updates.xml from ${STABILITY} [skip ci]',
|
|
||||||
'branch': 'main'
|
|
||||||
}).encode()
|
|
||||||
req = urllib.request.Request(
|
|
||||||
'${API_BASE}/contents/updates.xml',
|
|
||||||
data=payload, method='PUT',
|
|
||||||
headers={
|
|
||||||
'Authorization': 'token ${GA_TOKEN}',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
})
|
|
||||||
try:
|
|
||||||
urllib.request.urlopen(req)
|
|
||||||
print('updates.xml synced to main')
|
|
||||||
except Exception as e:
|
|
||||||
print(f'ERROR: failed to sync updates.xml to main: {e}', file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
" \
|
|
||||||
&& echo "updates.xml synced to main (${STABILITY})" >> $GITHUB_STEP_SUMMARY \
|
|
||||||
|| echo "::error::failed to sync updates.xml to main" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "::error::could not get updates.xml SHA from main — file may not exist on main yet" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: SFTP deploy to dev server
|
|
||||||
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
|
|
||||||
env:
|
|
||||||
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
|
|
||||||
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
|
|
||||||
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
|
||||||
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
|
|
||||||
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
|
|
||||||
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
|
|
||||||
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
# -- Permission check: admin or maintain role required --------
|
|
||||||
ACTOR="${{ github.actor }}"
|
|
||||||
REPO="${{ github.repository }}"
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
|
|
||||||
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
|
||||||
"${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
|
|
||||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
|
|
||||||
case "$PERMISSION" in
|
|
||||||
admin|maintain|write) ;;
|
|
||||||
*)
|
|
||||||
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
|
|
||||||
|
|
||||||
SOURCE_DIR="src"
|
|
||||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
|
||||||
[ ! -d "$SOURCE_DIR" ] && exit 0
|
|
||||||
|
|
||||||
PORT="${DEV_PORT:-22}"
|
|
||||||
REMOTE="${DEV_PATH%/}"
|
|
||||||
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
|
|
||||||
|
|
||||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
|
||||||
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
|
|
||||||
if [ -n "$DEV_KEY" ]; then
|
|
||||||
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
|
|
||||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
|
||||||
else
|
|
||||||
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
PLATFORM=$(php /tmp/moko-platform/cli/platform_detect.php --path . 2>/dev/null || true)
|
|
||||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform/deploy/deploy-joomla.php" ]; then
|
|
||||||
php /tmp/moko-platform/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
|
||||||
elif [ -f "/tmp/moko-platform/deploy/deploy-sftp.php" ]; then
|
|
||||||
php /tmp/moko-platform/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
|
||||||
fi
|
|
||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
|
||||||
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
- name: Validate updates.xml integrity
|
|
||||||
run: |
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
if [ ! -f "updates.xml" ]; then
|
|
||||||
echo "::error::updates.xml not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Well-formed XML
|
|
||||||
if ! python3 -c "import xml.etree.ElementTree as ET; ET.parse('updates.xml')" 2>/dev/null; then
|
|
||||||
echo "::error::updates.xml is not valid XML"
|
|
||||||
ERRORS=$((ERRORS+1))
|
|
||||||
fi
|
|
||||||
|
|
||||||
python3 << 'PYEOF'
|
|
||||||
import xml.etree.ElementTree as ET, sys, re, os
|
|
||||||
|
|
||||||
tree = ET.parse("updates.xml")
|
|
||||||
root = tree.getroot()
|
|
||||||
updates = root.findall("update")
|
|
||||||
errors = 0
|
|
||||||
warnings = 0
|
|
||||||
seen_tags = set()
|
|
||||||
|
|
||||||
# All 5 channels MUST be present
|
|
||||||
REQUIRED_CHANNELS = {"stable", "rc", "beta", "alpha", "dev"}
|
|
||||||
VALID_TAGS = REQUIRED_CHANNELS | {"development"} # accept legacy alias
|
|
||||||
REPO = os.environ.get("GITEA_REPO", "")
|
|
||||||
ORG = os.environ.get("GITEA_ORG", "MokoConsulting")
|
|
||||||
REPO_BASE = f"https://git.mokoconsulting.tech/{ORG}/"
|
|
||||||
|
|
||||||
# Gitea release tag names per channel (Moko standard)
|
|
||||||
RELEASE_TAG_MAP = {
|
|
||||||
"stable": "stable",
|
|
||||||
"rc": "release-candidate",
|
|
||||||
"beta": "beta",
|
|
||||||
"alpha": "alpha",
|
|
||||||
"dev": "development",
|
|
||||||
"development": "development",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Joomla update XML required fields per
|
|
||||||
# https://docs.joomla.org/Deploying_an_Update_Server
|
|
||||||
REQUIRED_FIELDS = ["name", "element", "type", "version", "infourl"]
|
|
||||||
|
|
||||||
for i, u in enumerate(updates):
|
|
||||||
tag_el = u.find("tags/tag")
|
|
||||||
tag = tag_el.text.strip() if tag_el is not None and tag_el.text else None
|
|
||||||
label = f"Entry {i+1} (<tag>{tag or '?'}</tag>)"
|
|
||||||
|
|
||||||
# -- Required Joomla fields --
|
|
||||||
for field in REQUIRED_FIELDS:
|
|
||||||
el = u.find(field)
|
|
||||||
if el is None or not (el.text or "").strip():
|
|
||||||
print(f"::error::{label}: missing required <{field}>")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- <downloads><downloadurl> --
|
|
||||||
dl = u.find("downloads/downloadurl")
|
|
||||||
if dl is None or not (dl.text or "").strip():
|
|
||||||
print(f"::error::{label}: missing <downloads><downloadurl>")
|
|
||||||
errors += 1
|
|
||||||
else:
|
|
||||||
dl_url = dl.text.strip()
|
|
||||||
# Must point to org repo
|
|
||||||
if REPO_BASE not in dl_url:
|
|
||||||
print(f"::error::{label}: download URL not under {REPO_BASE}: {dl_url}")
|
|
||||||
errors += 1
|
|
||||||
# Must end in .zip
|
|
||||||
if not dl_url.endswith(".zip"):
|
|
||||||
print(f"::error::{label}: download URL must end in .zip: {dl_url}")
|
|
||||||
errors += 1
|
|
||||||
# Must use correct Gitea release tag in path
|
|
||||||
if tag and tag in RELEASE_TAG_MAP:
|
|
||||||
expected_tag = RELEASE_TAG_MAP[tag]
|
|
||||||
if f"/download/{expected_tag}/" not in dl_url:
|
|
||||||
print(f"::error::{label}: download URL should contain /download/{expected_tag}/ but got: {dl_url}")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- <client> (required for Joomla to match update) --
|
|
||||||
client = u.find("client")
|
|
||||||
if client is None or not (client.text or "").strip():
|
|
||||||
print(f"::error::{label}: missing <client> (required for Joomla update matching)")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- <targetplatform> --
|
|
||||||
tp = u.find("targetplatform")
|
|
||||||
if tp is None:
|
|
||||||
print(f"::error::{label}: missing <targetplatform>")
|
|
||||||
errors += 1
|
|
||||||
else:
|
|
||||||
tp_name = tp.get("name", "")
|
|
||||||
tp_ver = tp.get("version", "")
|
|
||||||
if tp_name != "joomla":
|
|
||||||
print(f"::error::{label}: targetplatform name should be 'joomla', got '{tp_name}'")
|
|
||||||
errors += 1
|
|
||||||
if not tp_ver:
|
|
||||||
print(f"::error::{label}: targetplatform missing version regex")
|
|
||||||
errors += 1
|
|
||||||
elif "5" not in tp_ver or "6" not in tp_ver:
|
|
||||||
print(f"::warning::{label}: targetplatform version may not cover Joomla 5+6: {tp_ver}")
|
|
||||||
warnings += 1
|
|
||||||
|
|
||||||
# -- <type> must be valid Joomla type --
|
|
||||||
type_el = u.find("type")
|
|
||||||
if type_el is not None and type_el.text:
|
|
||||||
valid_types = {"component", "module", "plugin", "template", "library", "package", "file"}
|
|
||||||
if type_el.text.strip() not in valid_types:
|
|
||||||
print(f"::error::{label}: invalid type '{type_el.text}' (expected: {valid_types})")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- <version> format (XX.YY.ZZ with optional suffix) --
|
|
||||||
ver_el = u.find("version")
|
|
||||||
if ver_el is not None and ver_el.text:
|
|
||||||
if not re.match(r"^\d{2}\.\d{2}\.\d{2}(-\w+)?$", ver_el.text.strip()):
|
|
||||||
print(f"::warning::{label}: version '{ver_el.text}' does not match XX.YY.ZZ format")
|
|
||||||
warnings += 1
|
|
||||||
|
|
||||||
# -- <maintainer> and <maintainerurl> --
|
|
||||||
for field in ["maintainer", "maintainerurl"]:
|
|
||||||
el = u.find(field)
|
|
||||||
if el is None or not (el.text or "").strip():
|
|
||||||
print(f"::warning::{label}: missing <{field}>")
|
|
||||||
warnings += 1
|
|
||||||
|
|
||||||
# -- Valid stability tag --
|
|
||||||
if tag is None:
|
|
||||||
print(f"::error::{label}: missing <tags><tag>")
|
|
||||||
errors += 1
|
|
||||||
elif tag not in VALID_TAGS:
|
|
||||||
print(f"::error::{label}: invalid tag '{tag}' (expected: {VALID_TAGS})")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- Duplicate tag check --
|
|
||||||
norm_tag = "dev" if tag == "development" else tag
|
|
||||||
if norm_tag in seen_tags:
|
|
||||||
print(f"::error::{label}: duplicate channel '{tag}'")
|
|
||||||
errors += 1
|
|
||||||
if norm_tag:
|
|
||||||
seen_tags.add(norm_tag)
|
|
||||||
|
|
||||||
# -- All 5 channels must exist --
|
|
||||||
missing = REQUIRED_CHANNELS - seen_tags
|
|
||||||
if missing:
|
|
||||||
print(f"::error::Missing required update channels: {', '.join(sorted(missing))}")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- Version ordering: higher stability must not exceed dev version --
|
|
||||||
channel_versions = {}
|
|
||||||
for u in updates:
|
|
||||||
tag_el = u.find("tags/tag")
|
|
||||||
ver_el = u.find("version")
|
|
||||||
if tag_el is not None and ver_el is not None and tag_el.text and ver_el.text:
|
|
||||||
norm = "dev" if tag_el.text.strip() == "development" else tag_el.text.strip()
|
|
||||||
# Strip suffix for comparison (01.00.18-dev -> 01.00.18)
|
|
||||||
base_ver = re.sub(r"-\w+$", "", ver_el.text.strip())
|
|
||||||
channel_versions[norm] = base_ver
|
|
||||||
|
|
||||||
# Cascade check: dev >= alpha >= beta >= rc >= stable
|
|
||||||
ORDER = ["dev", "alpha", "beta", "rc", "stable"]
|
|
||||||
for j in range(1, len(ORDER)):
|
|
||||||
current = ORDER[j]
|
|
||||||
previous = ORDER[j - 1]
|
|
||||||
if current in channel_versions and previous in channel_versions:
|
|
||||||
if channel_versions[current] > channel_versions[previous]:
|
|
||||||
print(f"::error::{current} version ({channel_versions[current]}) is ahead of {previous} ({channel_versions[previous]})")
|
|
||||||
errors += 1
|
|
||||||
|
|
||||||
# -- Summary --
|
|
||||||
print(f"\nupdates.xml validation: {len(updates)} entries, {errors} error(s), {warnings} warning(s)")
|
|
||||||
if errors > 0:
|
|
||||||
sys.exit(1)
|
|
||||||
PYEOF
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "## Joomla Update Server" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Version | \`${DISPLAY_VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Element | \`${EXT_ELEMENT}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Download | [ZIP](${DOWNLOAD_URL}) |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|||||||
+100
-252
@@ -1,293 +1,141 @@
|
|||||||
# Contribution Guidelines
|
# Contributing to Moko Consulting Projects
|
||||||
|
|
||||||
This document explains how to contribute changes to the Gitea project. Topic-specific guides live in separate files so the essentials are easier to find.
|
Thank you for your interest in contributing. All Moko Consulting repositories follow this universal workflow and version policy.
|
||||||
|
|
||||||
| Topic | Document |
|
## Branching Workflow
|
||||||
| :---- | :------- |
|
|
||||||
| Backend (Go modules, API v1) | [docs/guideline-backend.md](docs/guideline-backend.md) |
|
|
||||||
| Frontend (npm, UI guidelines) | [docs/guideline-frontend.md](docs/guideline-frontend.md) |
|
|
||||||
| Maintainers, TOC, labels, merge queue, commit format for mergers | [docs/community-governance.md](docs/community-governance.md) |
|
|
||||||
| Release cycle, backports, tagging releases | [docs/release-management.md](docs/release-management.md) |
|
|
||||||
|
|
||||||
<details><summary>Table of Contents</summary>
|
|
||||||
|
|
||||||
- [Contribution Guidelines](#contribution-guidelines)
|
|
||||||
- [Introduction](#introduction)
|
|
||||||
- [AI Contribution Policy](#ai-contribution-policy)
|
|
||||||
- [Issues](#issues)
|
|
||||||
- [How to report issues](#how-to-report-issues)
|
|
||||||
- [Types of issues](#types-of-issues)
|
|
||||||
- [Discuss your design before the implementation](#discuss-your-design-before-the-implementation)
|
|
||||||
- [Issue locking](#issue-locking)
|
|
||||||
- [Building Gitea](#building-gitea)
|
|
||||||
- [Styleguide](#styleguide)
|
|
||||||
- [Copyright](#copyright)
|
|
||||||
- [Testing](#testing)
|
|
||||||
- [Translation](#translation)
|
|
||||||
- [Code review](#code-review)
|
|
||||||
- [Pull request format](#pull-request-format)
|
|
||||||
- [PR title and summary](#pr-title-and-summary)
|
|
||||||
- [Breaking PRs](#breaking-prs)
|
|
||||||
- [What is a breaking PR?](#what-is-a-breaking-pr)
|
|
||||||
- [How to handle breaking PRs?](#how-to-handle-breaking-prs)
|
|
||||||
- [Maintaining open PRs](#maintaining-open-prs)
|
|
||||||
- [Reviewing PRs](#reviewing-prs)
|
|
||||||
- [For PR authors](#for-pr-authors)
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
It assumes you have followed the [installation instructions](https://docs.gitea.com/category/installation). \
|
|
||||||
Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io).
|
|
||||||
|
|
||||||
For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/).
|
|
||||||
|
|
||||||
## AI Contribution Policy
|
|
||||||
|
|
||||||
Contributions made with the assistance of AI tools are welcome, but contributors must use them responsibly and disclose that use clearly.
|
|
||||||
|
|
||||||
1. Review AI-generated code closely before marking a pull request ready for review.
|
|
||||||
2. Manually test the changes and add appropriate automated tests where feasible.
|
|
||||||
3. Only use AI to assist in contributions that you understand well enough to explain, defend, and revise yourself during review.
|
|
||||||
4. Disclose AI-assisted content clearly.
|
|
||||||
5. Do not use AI to reply to questions about your issue or pull request. The questions are for you, not an AI model.
|
|
||||||
6. AI may be used to help draft issues and pull requests, but contributors remain responsible for the accuracy, completeness, and intent of what they submit.
|
|
||||||
|
|
||||||
Maintainers reserve the right to close pull requests and issues that do not disclose AI assistance, that appear to be low-quality AI-generated content, or where the contributor cannot explain or defend the proposed changes themselves.
|
|
||||||
|
|
||||||
We welcome new contributors, but cannot sustain the effort of supporting contributors who primarily defer to AI rather than engaging substantively with the review process.
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
### How to report issues
|
|
||||||
|
|
||||||
Please search the issues on the issue tracker with a variety of related keywords to ensure that your issue has not already been reported.
|
|
||||||
|
|
||||||
If your issue has not been reported yet, [open an issue](https://github.com/go-gitea/gitea/issues/new)
|
|
||||||
and answer the questions so we can understand and reproduce the problematic behavior. \
|
|
||||||
Please write clear and concise instructions so that we can reproduce the behavior — even if it seems obvious. \
|
|
||||||
The more detailed and specific you are, the faster we can fix the issue. \
|
|
||||||
It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://demo.gitea.com>, as perhaps your problem has already been fixed on a current version. \
|
|
||||||
Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report.
|
|
||||||
|
|
||||||
Please be kind—remember that Gitea comes at no cost to you, and you're getting free help.
|
|
||||||
|
|
||||||
### Types of issues
|
|
||||||
|
|
||||||
Typically, issues fall in one of the following categories:
|
|
||||||
|
|
||||||
- `bug`: Something in the frontend or backend behaves unexpectedly
|
|
||||||
- `security issue`: bug that has serious implications such as leaking another users data. Please do not file such issues on the public tracker and send a mail to security@gitea.io instead
|
|
||||||
- `feature`: Completely new functionality. You should describe this feature in enough detail that anyone who reads the issue can understand how it is supposed to be implemented
|
|
||||||
- `enhancement`: An existing feature should get an upgrade
|
|
||||||
- `refactoring`: Parts of the code base don't conform with other parts and should be changed to improve Gitea's maintainability
|
|
||||||
|
|
||||||
### Discuss your design before the implementation
|
|
||||||
|
|
||||||
We welcome submissions. \
|
|
||||||
If you want to change or add something, please let everyone know what you're working on — [file an issue](https://github.com/go-gitea/gitea/issues/new) or comment on an existing one before starting your work!
|
|
||||||
|
|
||||||
Significant changes such as new features must go through the change proposal process before they can be accepted. \
|
|
||||||
This is mainly to save yourself the trouble of implementing it, only to find out that your proposed implementation has some potential problems. \
|
|
||||||
Furthermore, this process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside
|
|
||||||
the goals for the project and tools.
|
|
||||||
|
|
||||||
Pull requests should not be the place for architecture discussions.
|
|
||||||
|
|
||||||
### Issue locking
|
|
||||||
|
|
||||||
Commenting on closed or merged issues/PRs is strongly discouraged.
|
|
||||||
Such comments will likely be overlooked as some maintainers may not view notifications on closed issues, thinking that the item is resolved.
|
|
||||||
As such, commenting on closed/merged issues/PRs may be disabled prior to the scheduled auto-locking if a discussion starts or if unrelated comments are posted.
|
|
||||||
If further discussion is needed, we encourage you to open a new issue instead and we recommend linking to the issue/PR in question for context.
|
|
||||||
|
|
||||||
## Building Gitea
|
|
||||||
|
|
||||||
See the [development setup instructions](https://docs.gitea.com/development/hacking-on-gitea).
|
|
||||||
|
|
||||||
## Styleguide
|
|
||||||
|
|
||||||
You should always run `make fmt` before committing to conform to Gitea's styleguide.
|
|
||||||
|
|
||||||
## Copyright
|
|
||||||
|
|
||||||
New code files that you contribute should use the standard copyright header:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
// Copyright <current year> The Gitea Authors. All rights reserved.
|
feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge──> main
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Afterwards, copyright should only be modified when the copyright author changes.
|
### Step by step
|
||||||
|
|
||||||
## Testing
|
1. **Create a feature branch** from `dev`:
|
||||||
|
```bash
|
||||||
|
git checkout dev && git pull
|
||||||
|
git checkout -b feature/my-change
|
||||||
|
```
|
||||||
|
|
||||||
Before submitting a pull request, run all tests to make sure your changes don't cause a regression elsewhere.
|
2. **Work and commit** on your feature branch. Push to origin.
|
||||||
|
|
||||||
Here's how to run the test suite:
|
3. **Open a PR**: `feature/my-change` → `dev`. After review and checks, merge it.
|
||||||
|
|
||||||
- code lint
|
4. **When ready for release**, open a **draft PR**: `dev` → `main`.
|
||||||
|
- This automatically renames the source branch to `rc` (release candidate)
|
||||||
|
- An RC pre-release is built and uploaded
|
||||||
|
|
||||||
| | |
|
5. **Alpha and beta branches** are created by manually renaming the branch before the RC stage:
|
||||||
| :-------------------- | :--------------------------------------------------------------------------- |
|
- Rename `dev` to `alpha` for early testing → alpha pre-release is built
|
||||||
|``make lint`` | lint everything (not needed if you only change the front- **or** backend) |
|
- Rename `alpha` to `beta` for feature-complete testing → beta pre-release is built
|
||||||
|``make lint-frontend`` | lint frontend files |
|
- When the draft PR is created, the branch is renamed to `rc`
|
||||||
|``make lint-backend`` | lint backend files |
|
|
||||||
|
|
||||||
- run tests (we suggest running them on Linux)
|
6. **Once PR checks pass** on the `rc` branch, mark the PR as ready and merge to `main`.
|
||||||
|
|
||||||
| Command | Action | |
|
7. **Merging to main** triggers the stable release pipeline:
|
||||||
|:----------------------------------------------|:-----------------------------------------------------| ------------------------------------------- |
|
- Minor version bump (e.g., `02.09.xx` → `02.10.00`)
|
||||||
| ``make test-backend[\#SpecificTestName]`` | run unit test(s) | |
|
- Stability suffix stripped (clean version)
|
||||||
| ``make test-integration[\#SpecificTestName]`` | run [integration](tests/integration) test(s) | [More details](tests/integration/README.md) |
|
- Gitea release created with ZIP/tar.gz packages
|
||||||
| ``make test-e2e`` | run [end-to-end](tests/e2e) test(s) using Playwright | |
|
- `updates.xml` updated (Joomla extensions)
|
||||||
|
- `dev` branch recreated from `main`
|
||||||
|
|
||||||
- E2E test environment variables
|
### Branch summary
|
||||||
|
|
||||||
| Variable | Description |
|
| Branch | Purpose | Created by |
|
||||||
| :-------------------------------- | :---------------------------------------------------------- |
|
|--------|---------|-----------|
|
||||||
| ``GITEA_TEST_E2E_DEBUG`` | When set, show Gitea server output |
|
| `feature/*` | New features and fixes | Developer |
|
||||||
| ``GITEA_TEST_E2E_FLAGS`` | Additional flags passed to Playwright, for example ``--ui`` |
|
| `dev` | Integration branch | Auto-recreated after release |
|
||||||
| ``GITEA_TEST_E2E_TIMEOUT_FACTOR`` | Timeout multiplier (default: 4 on CI, 1 locally) |
|
| `alpha` | Alpha pre-release testing | Manual rename from `dev` |
|
||||||
|
| `beta` | Beta pre-release testing | Manual rename from `alpha` |
|
||||||
|
| `rc` | Release candidate | Auto-renamed on draft PR to main |
|
||||||
|
| `main` | Stable releases | Protected, merge only |
|
||||||
|
| `version/XX.YY.ZZ` | Archived release snapshots | Auto-created by CI |
|
||||||
|
|
||||||
## Translation
|
### Protected branches
|
||||||
|
|
||||||
All translation work happens on [Crowdin](https://translate.gitea.com).
|
| Branch | Direct push | Merge via |
|
||||||
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.json).
|
|--------|------------|-----------|
|
||||||
It is synced regularly with Crowdin. \
|
| `main` | Blocked (CI bot whitelisted) | PR merge only |
|
||||||
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
| `dev` | Blocked (CI bot whitelisted) | PR merge from feature/* |
|
||||||
Once a language has reached a **satisfactory percentage** of translated keys (~25%), it will be synced back into this repo and included in the next released version.
|
| `rc` | Blocked (CI bot whitelisted) | Auto-created on draft PR |
|
||||||
|
| `alpha` | Blocked (CI bot whitelisted) | Manual rename |
|
||||||
|
| `beta` | Blocked (CI bot whitelisted) | Manual rename |
|
||||||
|
| `feature/*` | Open | N/A (source branch) |
|
||||||
|
|
||||||
The tool `go run build/backport-locale.go` can be used to backport locales from the main branch to release branches that were missed.
|
## Version Policy
|
||||||
|
|
||||||
## Code review
|
### Format
|
||||||
|
|
||||||
How labels, milestones, and the merge queue work is documented in [docs/community-governance.md](docs/community-governance.md).
|
All versions use `XX.YY.ZZ` — three two-digit segments, zero-padded:
|
||||||
|
|
||||||
### Pull request format
|
- **XX** — Major version (breaking changes)
|
||||||
|
- **YY** — Minor version (new features, bumped on release to main)
|
||||||
|
- **ZZ** — Patch version (auto-incremented on every push to dev/feature branches)
|
||||||
|
|
||||||
Please try to make your pull request easy to review for us. \
|
Rollover: patch `99` → `00` increments minor; minor `99` → `00` increments major.
|
||||||
For that, please read the [*Best Practices for Faster Reviews*](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) guide. \
|
|
||||||
It has lots of useful tips for any project you may want to contribute to. \
|
|
||||||
Some of the key points:
|
|
||||||
|
|
||||||
- Make small pull requests. \
|
### Stability suffixes
|
||||||
The smaller, the faster to review and the more likely it will be merged soon.
|
|
||||||
- Don't make changes unrelated to your PR. \
|
|
||||||
Maybe there are typos on some comments, maybe refactoring would be welcome on a function... \
|
|
||||||
but if that is not related to your PR, please make *another* PR for that.
|
|
||||||
- Split big pull requests into multiple small ones. \
|
|
||||||
An incremental change will be faster to review than a huge PR.
|
|
||||||
- Allow edits by maintainers. This way, the maintainers will take care of merging the PR later on instead of you.
|
|
||||||
|
|
||||||
### PR title and summary
|
Each branch appends a suffix to indicate stability:
|
||||||
|
|
||||||
In the PR title, describe the problem you are fixing, not how you are fixing it. \
|
| Branch | Suffix | Example |
|
||||||
Use the first comment as a summary of your PR. \
|
|--------|--------|---------|
|
||||||
In the PR summary, you can describe exactly how you are fixing this problem.
|
| `main` | (none) | `02.09.00` |
|
||||||
|
| `dev` | `-dev` | `02.09.01-dev` |
|
||||||
|
| `feature/*` | `-dev` | `02.09.01-dev` |
|
||||||
|
| `alpha` | `-alpha` | `02.09.01-alpha` |
|
||||||
|
| `beta` | `-beta` | `02.09.01-beta` |
|
||||||
|
| `rc` | `-rc` | `02.09.01-rc` |
|
||||||
|
|
||||||
PR titles must follow the [Conventional Commits](https://www.conventionalcommits.org/) format, because PRs are squash-merged and the PR title becomes the resulting commit message:
|
### Auto version bump
|
||||||
|
|
||||||
```text
|
On every push to `dev`, `alpha`, `beta`, `rc`, or `feature/*`:
|
||||||
type(scope)!: subject
|
|
||||||
```
|
|
||||||
|
|
||||||
The allowed types are `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, and `test`. The generic `chore` type is intentionally not accepted; pick a more descriptive type instead.
|
1. Patch version incremented
|
||||||
|
2. Stability suffix applied based on branch name
|
||||||
|
3. All version-bearing files updated (manifests, CHANGELOG, PHP headers, etc.)
|
||||||
|
4. Commit created with `[skip ci]` to avoid loops
|
||||||
|
|
||||||
Examples:
|
### Version files
|
||||||
|
|
||||||
```text
|
The version tools update all files containing version stamps:
|
||||||
fix(web): prevent avatar upload crash on empty file
|
|
||||||
feat(api): add pagination to repo hooks list
|
|
||||||
ci(workflows): lint PR titles with commitlint
|
|
||||||
```
|
|
||||||
|
|
||||||
Keep this summary up-to-date as the PR evolves. \
|
- `.mokogitea/manifest.xml` (canonical source)
|
||||||
If your PR changes the UI, you must add **after** screenshots in the PR summary. \
|
- Joomla XML manifests (`<version>` tag)
|
||||||
If you are not implementing a new feature, you should also post **before** screenshots for comparison.
|
- `README.md`, `CHANGELOG.md` (`VERSION:` pattern)
|
||||||
|
- `package.json`, `pyproject.toml`
|
||||||
|
- Any text file with a `VERSION: XX.YY.ZZ` label
|
||||||
|
|
||||||
If you are implementing a new feature, your PR will only be merged if your screenshots are up to date.\
|
Files synced from other repos (with a `# REPO:` header) are not touched.
|
||||||
Furthermore, feature PRs will only be merged if their summary contains a clear usage description (understandable for users) and testing description (understandable for reviewers).
|
|
||||||
You should strive to combine both into a single description.
|
|
||||||
|
|
||||||
Another requirement for merging PRs is that the PR is labeled correctly.\
|
## Code Standards
|
||||||
However, this is not your job as a contributor, but the job of the person merging your PR.\
|
|
||||||
If you think that your PR was labeled incorrectly, or notice that it was merged without labels, please let us know.
|
|
||||||
|
|
||||||
If your PR closes some issues, you must note that in a way that both GitHub and Gitea understand, i.e. by appending a paragraph like
|
- **PHP**: PSR-12, tabs for indentation
|
||||||
|
- **Copyright**: all files must include the Moko Consulting copyright header
|
||||||
|
- **License**: SPDX identifier `GPL-3.0-or-later` (or as specified per repo)
|
||||||
|
- **Attribution**: use `Authored-by: Moko Consulting` in commits, not individual names
|
||||||
|
|
||||||
```text
|
## Commit Messages
|
||||||
Fixes/Closes/Resolves #<ISSUE_NR_X>.
|
|
||||||
Fixes/Closes/Resolves #<ISSUE_NR_Y>.
|
|
||||||
```
|
|
||||||
|
|
||||||
to your summary. \
|
Use conventional commit format:
|
||||||
Each issue that will be closed must stand on a separate line.
|
|
||||||
|
|
||||||
### Breaking PRs
|
|
||||||
|
|
||||||
#### What is a breaking PR?
|
|
||||||
|
|
||||||
A PR is breaking if it meets one of the following criteria:
|
|
||||||
|
|
||||||
- It changes API output in an incompatible way for existing users
|
|
||||||
- It removes a setting that an admin could previously set (i.e. via `app.ini`)
|
|
||||||
- An admin must do something manually to restore the old behavior
|
|
||||||
|
|
||||||
In particular, this means that adding new settings is not breaking.\
|
|
||||||
Changing the default value of a setting or replacing the setting with another one is breaking, however.
|
|
||||||
|
|
||||||
#### How to handle breaking PRs?
|
|
||||||
|
|
||||||
If your PR has a breaking change, you must add two things to the summary of your PR:
|
|
||||||
|
|
||||||
1. A reasoning why this breaking change is necessary
|
|
||||||
2. A `BREAKING` section explaining in simple terms (understandable for a typical user) how this PR affects users and how to mitigate these changes. This section can look for example like
|
|
||||||
|
|
||||||
```md
|
|
||||||
## :warning: BREAKING :warning:
|
|
||||||
```
|
|
||||||
|
|
||||||
Breaking PRs will not be merged as long as not both of these requirements are met.
|
|
||||||
|
|
||||||
### Maintaining open PRs
|
|
||||||
|
|
||||||
Code review starts when you open a non-draft PR or move a draft out of draft state. After that, do not rebase or squash your branch; it makes new changes harder to review.
|
|
||||||
|
|
||||||
Merge the base branch into yours only when you need to, for example because of conflicting changes elsewhere. That limits unnecessary CI runs.
|
|
||||||
|
|
||||||
Every PR is squash-merged, so merge commits on your branch do not matter for final history. The squash produces a single commit; mergers follow the [commit message format](docs/community-governance.md#commit-messages) in the governance guide.
|
|
||||||
|
|
||||||
### Reviewing PRs
|
|
||||||
|
|
||||||
Maintainers are encouraged to review pull requests in areas where they have expertise or particular interest.
|
|
||||||
|
|
||||||
#### For PR authors
|
|
||||||
|
|
||||||
- **Response**: When answering reviewer questions, use real-world cases or examples and avoid speculation.
|
|
||||||
- **Discussion**: A discussion is always welcome and should be used to clarify the changes and the intent of the PR.
|
|
||||||
- **Help**: If you need help with the PR or comments are unclear, ask for clarification.
|
|
||||||
|
|
||||||
Guidance for reviewers, the merge queue, and the squash commit message format is in [docs/community-governance.md](docs/community-governance.md).
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
If you add a new feature or change an existing aspect of Gitea, the documentation for that feature must be created or updated in another PR at [https://gitea.com/gitea/docs](https://gitea.com/gitea/docs).
|
|
||||||
**The docs directory on main repository will be removed at some time. We will have a yaml file to store configuration file's meta data. After that completed, configuration documentation should be in the main repository.**
|
|
||||||
|
|
||||||
## Developer Certificate of Origin (DCO)
|
|
||||||
|
|
||||||
We consider the act of contributing to the code by submitting a Pull Request as the "Sign off" or agreement to the certifications and terms of the [DCO](DCO) and [MIT license](LICENSE). \
|
|
||||||
No further action is required. \
|
|
||||||
You can also decide to sign off your commits by adding the following line at the end of your commit messages:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
type(scope): short description
|
||||||
|
|
||||||
|
Optional body with context.
|
||||||
|
|
||||||
|
Authored-by: Moko Consulting
|
||||||
```
|
```
|
||||||
|
|
||||||
If you set the `user.name` and `user.email` Git config options, you can add the line to the end of your commits automatically with `git commit -s`.
|
Types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `ci`
|
||||||
|
|
||||||
We assume in good faith that the information you provide is legally binding.
|
Special flags in commit messages:
|
||||||
|
- `[skip ci]` — skip all CI workflows
|
||||||
|
- `[skip bump]` — skip auto version bump only
|
||||||
|
|
||||||
|
## Reporting Issues
|
||||||
|
|
||||||
|
Use the repository's issue tracker with the appropriate template.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Moko Consulting <hello@mokoconsulting.tech>*
|
||||||
|
|||||||
@@ -34,16 +34,19 @@ const (
|
|||||||
swaggerSpecPath = "templates/swagger/v1_json.tmpl"
|
swaggerSpecPath = "templates/swagger/v1_json.tmpl"
|
||||||
openapi3OutPath = "templates/swagger/v1_openapi3_json.tmpl"
|
openapi3OutPath = "templates/swagger/v1_openapi3_json.tmpl"
|
||||||
|
|
||||||
appSubUrlVar = "{{.SwaggerAppSubUrl}}"
|
appSubUrlVar = "{{.SwaggerAppSubUrl}}"
|
||||||
appVerVar = "{{.SwaggerAppVer}}"
|
appVerVar = "{{.SwaggerAppVer}}"
|
||||||
|
appNameVar = "{{.SwaggerAppName}}"
|
||||||
|
|
||||||
appSubUrlPlaceholder = "GITEA_APP_SUB_URL_PLACEHOLDER"
|
appSubUrlPlaceholder = "GITEA_APP_SUB_URL_PLACEHOLDER"
|
||||||
appVerPlaceholder = "0.0.0-gitea-placeholder"
|
appVerPlaceholder = "0.0.0-gitea-placeholder"
|
||||||
|
appNamePlaceholder = "GiteaAppNamePlaceholder"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
appSubUrlRe = regexp.MustCompile(regexp.QuoteMeta(appSubUrlVar))
|
appSubUrlRe = regexp.MustCompile(regexp.QuoteMeta(appSubUrlVar))
|
||||||
appVerRe = regexp.MustCompile(regexp.QuoteMeta(appVerVar))
|
appVerRe = regexp.MustCompile(regexp.QuoteMeta(appVerVar))
|
||||||
|
appNameRe = regexp.MustCompile(regexp.QuoteMeta(appNameVar))
|
||||||
|
|
||||||
enumScanDirs = []string{
|
enumScanDirs = []string{
|
||||||
"modules/structs",
|
"modules/structs",
|
||||||
@@ -70,6 +73,7 @@ func main() {
|
|||||||
|
|
||||||
cleaned := appSubUrlRe.ReplaceAll(data, []byte(appSubUrlPlaceholder))
|
cleaned := appSubUrlRe.ReplaceAll(data, []byte(appSubUrlPlaceholder))
|
||||||
cleaned = appVerRe.ReplaceAll(cleaned, []byte(appVerPlaceholder))
|
cleaned = appVerRe.ReplaceAll(cleaned, []byte(appVerPlaceholder))
|
||||||
|
cleaned = appNameRe.ReplaceAll(cleaned, []byte(appNamePlaceholder))
|
||||||
|
|
||||||
oas3, err := openapi3gen.Convert(cleaned, astEnumMap)
|
oas3, err := openapi3gen.Convert(cleaned, astEnumMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,6 +91,7 @@ func main() {
|
|||||||
|
|
||||||
result := strings.ReplaceAll(string(out), appSubUrlPlaceholder, appSubUrlVar)
|
result := strings.ReplaceAll(string(out), appSubUrlPlaceholder, appSubUrlVar)
|
||||||
result = strings.ReplaceAll(result, appVerPlaceholder, appVerVar)
|
result = strings.ReplaceAll(result, appVerPlaceholder, appVerVar)
|
||||||
|
result = strings.ReplaceAll(result, appNamePlaceholder, appNameVar)
|
||||||
result = strings.TrimSpace(result)
|
result = strings.TrimSpace(result)
|
||||||
|
|
||||||
if err := os.WriteFile(openapi3OutPath, []byte(result), 0o644); err != nil {
|
if err := os.WriteFile(openapi3OutPath, []byte(result), 0o644); err != nil {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,6 +68,21 @@ func TestLocaleStore(t *testing.T) {
|
|||||||
assert.Equal(t, "<no-such>", string(res))
|
assert.Equal(t, "<no-such>", string(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLocaleAppNameSubstitution(t *testing.T) {
|
||||||
|
setting.AppName = "TestApp"
|
||||||
|
|
||||||
|
ls := NewLocaleStore()
|
||||||
|
assert.NoError(t, ls.AddLocaleByJSON("lang1", "Lang1", []byte(`{"greeting":"Welcome to ${APP_NAME}","plain":"No placeholder here"}`), nil))
|
||||||
|
lang1, _ := ls.Locale("lang1")
|
||||||
|
|
||||||
|
assert.Equal(t, "Welcome to TestApp", lang1.TrString("greeting"))
|
||||||
|
assert.Equal(t, "No placeholder here", lang1.TrString("plain"))
|
||||||
|
|
||||||
|
// Verify it responds to runtime AppName changes
|
||||||
|
setting.AppName = "ChangedApp"
|
||||||
|
assert.Equal(t, "Welcome to ChangedApp", lang1.TrString("greeting"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestLocaleStoreMoreSource(t *testing.T) {
|
func TestLocaleStoreMoreSource(t *testing.T) {
|
||||||
testData1 := []byte(`
|
testData1 := []byte(`
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import (
|
|||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/json"
|
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/json"
|
||||||
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
|
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
|
||||||
|
"git.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file implements the static LocaleStore that will not watch for changes
|
// This file implements the static LocaleStore that will not watch for changes
|
||||||
@@ -142,6 +144,9 @@ func (l *locale) TrString(trKey string, trArgs ...any) string {
|
|||||||
if format == "" { // still missing, use the key itself
|
if format == "" { // still missing, use the key itself
|
||||||
format = html.EscapeString(trKey)
|
format = html.EscapeString(trKey)
|
||||||
}
|
}
|
||||||
|
if strings.Contains(format, "${APP_NAME}") {
|
||||||
|
format = strings.ReplaceAll(format, "${APP_NAME}", setting.AppName)
|
||||||
|
}
|
||||||
msg, err := Format(format, trArgs...)
|
msg, err := Format(format, trArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error whilst formatting %q in %s: %v", trKey, l.langName, err)
|
log.Error("Error whilst formatting %q in %s: %v", trKey, l.langName, err)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"enable_javascript": "This website requires JavaScript.",
|
"enable_javascript": "This website requires JavaScript.",
|
||||||
"toc": "Table of Contents",
|
"toc": "Table of Contents",
|
||||||
"licenses": "Licenses",
|
"licenses": "Licenses",
|
||||||
"return_to_gitea": "Return to MokoGitea",
|
"return_to_gitea": "Return to ${APP_NAME}",
|
||||||
"more_items": "More items",
|
"more_items": "More items",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"email": "Email Address",
|
"email": "Email Address",
|
||||||
@@ -222,7 +222,7 @@
|
|||||||
"filter.string.asc": "A–Z",
|
"filter.string.asc": "A–Z",
|
||||||
"filter.string.desc": "Z–A",
|
"filter.string.desc": "Z–A",
|
||||||
"error.occurred": "An error occurred",
|
"error.occurred": "An error occurred",
|
||||||
"error.report_message": "If you believe that this is a MokoGitea bug, please search for issues on <a href=\"%s\" target=\"_blank\">GitHub</a> or open a new issue if necessary.",
|
"error.report_message": "If you believe that this is a ${APP_NAME} bug, please search for issues on <a href=\"%s\" target=\"_blank\">GitHub</a> or open a new issue if necessary.",
|
||||||
"error.not_found": "The target couldn't be found.",
|
"error.not_found": "The target couldn't be found.",
|
||||||
"error.permission_denied": "Permission denied.",
|
"error.permission_denied": "Permission denied.",
|
||||||
"error.network_error": "Network error",
|
"error.network_error": "Network error",
|
||||||
@@ -230,16 +230,16 @@
|
|||||||
"startpage.install": "Easy to install",
|
"startpage.install": "Easy to install",
|
||||||
"startpage.install_desc": "Simply <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[1]s\">run the binary</a> for your platform, ship it with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[2]s\">Docker</a>, or get it <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[3]s\">packaged</a>.",
|
"startpage.install_desc": "Simply <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[1]s\">run the binary</a> for your platform, ship it with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[2]s\">Docker</a>, or get it <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[3]s\">packaged</a>.",
|
||||||
"startpage.platform": "Cross-platform",
|
"startpage.platform": "Cross-platform",
|
||||||
"startpage.platform_desc": "MokoGitea runs anywhere <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">Go</a> can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love!",
|
"startpage.platform_desc": "${APP_NAME} runs anywhere <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">Go</a> can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love!",
|
||||||
"startpage.lightweight": "Lightweight",
|
"startpage.lightweight": "Lightweight",
|
||||||
"startpage.lightweight_desc": "MokoGitea has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!",
|
"startpage.lightweight_desc": "${APP_NAME} has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!",
|
||||||
"startpage.license": "Open Source",
|
"startpage.license": "Open Source",
|
||||||
"startpage.license_desc": "Go get <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[1]s\">%[2]s</a>! Join us by <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[3]s\">contributing</a> to make this project even better. Don't hesitate to contribute!",
|
"startpage.license_desc": "Go get <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[1]s\">%[2]s</a>! Join us by <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%[3]s\">contributing</a> to make this project even better. Don't hesitate to contribute!",
|
||||||
"install.install": "Installation",
|
"install.install": "Installation",
|
||||||
"install.installing_desc": "Installing now, please wait…",
|
"install.installing_desc": "Installing now, please wait…",
|
||||||
"install.title": "Initial Configuration",
|
"install.title": "Initial Configuration",
|
||||||
"install.docker_helper": "If you run MokoGitea inside Docker, please read the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">documentation</a> before changing any settings.",
|
"install.docker_helper": "If you run ${APP_NAME} inside Docker, please read the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">documentation</a> before changing any settings.",
|
||||||
"install.require_db_desc": "MokoGitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).",
|
"install.require_db_desc": "${APP_NAME} requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).",
|
||||||
"install.db_title": "Database Settings",
|
"install.db_title": "Database Settings",
|
||||||
"install.db_type": "Database Type",
|
"install.db_type": "Database Type",
|
||||||
"install.host": "Host",
|
"install.host": "Host",
|
||||||
@@ -250,12 +250,12 @@
|
|||||||
"install.db_schema_helper": "Leave blank for database default (\"public\").",
|
"install.db_schema_helper": "Leave blank for database default (\"public\").",
|
||||||
"install.ssl_mode": "SSL",
|
"install.ssl_mode": "SSL",
|
||||||
"install.path": "Path",
|
"install.path": "Path",
|
||||||
"install.sqlite_helper": "File path for the SQLite3 database.<br>Enter an absolute path if you run MokoGitea as a service.",
|
"install.sqlite_helper": "File path for the SQLite3 database.<br>Enter an absolute path if you run ${APP_NAME} as a service.",
|
||||||
"install.reinstall_error": "You are trying to install into an existing MokoGitea database",
|
"install.reinstall_error": "You are trying to install into an existing ${APP_NAME} database",
|
||||||
"install.reinstall_confirm_message": "Re-installing with an existing MokoGitea database can cause multiple problems. In most cases, you should use your existing \"app.ini\" to run MokoGitea. If you know what you are doing, confirm the following:",
|
"install.reinstall_confirm_message": "Re-installing with an existing ${APP_NAME} database can cause multiple problems. In most cases, you should use your existing \"app.ini\" to run ${APP_NAME}. If you know what you are doing, confirm the following:",
|
||||||
"install.reinstall_confirm_check_1": "The data encrypted by the SECRET_KEY in app.ini may be lost: users may not be able to log in with 2FA/OTP and mirrors may not function correctly. By checking this box, you confirm that the current app.ini file contains the correct SECRET_KEY.",
|
"install.reinstall_confirm_check_1": "The data encrypted by the SECRET_KEY in app.ini may be lost: users may not be able to log in with 2FA/OTP and mirrors may not function correctly. By checking this box, you confirm that the current app.ini file contains the correct SECRET_KEY.",
|
||||||
"install.reinstall_confirm_check_2": "The repositories and settings may need to be resynchronized. By checking this box, you confirm that you will resynchronize the hooks for the repositories and authorized_keys file manually. You confirm that you will ensure that repository and mirror settings are correct.",
|
"install.reinstall_confirm_check_2": "The repositories and settings may need to be resynchronized. By checking this box, you confirm that you will resynchronize the hooks for the repositories and authorized_keys file manually. You confirm that you will ensure that repository and mirror settings are correct.",
|
||||||
"install.reinstall_confirm_check_3": "You confirm that you are absolutely sure that this MokoGitea is running with the correct app.ini location and that you are sure that you have to re-install. You confirm that you acknowledge the above risks.",
|
"install.reinstall_confirm_check_3": "You confirm that you are absolutely sure that this ${APP_NAME} is running with the correct app.ini location and that you are sure that you have to re-install. You confirm that you acknowledge the above risks.",
|
||||||
"install.err_empty_db_path": "The SQLite3 database path cannot be empty.",
|
"install.err_empty_db_path": "The SQLite3 database path cannot be empty.",
|
||||||
"install.no_admin_and_disable_registration": "You cannot disable user self-registration without creating an administrator account.",
|
"install.no_admin_and_disable_registration": "You cannot disable user self-registration without creating an administrator account.",
|
||||||
"install.err_empty_admin_password": "The administrator password cannot be empty.",
|
"install.err_empty_admin_password": "The administrator password cannot be empty.",
|
||||||
@@ -271,14 +271,14 @@
|
|||||||
"install.lfs_path": "Git LFS Root Path",
|
"install.lfs_path": "Git LFS Root Path",
|
||||||
"install.lfs_path_helper": "Files tracked by Git LFS will be stored in this directory. Leave empty to disable.",
|
"install.lfs_path_helper": "Files tracked by Git LFS will be stored in this directory. Leave empty to disable.",
|
||||||
"install.run_user": "Run As Username",
|
"install.run_user": "Run As Username",
|
||||||
"install.run_user_helper": "The operating system username that MokoGitea runs as, it must have write access to the data paths. This value is auto-detected and cannot be changed here. To use a different user, restart MokoGitea under that account.",
|
"install.run_user_helper": "The operating system username that ${APP_NAME} runs as, it must have write access to the data paths. This value is auto-detected and cannot be changed here. To use a different user, restart ${APP_NAME} under that account.",
|
||||||
"install.domain": "Server Domain",
|
"install.domain": "Server Domain",
|
||||||
"install.domain_helper": "Domain or host address for the server.",
|
"install.domain_helper": "Domain or host address for the server.",
|
||||||
"install.ssh_port": "SSH Server Port",
|
"install.ssh_port": "SSH Server Port",
|
||||||
"install.ssh_port_helper": "Port number your SSH server listens on. Leave empty to disable.",
|
"install.ssh_port_helper": "Port number your SSH server listens on. Leave empty to disable.",
|
||||||
"install.http_port": "MokoGitea HTTP Listen Port",
|
"install.http_port": "${APP_NAME} HTTP Listen Port",
|
||||||
"install.http_port_helper": "Port number the MokoGitea web server will listen on.",
|
"install.http_port_helper": "Port number the ${APP_NAME} web server will listen on.",
|
||||||
"install.app_url": "MokoGitea Base URL",
|
"install.app_url": "${APP_NAME} Base URL",
|
||||||
"install.app_url_helper": "Base address for HTTP(S) clone URLs and email notifications.",
|
"install.app_url_helper": "Base address for HTTP(S) clone URLs and email notifications.",
|
||||||
"install.log_root_path": "Log Path",
|
"install.log_root_path": "Log Path",
|
||||||
"install.log_root_path_helper": "Log files will be written to this directory.",
|
"install.log_root_path_helper": "Log files will be written to this directory.",
|
||||||
@@ -288,7 +288,7 @@
|
|||||||
"install.smtp_port": "SMTP Port",
|
"install.smtp_port": "SMTP Port",
|
||||||
"install.smtp_from": "Send Email As",
|
"install.smtp_from": "Send Email As",
|
||||||
"install.smtp_from_invalid": "The \"Send Email As\" address is invalid",
|
"install.smtp_from_invalid": "The \"Send Email As\" address is invalid",
|
||||||
"install.smtp_from_helper": "Email address MokoGitea will use. Enter a plain email address or use the \"Name\" <email@example.com> format.",
|
"install.smtp_from_helper": "Email address ${APP_NAME} will use. Enter a plain email address or use the \"Name\" <email@example.com> format.",
|
||||||
"install.mailer_user": "SMTP Username",
|
"install.mailer_user": "SMTP Username",
|
||||||
"install.mailer_password": "SMTP Password",
|
"install.mailer_password": "SMTP Password",
|
||||||
"install.register_confirm": "Require Email Confirmation to Register",
|
"install.register_confirm": "Require Email Confirmation to Register",
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
"install.admin_password": "Password",
|
"install.admin_password": "Password",
|
||||||
"install.confirm_password": "Confirm Password",
|
"install.confirm_password": "Confirm Password",
|
||||||
"install.admin_email": "Email Address",
|
"install.admin_email": "Email Address",
|
||||||
"install.install_btn_confirm": "Install MokoGitea",
|
"install.install_btn_confirm": "Install ${APP_NAME}",
|
||||||
"install.test_git_failed": "Could not test 'git' command: %v",
|
"install.test_git_failed": "Could not test 'git' command: %v",
|
||||||
"install.invalid_db_setting": "The database settings are invalid: %v",
|
"install.invalid_db_setting": "The database settings are invalid: %v",
|
||||||
"install.invalid_db_table": "The database table \"%s\" is invalid: %v",
|
"install.invalid_db_table": "The database table \"%s\" is invalid: %v",
|
||||||
@@ -385,7 +385,7 @@
|
|||||||
"auth.forgot_password_title": "Forgot Password",
|
"auth.forgot_password_title": "Forgot Password",
|
||||||
"auth.forgot_password": "Forgot password?",
|
"auth.forgot_password": "Forgot password?",
|
||||||
"auth.need_account": "Need an account?",
|
"auth.need_account": "Need an account?",
|
||||||
"auth.sign_up_tip": "You are registering the first account in the system, which has administrator privileges. Please carefully remember your username and password. If you forget the username or password, please refer to the MokoGitea documentation to recover the account.",
|
"auth.sign_up_tip": "You are registering the first account in the system, which has administrator privileges. Please carefully remember your username and password. If you forget the username or password, please refer to the ${APP_NAME} documentation to recover the account.",
|
||||||
"auth.sign_up_now": "Register now.",
|
"auth.sign_up_now": "Register now.",
|
||||||
"auth.sign_up_successful": "Account was successfully created. Welcome!",
|
"auth.sign_up_successful": "Account was successfully created. Welcome!",
|
||||||
"auth.confirmation_mail_sent_prompt_ex": "A new confirmation email has been sent to <b>%s</b>. Please check your inbox within the next %s to complete the registration process. If your registration email address is incorrect, you can sign in again and change it.",
|
"auth.confirmation_mail_sent_prompt_ex": "A new confirmation email has been sent to <b>%s</b>. Please check your inbox within the next %s to complete the registration process. If your registration email address is incorrect, you can sign in again and change it.",
|
||||||
@@ -409,7 +409,7 @@
|
|||||||
"auth.reset_password_helper": "Recover Account",
|
"auth.reset_password_helper": "Recover Account",
|
||||||
"auth.reset_password_wrong_user": "You are signed in as %s, but the account recovery link is meant for %s",
|
"auth.reset_password_wrong_user": "You are signed in as %s, but the account recovery link is meant for %s",
|
||||||
"auth.password_too_short": "Password length cannot be less than %d characters.",
|
"auth.password_too_short": "Password length cannot be less than %d characters.",
|
||||||
"auth.non_local_account": "Non-local users cannot update their password through the MokoGitea web interface.",
|
"auth.non_local_account": "Non-local users cannot update their password through the ${APP_NAME} web interface.",
|
||||||
"auth.verify": "Verify",
|
"auth.verify": "Verify",
|
||||||
"auth.scratch_code": "Scratch code",
|
"auth.scratch_code": "Scratch code",
|
||||||
"auth.use_scratch_code": "Use a scratch code",
|
"auth.use_scratch_code": "Use a scratch code",
|
||||||
@@ -726,7 +726,7 @@
|
|||||||
"settings.retype_new_password": "Confirm New Password",
|
"settings.retype_new_password": "Confirm New Password",
|
||||||
"settings.password_incorrect": "The current password is incorrect.",
|
"settings.password_incorrect": "The current password is incorrect.",
|
||||||
"settings.change_password_success": "Your password has been updated. Sign in using your new password from now on.",
|
"settings.change_password_success": "Your password has been updated. Sign in using your new password from now on.",
|
||||||
"settings.password_change_disabled": "Non-local users cannot update their password through the MokoGitea web interface.",
|
"settings.password_change_disabled": "Non-local users cannot update their password through the ${APP_NAME} web interface.",
|
||||||
"settings.emails": "Email Addresses",
|
"settings.emails": "Email Addresses",
|
||||||
"settings.manage_emails": "Manage Email Addresses",
|
"settings.manage_emails": "Manage Email Addresses",
|
||||||
"settings.manage_themes": "Select default theme",
|
"settings.manage_themes": "Select default theme",
|
||||||
@@ -734,7 +734,7 @@
|
|||||||
"settings.email_desc": "Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.",
|
"settings.email_desc": "Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.",
|
||||||
"settings.theme_desc": "This will be your default theme across the site.",
|
"settings.theme_desc": "This will be your default theme across the site.",
|
||||||
"settings.theme_colorblindness_help": "Color blindness Theme Support",
|
"settings.theme_colorblindness_help": "Color blindness Theme Support",
|
||||||
"settings.theme_colorblindness_prompt": "MokoGitea only has a few themes with basic color blindness support, which only have a few colors defined. The work is still in progress. More improvements could be made by defining more colors in the theme CSS files.",
|
"settings.theme_colorblindness_prompt": "${APP_NAME} only has a few themes with basic color blindness support, which only have a few colors defined. The work is still in progress. More improvements could be made by defining more colors in the theme CSS files.",
|
||||||
"settings.primary": "Primary",
|
"settings.primary": "Primary",
|
||||||
"settings.activated": "Activated",
|
"settings.activated": "Activated",
|
||||||
"settings.requires_activation": "Requires activation",
|
"settings.requires_activation": "Requires activation",
|
||||||
@@ -843,7 +843,7 @@
|
|||||||
"settings.unbind_success": "The social account has been removed successfully.",
|
"settings.unbind_success": "The social account has been removed successfully.",
|
||||||
"settings.manage_access_token": "Manage Access Tokens",
|
"settings.manage_access_token": "Manage Access Tokens",
|
||||||
"settings.generate_new_token": "Generate New Token",
|
"settings.generate_new_token": "Generate New Token",
|
||||||
"settings.tokens_desc": "These tokens grant access to your account using the Gitea API.",
|
"settings.tokens_desc": "These tokens grant access to your account using the ${APP_NAME} API.",
|
||||||
"settings.token_name": "Token Name",
|
"settings.token_name": "Token Name",
|
||||||
"settings.generate_token": "Generate Token",
|
"settings.generate_token": "Generate Token",
|
||||||
"settings.generate_token_success": "Your new token has been generated. Copy it now as it will not be shown again.",
|
"settings.generate_token_success": "Your new token has been generated. Copy it now as it will not be shown again.",
|
||||||
@@ -869,7 +869,7 @@
|
|||||||
"settings.permissions_list": "Permissions:",
|
"settings.permissions_list": "Permissions:",
|
||||||
"settings.manage_oauth2_applications": "Manage OAuth2 Applications",
|
"settings.manage_oauth2_applications": "Manage OAuth2 Applications",
|
||||||
"settings.edit_oauth2_application": "Edit OAuth2 Application",
|
"settings.edit_oauth2_application": "Edit OAuth2 Application",
|
||||||
"settings.oauth2_applications_desc": "OAuth2 applications enable your third-party application to securely authenticate users at this MokoGitea instance.",
|
"settings.oauth2_applications_desc": "OAuth2 applications enable your third-party application to securely authenticate users at this ${APP_NAME} instance.",
|
||||||
"settings.remove_oauth2_application": "Remove OAuth2 Application",
|
"settings.remove_oauth2_application": "Remove OAuth2 Application",
|
||||||
"settings.remove_oauth2_application_desc": "Removing an OAuth2 application will revoke access to all signed access tokens. Continue?",
|
"settings.remove_oauth2_application_desc": "Removing an OAuth2 application will revoke access to all signed access tokens. Continue?",
|
||||||
"settings.remove_oauth2_application_success": "The application has been deleted.",
|
"settings.remove_oauth2_application_success": "The application has been deleted.",
|
||||||
@@ -890,9 +890,9 @@
|
|||||||
"settings.oauth2_application_edit": "Edit",
|
"settings.oauth2_application_edit": "Edit",
|
||||||
"settings.oauth2_application_create_description": "OAuth2 applications give your third-party application access to user accounts on this instance.",
|
"settings.oauth2_application_create_description": "OAuth2 applications give your third-party application access to user accounts on this instance.",
|
||||||
"settings.oauth2_application_remove_description": "Removing an OAuth2 application will prevent it from accessing authorized user accounts on this instance. Continue?",
|
"settings.oauth2_application_remove_description": "Removing an OAuth2 application will prevent it from accessing authorized user accounts on this instance. Continue?",
|
||||||
"settings.oauth2_application_locked": "MokoGitea pre-registers some OAuth2 applications on startup if enabled in config. To prevent unexpected behavior, these can neither be edited nor removed. Please refer to the OAuth2 documentation for more information.",
|
"settings.oauth2_application_locked": "${APP_NAME} pre-registers some OAuth2 applications on startup if enabled in config. To prevent unexpected behavior, these can neither be edited nor removed. Please refer to the OAuth2 documentation for more information.",
|
||||||
"settings.authorized_oauth2_applications": "Authorized OAuth2 Applications",
|
"settings.authorized_oauth2_applications": "Authorized OAuth2 Applications",
|
||||||
"settings.authorized_oauth2_applications_description": "You have granted access to your personal MokoGitea account to these third-party applications. Please revoke access for applications you no longer need.",
|
"settings.authorized_oauth2_applications_description": "You have granted access to your personal ${APP_NAME} account to these third-party applications. Please revoke access for applications you no longer need.",
|
||||||
"settings.revoke_key": "Revoke",
|
"settings.revoke_key": "Revoke",
|
||||||
"settings.revoke_oauth2_grant": "Revoke Access",
|
"settings.revoke_oauth2_grant": "Revoke Access",
|
||||||
"settings.revoke_oauth2_grant_description": "Revoking access for this third-party application will prevent this application from accessing your data. Are you sure?",
|
"settings.revoke_oauth2_grant_description": "Revoking access for this third-party application will prevent this application from accessing your data. Are you sure?",
|
||||||
@@ -923,11 +923,11 @@
|
|||||||
"settings.webauthn_key_loss_warning": "If you lose your security keys, you will lose access to your account.",
|
"settings.webauthn_key_loss_warning": "If you lose your security keys, you will lose access to your account.",
|
||||||
"settings.webauthn_alternative_tip": "You may want to configure an additional authentication method.",
|
"settings.webauthn_alternative_tip": "You may want to configure an additional authentication method.",
|
||||||
"settings.manage_account_links": "Manage Linked Accounts",
|
"settings.manage_account_links": "Manage Linked Accounts",
|
||||||
"settings.manage_account_links_desc": "These external accounts are linked to your MokoGitea account.",
|
"settings.manage_account_links_desc": "These external accounts are linked to your ${APP_NAME} account.",
|
||||||
"settings.account_links_not_available": "No external accounts are currently linked to your MokoGitea account.",
|
"settings.account_links_not_available": "No external accounts are currently linked to your ${APP_NAME} account.",
|
||||||
"settings.link_account": "Link Account",
|
"settings.link_account": "Link Account",
|
||||||
"settings.remove_account_link": "Remove Linked Account",
|
"settings.remove_account_link": "Remove Linked Account",
|
||||||
"settings.remove_account_link_desc": "Removing a linked account will revoke its access to your MokoGitea account. Continue?",
|
"settings.remove_account_link_desc": "Removing a linked account will revoke its access to your ${APP_NAME} account. Continue?",
|
||||||
"settings.remove_account_link_success": "The linked account has been removed.",
|
"settings.remove_account_link_success": "The linked account has been removed.",
|
||||||
"settings.hooks.desc": "Add webhooks which will be triggered for <strong>all repositories</strong> that you own.",
|
"settings.hooks.desc": "Add webhooks which will be triggered for <strong>all repositories</strong> that you own.",
|
||||||
"settings.orgs_none": "You are not a member of any organizations.",
|
"settings.orgs_none": "You are not a member of any organizations.",
|
||||||
@@ -943,7 +943,7 @@
|
|||||||
"settings.email_notifications.disable": "Disable Email Notifications",
|
"settings.email_notifications.disable": "Disable Email Notifications",
|
||||||
"settings.email_notifications.submit": "Set Email Preference",
|
"settings.email_notifications.submit": "Set Email Preference",
|
||||||
"settings.email_notifications.andyourown": "And Your Own Notifications",
|
"settings.email_notifications.andyourown": "And Your Own Notifications",
|
||||||
"settings.email_notifications.actions.desc": "Notifications for workflow runs on repositories set up with <a target=\"_blank\" href=\"%s\">Gitea Actions</a>.",
|
"settings.email_notifications.actions.desc": "Notifications for workflow runs on repositories set up with <a target=\"_blank\" href=\"%s\">${APP_NAME} Actions</a>.",
|
||||||
"settings.email_notifications.actions.failure_only": "Only notify for failed workflow runs",
|
"settings.email_notifications.actions.failure_only": "Only notify for failed workflow runs",
|
||||||
"settings.visibility": "User visibility",
|
"settings.visibility": "User visibility",
|
||||||
"settings.visibility.public": "Public",
|
"settings.visibility.public": "Public",
|
||||||
@@ -1125,7 +1125,7 @@
|
|||||||
"repo.migrate.github.description": "Migrate data from github.com or other GitHub instances.",
|
"repo.migrate.github.description": "Migrate data from github.com or other GitHub instances.",
|
||||||
"repo.migrate.git.description": "Migrate a repository only from any Git service.",
|
"repo.migrate.git.description": "Migrate a repository only from any Git service.",
|
||||||
"repo.migrate.gitlab.description": "Migrate data from gitlab.com or other GitLab instances.",
|
"repo.migrate.gitlab.description": "Migrate data from gitlab.com or other GitLab instances.",
|
||||||
"repo.migrate.gitea.description": "Migrate data from gitea.com or other Gitea instances.",
|
"repo.migrate.gitea.description": "Migrate data from other ${APP_NAME} instances.",
|
||||||
"repo.migrate.gogs.description": "Migrate data from notabug.org or other Gogs instances.",
|
"repo.migrate.gogs.description": "Migrate data from notabug.org or other Gogs instances.",
|
||||||
"repo.migrate.onedev.description": "Migrate data from code.onedev.io or other OneDev instances.",
|
"repo.migrate.onedev.description": "Migrate data from code.onedev.io or other OneDev instances.",
|
||||||
"repo.migrate.codebase.description": "Migrate data from codebasehq.com.",
|
"repo.migrate.codebase.description": "Migrate data from codebasehq.com.",
|
||||||
@@ -1891,7 +1891,7 @@
|
|||||||
"repo.pulls.cmd_instruction_checkout_title": "Checkout",
|
"repo.pulls.cmd_instruction_checkout_title": "Checkout",
|
||||||
"repo.pulls.cmd_instruction_checkout_desc": "From your project repository, check out a new branch and test the changes.",
|
"repo.pulls.cmd_instruction_checkout_desc": "From your project repository, check out a new branch and test the changes.",
|
||||||
"repo.pulls.cmd_instruction_merge_title": "Merge",
|
"repo.pulls.cmd_instruction_merge_title": "Merge",
|
||||||
"repo.pulls.cmd_instruction_merge_desc": "Merge the changes and update on MokoGitea.",
|
"repo.pulls.cmd_instruction_merge_desc": "Merge the changes and update on ${APP_NAME}.",
|
||||||
"repo.pulls.cmd_instruction_merge_warning": "Warning: This operation cannot merge pull request because \"autodetect manual merge\" is not enabled.",
|
"repo.pulls.cmd_instruction_merge_warning": "Warning: This operation cannot merge pull request because \"autodetect manual merge\" is not enabled.",
|
||||||
"repo.pulls.clear_merge_message": "Clear merge message",
|
"repo.pulls.clear_merge_message": "Clear merge message",
|
||||||
"repo.pulls.clear_merge_message_hint": "Clearing the merge message will only remove the commit message content and keep generated git trailers such as \"Co-Authored-By…\".",
|
"repo.pulls.clear_merge_message_hint": "Clearing the merge message will only remove the commit message content and keep generated git trailers such as \"Co-Authored-By…\".",
|
||||||
@@ -2199,11 +2199,11 @@
|
|||||||
"repo.settings.trust_model.collaborator.long": "Collaborator: Trust signatures by collaborators",
|
"repo.settings.trust_model.collaborator.long": "Collaborator: Trust signatures by collaborators",
|
||||||
"repo.settings.trust_model.collaborator.desc": "Valid signatures by collaborators of this repository will be marked \"trusted\", whether they match the committer or not. Otherwise, valid signatures will be marked \"untrusted\" if the signature matches the committer and \"unmatched\" if not.",
|
"repo.settings.trust_model.collaborator.desc": "Valid signatures by collaborators of this repository will be marked \"trusted\", whether they match the committer or not. Otherwise, valid signatures will be marked \"untrusted\" if the signature matches the committer and \"unmatched\" if not.",
|
||||||
"repo.settings.trust_model.committer": "Committer",
|
"repo.settings.trust_model.committer": "Committer",
|
||||||
"repo.settings.trust_model.committer.long": "Committer: Trust signatures that match committers. This matches GitHub's behavior and will force commits signed by MokoGitea to have MokoGitea as the committer.",
|
"repo.settings.trust_model.committer.long": "Committer: Trust signatures that match committers. This matches GitHub's behavior and will force commits signed by ${APP_NAME} to have ${APP_NAME} as the committer.",
|
||||||
"repo.settings.trust_model.committer.desc": "Valid signatures will only be marked \"trusted\" if they match the committer, otherwise they will be marked \"unmatched\". This forces MokoGitea to be the committer on signed commits, with the actual committer marked as Co-authored-by: and Co-committed-by: trailer in the commit. The default MokoGitea key must match a user in the database.",
|
"repo.settings.trust_model.committer.desc": "Valid signatures will only be marked \"trusted\" if they match the committer, otherwise they will be marked \"unmatched\". This forces ${APP_NAME} to be the committer on signed commits, with the actual committer marked as Co-authored-by: and Co-committed-by: trailer in the commit. The default ${APP_NAME} key must match a user in the database.",
|
||||||
"repo.settings.trust_model.collaboratorcommitter": "Collaborator+Committer",
|
"repo.settings.trust_model.collaboratorcommitter": "Collaborator+Committer",
|
||||||
"repo.settings.trust_model.collaboratorcommitter.long": "Collaborator+Committer: Trust signatures by collaborators which match the committer",
|
"repo.settings.trust_model.collaboratorcommitter.long": "Collaborator+Committer: Trust signatures by collaborators which match the committer",
|
||||||
"repo.settings.trust_model.collaboratorcommitter.desc": "Valid signatures by collaborators of this repository will be marked \"trusted\" if they match the committer. Otherwise, valid signatures will be marked \"untrusted\" if the signature matches the committer and \"unmatched\" otherwise. This will force MokoGitea to be marked as the committer on signed commits, with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default MokoGitea key must match a user in the database.",
|
"repo.settings.trust_model.collaboratorcommitter.desc": "Valid signatures by collaborators of this repository will be marked \"trusted\" if they match the committer. Otherwise, valid signatures will be marked \"untrusted\" if the signature matches the committer and \"unmatched\" otherwise. This will force ${APP_NAME} to be marked as the committer on signed commits, with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default ${APP_NAME} key must match a user in the database.",
|
||||||
"repo.settings.wiki_delete": "Delete Wiki Data",
|
"repo.settings.wiki_delete": "Delete Wiki Data",
|
||||||
"repo.settings.wiki_delete_desc": "Deleting repository wiki data is permanent and cannot be undone.",
|
"repo.settings.wiki_delete_desc": "Deleting repository wiki data is permanent and cannot be undone.",
|
||||||
"repo.settings.wiki_delete_notices_1": "- This will permanently delete and disable the repository wiki for %s.",
|
"repo.settings.wiki_delete_notices_1": "- This will permanently delete and disable the repository wiki for %s.",
|
||||||
@@ -2240,7 +2240,7 @@
|
|||||||
"repo.settings.remove_team_success": "The team's access to the repository has been removed.",
|
"repo.settings.remove_team_success": "The team's access to the repository has been removed.",
|
||||||
"repo.settings.add_webhook": "Add Webhook",
|
"repo.settings.add_webhook": "Add Webhook",
|
||||||
"repo.settings.add_webhook.invalid_channel_name": "Webhook channel name cannot be empty and cannot contain only a # character.",
|
"repo.settings.add_webhook.invalid_channel_name": "Webhook channel name cannot be empty and cannot contain only a # character.",
|
||||||
"repo.settings.hooks_desc": "Webhooks automatically make HTTP POST requests to a server when certain MokoGitea events trigger. Read more in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">webhooks guide</a>.",
|
"repo.settings.hooks_desc": "Webhooks automatically make HTTP POST requests to a server when certain ${APP_NAME} events trigger. Read more in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">webhooks guide</a>.",
|
||||||
"repo.settings.webhook_deletion": "Remove Webhook",
|
"repo.settings.webhook_deletion": "Remove Webhook",
|
||||||
"repo.settings.webhook_deletion_desc": "Removing a webhook deletes its settings and delivery history. Continue?",
|
"repo.settings.webhook_deletion_desc": "Removing a webhook deletes its settings and delivery history. Continue?",
|
||||||
"repo.settings.webhook_deletion_success": "The webhook has been removed.",
|
"repo.settings.webhook_deletion_success": "The webhook has been removed.",
|
||||||
@@ -2258,7 +2258,7 @@
|
|||||||
"repo.settings.githooks_desc": "Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations.",
|
"repo.settings.githooks_desc": "Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations.",
|
||||||
"repo.settings.githook_edit_desc": "If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook.",
|
"repo.settings.githook_edit_desc": "If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook.",
|
||||||
"repo.settings.update_githook": "Update Hook",
|
"repo.settings.update_githook": "Update Hook",
|
||||||
"repo.settings.add_webhook_desc": "MokoGitea will send <code>POST</code> requests with a specified content type to the target URL. Read more in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">webhooks guide</a>.",
|
"repo.settings.add_webhook_desc": "${APP_NAME} will send <code>POST</code> requests with a specified content type to the target URL. Read more in the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">webhooks guide</a>.",
|
||||||
"repo.settings.payload_url": "Target URL",
|
"repo.settings.payload_url": "Target URL",
|
||||||
"repo.settings.http_method": "HTTP Method",
|
"repo.settings.http_method": "HTTP Method",
|
||||||
"repo.settings.content_type": "POST Content Type",
|
"repo.settings.content_type": "POST Content Type",
|
||||||
@@ -2326,9 +2326,9 @@
|
|||||||
"repo.settings.event_pull_request_merge": "Pull Request Merge",
|
"repo.settings.event_pull_request_merge": "Pull Request Merge",
|
||||||
"repo.settings.event_header_workflow": "Workflow Events",
|
"repo.settings.event_header_workflow": "Workflow Events",
|
||||||
"repo.settings.event_workflow_run": "Workflow Run",
|
"repo.settings.event_workflow_run": "Workflow Run",
|
||||||
"repo.settings.event_workflow_run_desc": "Gitea Actions Workflow run queued, waiting, in progress, or completed.",
|
"repo.settings.event_workflow_run_desc": "${APP_NAME} Actions Workflow run queued, waiting, in progress, or completed.",
|
||||||
"repo.settings.event_workflow_job": "Workflow Jobs",
|
"repo.settings.event_workflow_job": "Workflow Jobs",
|
||||||
"repo.settings.event_workflow_job_desc": "Gitea Actions Workflow job queued, waiting, in progress, or completed.",
|
"repo.settings.event_workflow_job_desc": "${APP_NAME} Actions Workflow job queued, waiting, in progress, or completed.",
|
||||||
"repo.settings.event_package": "Package",
|
"repo.settings.event_package": "Package",
|
||||||
"repo.settings.event_package_desc": "Package created or deleted in a repository.",
|
"repo.settings.event_package_desc": "Package created or deleted in a repository.",
|
||||||
"repo.settings.branch_filter": "Branch filter",
|
"repo.settings.branch_filter": "Branch filter",
|
||||||
@@ -2349,7 +2349,7 @@
|
|||||||
"repo.settings.slack_domain": "Domain",
|
"repo.settings.slack_domain": "Domain",
|
||||||
"repo.settings.slack_channel": "Channel",
|
"repo.settings.slack_channel": "Channel",
|
||||||
"repo.settings.add_web_hook_desc": "Integrate <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">%s</a> into your repository.",
|
"repo.settings.add_web_hook_desc": "Integrate <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">%s</a> into your repository.",
|
||||||
"repo.settings.web_hook_name_gitea": "MokoGitea",
|
"repo.settings.web_hook_name_gitea": "${APP_NAME}",
|
||||||
"repo.settings.web_hook_name_gogs": "Gogs",
|
"repo.settings.web_hook_name_gogs": "Gogs",
|
||||||
"repo.settings.web_hook_name_slack": "Slack",
|
"repo.settings.web_hook_name_slack": "Slack",
|
||||||
"repo.settings.web_hook_name_discord": "Discord",
|
"repo.settings.web_hook_name_discord": "Discord",
|
||||||
@@ -2634,7 +2634,7 @@
|
|||||||
"repo.release.delete_release": "Delete Release",
|
"repo.release.delete_release": "Delete Release",
|
||||||
"repo.release.delete_tag": "Delete Tag",
|
"repo.release.delete_tag": "Delete Tag",
|
||||||
"repo.release.deletion": "Delete Release",
|
"repo.release.deletion": "Delete Release",
|
||||||
"repo.release.deletion_desc": "Deleting a release only removes it from MokoGitea. It will not affect the Git tag, the contents of your repository or its history. Continue?",
|
"repo.release.deletion_desc": "Deleting a release only removes it from ${APP_NAME}. It will not affect the Git tag, the contents of your repository or its history. Continue?",
|
||||||
"repo.release.deletion_success": "The release has been deleted.",
|
"repo.release.deletion_success": "The release has been deleted.",
|
||||||
"repo.release.deletion_tag_desc": "Will delete this tag from repository. Repository contents and history remain unchanged. Continue?",
|
"repo.release.deletion_tag_desc": "Will delete this tag from repository. Repository contents and history remain unchanged. Continue?",
|
||||||
"repo.release.deletion_tag_success": "The tag has been deleted.",
|
"repo.release.deletion_tag_success": "The tag has been deleted.",
|
||||||
@@ -2913,7 +2913,7 @@
|
|||||||
"admin.last_page": "Last",
|
"admin.last_page": "Last",
|
||||||
"admin.total": "Total: %d",
|
"admin.total": "Total: %d",
|
||||||
"admin.settings": "Admin Settings",
|
"admin.settings": "Admin Settings",
|
||||||
"admin.dashboard.new_version_hint": "MokoGitea %s is now available, you are running %s. Check <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">the blog</a> for more details.",
|
"admin.dashboard.new_version_hint": "${APP_NAME} %s is now available, you are running %s. Check <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">the blog</a> for more details.",
|
||||||
"admin.dashboard.statistic": "Summary",
|
"admin.dashboard.statistic": "Summary",
|
||||||
"admin.dashboard.maintenance_operations": "Maintenance Operations",
|
"admin.dashboard.maintenance_operations": "Maintenance Operations",
|
||||||
"admin.dashboard.system_status": "System Status",
|
"admin.dashboard.system_status": "System Status",
|
||||||
@@ -2949,8 +2949,8 @@
|
|||||||
"admin.dashboard.deleted_branches_cleanup": "Clean up deleted branches",
|
"admin.dashboard.deleted_branches_cleanup": "Clean up deleted branches",
|
||||||
"admin.dashboard.update_migration_poster_id": "Update migration poster IDs",
|
"admin.dashboard.update_migration_poster_id": "Update migration poster IDs",
|
||||||
"admin.dashboard.git_gc_repos": "Garbage-collect all repositories",
|
"admin.dashboard.git_gc_repos": "Garbage-collect all repositories",
|
||||||
"admin.dashboard.resync_all_sshkeys": "Update the '.ssh/authorized_keys' file with MokoGitea SSH keys",
|
"admin.dashboard.resync_all_sshkeys": "Update the '.ssh/authorized_keys' file with ${APP_NAME} SSH keys",
|
||||||
"admin.dashboard.resync_all_sshprincipals": "Update the '.ssh/authorized_principals' file with MokoGitea SSH principals",
|
"admin.dashboard.resync_all_sshprincipals": "Update the '.ssh/authorized_principals' file with ${APP_NAME} SSH principals",
|
||||||
"admin.dashboard.resync_all_hooks": "Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)",
|
"admin.dashboard.resync_all_hooks": "Resynchronize git hooks of all repositories (pre-receive, update, post-receive, proc-receive, ...)",
|
||||||
"admin.dashboard.reinit_missing_repos": "Reinitialize all missing Git repositories for which records exist",
|
"admin.dashboard.reinit_missing_repos": "Reinitialize all missing Git repositories for which records exist",
|
||||||
"admin.dashboard.sync_external_users": "Synchronize external user data",
|
"admin.dashboard.sync_external_users": "Synchronize external user data",
|
||||||
@@ -3030,7 +3030,7 @@
|
|||||||
"admin.users.is_admin": "Is Administrator",
|
"admin.users.is_admin": "Is Administrator",
|
||||||
"admin.users.is_restricted": "Is Restricted",
|
"admin.users.is_restricted": "Is Restricted",
|
||||||
"admin.users.allow_git_hook": "May Create Git Hooks",
|
"admin.users.allow_git_hook": "May Create Git Hooks",
|
||||||
"admin.users.allow_git_hook_tooltip": "Git Hooks are executed as the OS user running MokoGitea and will have the same level of host access. As a result, users with this special Git Hook privilege can access and modify all MokoGitea repositories as well as the database used by MokoGitea. Consequently they are also able to gain MokoGitea administrator privileges.",
|
"admin.users.allow_git_hook_tooltip": "Git Hooks are executed as the OS user running ${APP_NAME} and will have the same level of host access. As a result, users with this special Git Hook privilege can access and modify all ${APP_NAME} repositories as well as the database used by ${APP_NAME}. Consequently they are also able to gain ${APP_NAME} administrator privileges.",
|
||||||
"admin.users.allow_import_local": "May Import Local Repositories",
|
"admin.users.allow_import_local": "May Import Local Repositories",
|
||||||
"admin.users.allow_create_organization": "May Create Organizations",
|
"admin.users.allow_create_organization": "May Create Organizations",
|
||||||
"admin.users.update_profile": "Update User Account",
|
"admin.users.update_profile": "Update User Account",
|
||||||
@@ -3100,11 +3100,11 @@
|
|||||||
"admin.packages.size": "Size",
|
"admin.packages.size": "Size",
|
||||||
"admin.packages.published": "Published",
|
"admin.packages.published": "Published",
|
||||||
"admin.defaulthooks": "Default Webhooks",
|
"admin.defaulthooks": "Default Webhooks",
|
||||||
"admin.defaulthooks.desc": "Webhooks automatically make HTTP POST requests to a server when certain MokoGitea events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the <a target=\"_blank\" rel=\"noopener\" href=\"%s\">webhooks guide</a>.",
|
"admin.defaulthooks.desc": "Webhooks automatically make HTTP POST requests to a server when certain ${APP_NAME} events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the <a target=\"_blank\" rel=\"noopener\" href=\"%s\">webhooks guide</a>.",
|
||||||
"admin.defaulthooks.add_webhook": "Add Default Webhook",
|
"admin.defaulthooks.add_webhook": "Add Default Webhook",
|
||||||
"admin.defaulthooks.update_webhook": "Update Default Webhook",
|
"admin.defaulthooks.update_webhook": "Update Default Webhook",
|
||||||
"admin.systemhooks": "System Webhooks",
|
"admin.systemhooks": "System Webhooks",
|
||||||
"admin.systemhooks.desc": "Webhooks automatically make HTTP POST requests to a server when certain MokoGitea events trigger. Webhooks defined here will act on all repositories on the system, so please consider any performance implications this may have. Read more in the <a target=\"_blank\" rel=\"noopener\" href=\"%s\">webhooks guide</a>.",
|
"admin.systemhooks.desc": "Webhooks automatically make HTTP POST requests to a server when certain ${APP_NAME} events trigger. Webhooks defined here will act on all repositories on the system, so please consider any performance implications this may have. Read more in the <a target=\"_blank\" rel=\"noopener\" href=\"%s\">webhooks guide</a>.",
|
||||||
"admin.systemhooks.add_webhook": "Add System Webhook",
|
"admin.systemhooks.add_webhook": "Add System Webhook",
|
||||||
"admin.systemhooks.update_webhook": "Update System Webhook",
|
"admin.systemhooks.update_webhook": "Update System Webhook",
|
||||||
"admin.auths.auth_manage_panel": "Authentication Source Management",
|
"admin.auths.auth_manage_panel": "Authentication Source Management",
|
||||||
@@ -3125,7 +3125,7 @@
|
|||||||
"admin.auths.user_base": "User Search Base",
|
"admin.auths.user_base": "User Search Base",
|
||||||
"admin.auths.user_dn": "User DN",
|
"admin.auths.user_dn": "User DN",
|
||||||
"admin.auths.attribute_username": "Username Attribute",
|
"admin.auths.attribute_username": "Username Attribute",
|
||||||
"admin.auths.attribute_username_placeholder": "Leave empty to use the username entered in MokoGitea.",
|
"admin.auths.attribute_username_placeholder": "Leave empty to use the username entered in ${APP_NAME}.",
|
||||||
"admin.auths.attribute_name": "First Name Attribute",
|
"admin.auths.attribute_name": "First Name Attribute",
|
||||||
"admin.auths.attribute_surname": "Surname Attribute",
|
"admin.auths.attribute_surname": "Surname Attribute",
|
||||||
"admin.auths.attribute_mail": "Email Attribute",
|
"admin.auths.attribute_mail": "Email Attribute",
|
||||||
@@ -3232,7 +3232,7 @@
|
|||||||
"admin.auths.invalid_openIdConnectAutoDiscoveryURL": "Invalid Auto Discovery URL (this must be a valid URL starting with http:// or https://)",
|
"admin.auths.invalid_openIdConnectAutoDiscoveryURL": "Invalid Auto Discovery URL (this must be a valid URL starting with http:// or https://)",
|
||||||
"admin.config.server_config": "Server Configuration",
|
"admin.config.server_config": "Server Configuration",
|
||||||
"admin.config.app_name": "Site Title",
|
"admin.config.app_name": "Site Title",
|
||||||
"admin.config.app_ver": "MokoGitea Version",
|
"admin.config.app_ver": "${APP_NAME} Version",
|
||||||
"admin.config.custom_conf": "Configuration File Path",
|
"admin.config.custom_conf": "Configuration File Path",
|
||||||
"admin.config.custom_file_root_path": "Custom File Root Path",
|
"admin.config.custom_file_root_path": "Custom File Root Path",
|
||||||
"admin.config.disable_router_log": "Disable Router Log",
|
"admin.config.disable_router_log": "Disable Router Log",
|
||||||
@@ -3272,7 +3272,7 @@
|
|||||||
"admin.config.service_config": "Service Configuration",
|
"admin.config.service_config": "Service Configuration",
|
||||||
"admin.config.register_email_confirm": "Require Email Confirmation to Register",
|
"admin.config.register_email_confirm": "Require Email Confirmation to Register",
|
||||||
"admin.config.disable_register": "Disable Self-Registration",
|
"admin.config.disable_register": "Disable Self-Registration",
|
||||||
"admin.config.allow_only_internal_registration": "Allow Registration Only Through MokoGitea itself",
|
"admin.config.allow_only_internal_registration": "Allow Registration Only Through ${APP_NAME} itself",
|
||||||
"admin.config.allow_only_external_registration": "Allow Registration Only Through External Services",
|
"admin.config.allow_only_external_registration": "Allow Registration Only Through External Services",
|
||||||
"admin.config.enable_openid_signup": "Enable OpenID Self-Registration",
|
"admin.config.enable_openid_signup": "Enable OpenID Self-Registration",
|
||||||
"admin.config.enable_openid_signin": "Enable OpenID Sign-In",
|
"admin.config.enable_openid_signin": "Enable OpenID Sign-In",
|
||||||
@@ -3414,11 +3414,11 @@
|
|||||||
"admin.self_check.no_problem_found": "No problem found yet.",
|
"admin.self_check.no_problem_found": "No problem found yet.",
|
||||||
"admin.self_check.startup_warnings": "Startup warnings:",
|
"admin.self_check.startup_warnings": "Startup warnings:",
|
||||||
"admin.self_check.database_collation_mismatch": "Expect database to use collation: %s",
|
"admin.self_check.database_collation_mismatch": "Expect database to use collation: %s",
|
||||||
"admin.self_check.database_collation_case_insensitive": "Database is using collation %s, which is a case-insensitive collation. Although MokoGitea could work with it, there might be some rare cases which don't work as expected.",
|
"admin.self_check.database_collation_case_insensitive": "Database is using collation %s, which is a case-insensitive collation. Although ${APP_NAME} could work with it, there might be some rare cases which don't work as expected.",
|
||||||
"admin.self_check.database_inconsistent_collation_columns": "Database is using collation %s, but these columns are using mismatched collations. This might cause some unexpected problems.",
|
"admin.self_check.database_inconsistent_collation_columns": "Database is using collation %s, but these columns are using mismatched collations. This might cause some unexpected problems.",
|
||||||
"admin.self_check.database_fix_mysql": "For MySQL/MariaDB users, you could use the \"gitea doctor convert\" command to fix the collation problems, or you could also fix the problem manually with \"ALTER ... COLLATE ...\" SQL queries.",
|
"admin.self_check.database_fix_mysql": "For MySQL/MariaDB users, you could use the \"gitea doctor convert\" command to fix the collation problems, or you could also fix the problem manually with \"ALTER ... COLLATE ...\" SQL queries.",
|
||||||
"admin.self_check.database_fix_mssql": "For MSSQL users, you could only fix the problem manually with \"ALTER ... COLLATE ...\" SQL queries at the moment.",
|
"admin.self_check.database_fix_mssql": "For MSSQL users, you could only fix the problem manually with \"ALTER ... COLLATE ...\" SQL queries at the moment.",
|
||||||
"admin.self_check.location_origin_mismatch": "Current URL (%[1]s) doesn't match the URL seen by MokoGitea (%[2]s). If you are using a reverse proxy, please make sure the \"Host\" and \"X-Forwarded-Proto\" headers are set correctly.",
|
"admin.self_check.location_origin_mismatch": "Current URL (%[1]s) doesn't match the URL seen by ${APP_NAME} (%[2]s). If you are using a reverse proxy, please make sure the \"Host\" and \"X-Forwarded-Proto\" headers are set correctly.",
|
||||||
"action.create_repo": "created repository <a href=\"%s\">%s</a>",
|
"action.create_repo": "created repository <a href=\"%s\">%s</a>",
|
||||||
"action.rename_repo": "renamed repository from <code>%[1]s</code> to <a href=\"%[2]s\">%[3]s</a>",
|
"action.rename_repo": "renamed repository from <code>%[1]s</code> to <a href=\"%[2]s\">%[3]s</a>",
|
||||||
"action.commit_repo": "pushed to <a href=\"%[2]s\">%[3]s</a> at <a href=\"%[1]s\">%[4]s</a>",
|
"action.commit_repo": "pushed to <a href=\"%[2]s\">%[3]s</a> at <a href=\"%[1]s\">%[4]s</a>",
|
||||||
@@ -3771,8 +3771,8 @@
|
|||||||
"actions.runs.status_no_select": "All status",
|
"actions.runs.status_no_select": "All status",
|
||||||
"actions.runs.no_results": "No results matched.",
|
"actions.runs.no_results": "No results matched.",
|
||||||
"actions.runs.no_workflows": "There are no workflows yet.",
|
"actions.runs.no_workflows": "There are no workflows yet.",
|
||||||
"actions.runs.no_workflows.quick_start": "Don't know how to start with Gitea Actions? See <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">the quick start guide</a>.",
|
"actions.runs.no_workflows.quick_start": "Don't know how to start with ${APP_NAME} Actions? See <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">the quick start guide</a>.",
|
||||||
"actions.runs.no_workflows.documentation": "For more information on Gitea Actions, see <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">the documentation</a>.",
|
"actions.runs.no_workflows.documentation": "For more information on ${APP_NAME} Actions, see <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">the documentation</a>.",
|
||||||
"actions.runs.no_runs": "The workflow has no runs yet.",
|
"actions.runs.no_runs": "The workflow has no runs yet.",
|
||||||
"actions.runs.empty_commit_message": "(empty commit message)",
|
"actions.runs.empty_commit_message": "(empty commit message)",
|
||||||
"actions.runs.expire_log_message": "Logs have been purged because they were too old.",
|
"actions.runs.expire_log_message": "Logs have been purged because they were too old.",
|
||||||
|
|||||||
@@ -147,11 +147,11 @@ func httpBase(ctx *context.Context, optGitService ...string) *serviceHandler {
|
|||||||
if !ctx.IsSigned {
|
if !ctx.IsSigned {
|
||||||
// TODO: support digit auth - which would be Authorization header with digit
|
// TODO: support digit auth - which would be Authorization header with digit
|
||||||
if setting.OAuth2.Enabled {
|
if setting.OAuth2.Enabled {
|
||||||
// `Basic realm="Gitea"` tells the GCM to use builtin OAuth2 application: https://github.com/git-ecosystem/git-credential-manager/pull/1442
|
// `Basic realm="<AppName>"` tells the GCM to use builtin OAuth2 application: https://github.com/git-ecosystem/git-credential-manager/pull/1442
|
||||||
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea"`)
|
ctx.Resp.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, setting.AppName))
|
||||||
} else {
|
} else {
|
||||||
// If OAuth2 is disabled, then use another realm to avoid GCM OAuth2 attempt
|
// If OAuth2 is disabled, then use another realm to avoid GCM OAuth2 attempt
|
||||||
ctx.Resp.Header().Set("WWW-Authenticate", `Basic realm="Gitea (Basic Auth)"`)
|
ctx.Resp.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s (Basic Auth)"`, setting.AppName))
|
||||||
}
|
}
|
||||||
ctx.HTTPError(http.StatusUnauthorized)
|
ctx.HTTPError(http.StatusUnauthorized)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
// SwaggerV1Json render swagger v1 json
|
// SwaggerV1Json render swagger v1 json
|
||||||
func SwaggerV1Json(ctx *context.Context) {
|
func SwaggerV1Json(ctx *context.Context) {
|
||||||
ctx.Data["SwaggerAppVer"] = template.HTML(template.JSEscapeString(setting.AppVer))
|
ctx.Data["SwaggerAppVer"] = template.HTML(template.JSEscapeString(setting.AppVer))
|
||||||
|
ctx.Data["SwaggerAppName"] = template.HTML(template.JSEscapeString(setting.AppName))
|
||||||
ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe
|
ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe
|
||||||
ctx.JSONTemplate("swagger/v1_json")
|
ctx.JSONTemplate("swagger/v1_json")
|
||||||
}
|
}
|
||||||
@@ -20,6 +21,7 @@ func SwaggerV1Json(ctx *context.Context) {
|
|||||||
// OpenAPI3Json render OpenAPI 3.0 json (auto-converted from Swagger 2.0)
|
// OpenAPI3Json render OpenAPI 3.0 json (auto-converted from Swagger 2.0)
|
||||||
func OpenAPI3Json(ctx *context.Context) {
|
func OpenAPI3Json(ctx *context.Context) {
|
||||||
ctx.Data["SwaggerAppVer"] = template.HTML(template.JSEscapeString(setting.AppVer))
|
ctx.Data["SwaggerAppVer"] = template.HTML(template.JSEscapeString(setting.AppVer))
|
||||||
|
ctx.Data["SwaggerAppName"] = template.HTML(template.JSEscapeString(setting.AppName))
|
||||||
ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe
|
ctx.Data["SwaggerAppSubUrl"] = setting.AppSubURL // it is JS-safe
|
||||||
ctx.JSONTemplate("swagger/v1_openapi3_json")
|
ctx.JSONTemplate("swagger/v1_openapi3_json")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="admin-setting-content">
|
<div class="admin-setting-content">
|
||||||
{{if .NeedUpdate}}
|
{{if .NeedUpdate}}
|
||||||
<div class="ui positive message">
|
<div class="ui positive message">
|
||||||
<div class="header">{{svg "octicon-info"}} MokoGitea Update Available</div>
|
<div class="header">{{svg "octicon-info"}} {{AppName}} Update Available</div>
|
||||||
<p>A new version <strong>{{.LatestVersion}}</strong> is available{{if .UpdateChannel}} ({{.UpdateChannel}} channel){{end}}.
|
<p>A new version <strong>{{.LatestVersion}}</strong> is available{{if .UpdateChannel}} ({{.UpdateChannel}} channel){{end}}.
|
||||||
{{if .ReleaseURL}}<a href="{{.ReleaseURL}}" target="_blank" rel="noopener noreferrer">View release notes</a>{{end}}</p>
|
{{if .ReleaseURL}}<a href="{{.ReleaseURL}}" target="_blank" rel="noopener noreferrer">View release notes</a>{{end}}</p>
|
||||||
{{if .DockerImage}}<p><code>docker pull {{.DockerImage}}</code></p>{{end}}
|
{{if .DockerImage}}<p><code>docker pull {{.DockerImage}}</code></p>{{end}}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<footer class="page-footer" role="group" aria-label="{{ctx.Locale.Tr "aria.footer"}}">
|
<footer class="page-footer" role="group" aria-label="{{ctx.Locale.Tr "aria.footer"}}">
|
||||||
<div class="left-links" role="contentinfo" aria-label="{{ctx.Locale.Tr "aria.footer.software"}}">
|
<div class="left-links" role="contentinfo" aria-label="{{ctx.Locale.Tr "aria.footer.software"}}">
|
||||||
{{if ShowFooterPoweredBy}}
|
{{if ShowFooterPoweredBy}}
|
||||||
<a target="_blank" href="https://git.mokoconsulting.tech/MokoConsulting/MokoGitea">{{ctx.Locale.Tr "powered_by" "MokoGitea"}}</a>
|
<a target="_blank" href="https://git.mokoconsulting.tech/MokoConsulting/MokoGitea">{{ctx.Locale.Tr "powered_by" AppName}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if (or .ShowFooterVersion .PageIsAdmin)}}
|
{{if (or .ShowFooterVersion .PageIsAdmin)}}
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
{{ctx.HeadMetaContentSecurityPolicy}}
|
{{ctx.HeadMetaContentSecurityPolicy}}
|
||||||
<title>Gitea API</title>
|
<title>{{AppName}} API</title>
|
||||||
<link rel="stylesheet" href="{{ctx.CurrentWebTheme.PublicAssetURI}}">
|
<link rel="stylesheet" href="{{ctx.CurrentWebTheme.PublicAssetURI}}">
|
||||||
{{/* HINT: SWAGGER-CSS-IMPORT: import swagger styles ahead to avoid UI flicker (e.g.: the swagger-back-link element) */}}
|
{{/* HINT: SWAGGER-CSS-IMPORT: import swagger styles ahead to avoid UI flicker (e.g.: the swagger-back-link element) */}}
|
||||||
<link rel="stylesheet" href="{{AssetURI "css/swagger.css"}}">
|
<link rel="stylesheet" href="{{AssetURI "css/swagger.css"}}">
|
||||||
|
|||||||
Generated
+2
-2
@@ -11,8 +11,8 @@
|
|||||||
],
|
],
|
||||||
"swagger": "2.0",
|
"swagger": "2.0",
|
||||||
"info": {
|
"info": {
|
||||||
"description": "This documentation describes the Gitea API.",
|
"description": "This documentation describes the {{.SwaggerAppName}} API.",
|
||||||
"title": "Gitea API",
|
"title": "{{.SwaggerAppName}} API",
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT",
|
"name": "MIT",
|
||||||
"url": "http://opensource.org/licenses/MIT"
|
"url": "http://opensource.org/licenses/MIT"
|
||||||
|
|||||||
@@ -10588,12 +10588,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"description": "This documentation describes the Gitea API.",
|
"description": "This documentation describes the {{.SwaggerAppName}} API.",
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT",
|
"name": "MIT",
|
||||||
"url": "http://opensource.org/licenses/MIT"
|
"url": "http://opensource.org/licenses/MIT"
|
||||||
},
|
},
|
||||||
"title": "Gitea API",
|
"title": "{{.SwaggerAppName}} API",
|
||||||
"version": "{{.SwaggerAppVer}}"
|
"version": "{{.SwaggerAppVer}}"
|
||||||
},
|
},
|
||||||
"openapi": "3.0.3",
|
"openapi": "3.0.3",
|
||||||
|
|||||||
Reference in New Issue
Block a user