Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c33645107 | |||
| 4d15e6ccb8 | |||
| d931c71937 | |||
| 2ae0fa7920 | |||
| d1f3118a75 | |||
| 9ac91f6f28 | |||
| c067b67db5 | |||
| bf2aa07768 | |||
| a21eebeaad | |||
| 9239c931f2 | |||
| 9421bff48b | |||
| 85a904586e | |||
| f2864130b7 | |||
| 8681c3d5ee | |||
| 6ccb3f7027 | |||
| 3a375e41d6 | |||
| b48846eaf1 | |||
| 8504c96589 | |||
| 38d7c040ce | |||
| dc6b3dbf16 | |||
| 612de01a6f | |||
| 6f6a7b32c4 |
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Release
|
# INGROUP: mokocli.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||||
# 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 (skips merge commits)
|
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
||||||
@@ -34,8 +34,7 @@ jobs:
|
|||||||
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') &&
|
!startsWith(github.event.head_commit.message, 'Merge pull request')
|
||||||
!startsWith(github.event.repository.name, 'Template-')
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -44,7 +43,7 @@ jobs:
|
|||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Setup MokoCLI tools
|
- name: Setup mokocli tools
|
||||||
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
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Release
|
# INGROUP: mokocli.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||||
# PATH: /.mokogitea/workflows/auto-release.yml
|
# PATH: /templates/workflows/universal/auto-release.yml.template
|
||||||
# VERSION: 05.01.00
|
# VERSION: 05.00.00
|
||||||
# BRIEF: Universal build & release � detects platform from metadata API
|
# BRIEF: Universal build & release � detects platform from manifest.xml
|
||||||
#
|
#
|
||||||
# +=======================================================================+
|
# +=======================================================================+
|
||||||
# | UNIVERSAL BUILD & RELEASE PIPELINE |
|
# | UNIVERSAL BUILD & RELEASE PIPELINE |
|
||||||
# +=======================================================================+
|
# +=======================================================================+
|
||||||
# | |
|
# | |
|
||||||
# | Reads metadata API (joomla|dolibarr|generic) to branch logic. |
|
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
||||||
# | |
|
# | |
|
||||||
# | Platform-specific: |
|
# | Platform-specific: |
|
||||||
# | joomla: XML manifest, type-prefixed packages |
|
# | joomla: XML manifest, type-prefixed packages |
|
||||||
@@ -64,14 +64,10 @@ jobs:
|
|||||||
promote-rc:
|
promote-rc:
|
||||||
name: Promote to RC
|
name: Promote to RC
|
||||||
runs-on: release
|
runs-on: release
|
||||||
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
|
||||||
if: >-
|
if: >-
|
||||||
!startsWith(github.event.repository.name, 'Template-') &&
|
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
||||||
(
|
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) ||
|
||||||
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
||||||
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) ||
|
|
||||||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
|
||||||
)
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -79,9 +75,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Setup MokoCLI tools
|
- name: Setup mokocli tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
@@ -132,9 +127,20 @@ jobs:
|
|||||||
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
||||||
"${API_BASE}/pulls/${PR}" -d '{"head":"rc"}' >/dev/null || true
|
"${API_BASE}/pulls/${PR}" -d '{"head":"rc"}' >/dev/null || true
|
||||||
fi
|
fi
|
||||||
curl -s -X DELETE -H "$AUTH" "${API_BASE}/branches/${FROM}" >/dev/null || true
|
# Never delete permanent branches (dev/main/rc/...); only ephemeral feature branches.
|
||||||
|
case "$FROM" in
|
||||||
|
dev|main|master|rc|stable|production|release|develop|staging|beta|alpha)
|
||||||
|
echo "Keeping permanent branch ${FROM} (not deleting)" ;;
|
||||||
|
*)
|
||||||
|
curl -s -X DELETE -H "$AUTH" "${API_BASE}/branches/${FROM}" >/dev/null || true ;;
|
||||||
|
esac
|
||||||
echo "Renamed ${FROM} -> rc"
|
echo "Renamed ${FROM} -> rc"
|
||||||
|
|
||||||
|
- name: Trigger RC deploy
|
||||||
|
run: |
|
||||||
|
# Workflow-token pushes do NOT wake downstream workflows; dispatch deploy-rc explicitly.
|
||||||
|
curl -sf -X POST -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \n -H "Content-Type: application/json" \n "${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}/actions/workflows/deploy-rc.yml/dispatches" \n -d '{"ref":"rc"}' \n && echo "Dispatched deploy-rc on rc" \n || echo "::warning::deploy-rc dispatch failed (no deploy-rc.yml on rc?)"
|
||||||
|
|
||||||
- name: Checkout rc and configure git
|
- name: Checkout rc and configure git
|
||||||
run: |
|
run: |
|
||||||
git fetch origin rc
|
git fetch origin rc
|
||||||
@@ -144,12 +150,14 @@ jobs:
|
|||||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
|
|
||||||
- name: Publish RC release
|
- name: Publish RC release
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
php ${MOKO_CLI}/release_publish.php \
|
php ${MOKO_CLI}/release_publish.php \
|
||||||
--path . --stability rc --bump minor --branch rc \
|
--path . --stability rc --bump minor --branch rc \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
- name: Update RC release notes from CHANGELOG.md
|
- name: Update RC release notes from CHANGELOG.md
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
@@ -193,13 +201,9 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Build & Release Pipeline
|
name: Build & Release Pipeline
|
||||||
runs-on: release
|
runs-on: release
|
||||||
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
|
||||||
if: >-
|
if: >-
|
||||||
!startsWith(github.event.repository.name, 'Template-') &&
|
github.event.pull_request.merged == true ||
|
||||||
(
|
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
||||||
github.event.pull_request.merged == true ||
|
|
||||||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
|
||||||
)
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -207,7 +211,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: recursive
|
|
||||||
|
|
||||||
- name: Configure git for bot pushes
|
- name: Configure git for bot pushes
|
||||||
run: |
|
run: |
|
||||||
@@ -228,7 +231,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
echo "No conflict markers found"
|
echo "No conflict markers found"
|
||||||
|
|
||||||
- name: Setup MokoCLI tools
|
- name: Setup mokocli tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
@@ -374,47 +377,13 @@ jobs:
|
|||||||
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
|
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
|
||||||
DATE=$(date +%Y-%m-%d)
|
DATE=$(date +%Y-%m-%d)
|
||||||
python3 -c "
|
python3 -c "
|
||||||
import sys, re
|
import sys
|
||||||
version, date = sys.argv[1], sys.argv[2]
|
version, date = sys.argv[1], sys.argv[2]
|
||||||
lines = open('CHANGELOG.md').read().split('\n')
|
content = open('CHANGELOG.md').read()
|
||||||
h2 = re.compile(r'^##\s+\[([^\]]+)\]')
|
old = '## [Unreleased]'
|
||||||
header, sections, cur = [], [], None
|
new = f'## [Unreleased]\n\n## [{version}] --- {date}'
|
||||||
for ln in lines:
|
content = content.replace(old, new, 1)
|
||||||
m = h2.match(ln)
|
open('CHANGELOG.md', 'w').write(content)
|
||||||
if m:
|
|
||||||
if cur: sections.append(cur)
|
|
||||||
cur = {'label': m.group(1).strip(), 'head': ln, 'body': []}
|
|
||||||
elif cur is None:
|
|
||||||
header.append(ln)
|
|
||||||
else:
|
|
||||||
cur['body'].append(ln)
|
|
||||||
if cur: sections.append(cur)
|
|
||||||
def nonempty(b): return any(x.strip() for x in b)
|
|
||||||
def trim(b):
|
|
||||||
b = b[:]
|
|
||||||
while b and not b[0].strip(): b.pop(0)
|
|
||||||
while b and not b[-1].strip(): b.pop()
|
|
||||||
return b
|
|
||||||
unreleased, order, bykey = [], [], {}
|
|
||||||
for s in sections:
|
|
||||||
key = s['label'].lower()
|
|
||||||
if key == 'unreleased':
|
|
||||||
if nonempty(s['body']): unreleased += s['body']
|
|
||||||
continue
|
|
||||||
if not nonempty(s['body']): continue # drop blank release sections
|
|
||||||
if key in bykey: bykey[key]['body'] += [''] + s['body'] # merge duplicate heading (never drop content)
|
|
||||||
else: bykey[key] = s; order.append(key)
|
|
||||||
rest, seen = [bykey[k] for k in order], set(order)
|
|
||||||
out = []
|
|
||||||
htxt = '\n'.join(header).rstrip()
|
|
||||||
if htxt: out += [htxt, '']
|
|
||||||
out += ['## [Unreleased]', '']
|
|
||||||
promote = bool(unreleased) and version.lower() not in seen
|
|
||||||
if unreleased and not promote: out += trim(unreleased) + ['']
|
|
||||||
if promote: out += ['## [%s] --- %s' % (version, date), ''] + trim(unreleased) + ['']
|
|
||||||
for s in rest: out += [s['head'], ''] + trim(s['body']) + ['']
|
|
||||||
res = re.sub(r'\n{3,}', '\n\n', '\n'.join(out)).rstrip('\n') + '\n'
|
|
||||||
open('CHANGELOG.md', 'w').write(res)
|
|
||||||
" "$VERSION" "$DATE"
|
" "$VERSION" "$DATE"
|
||||||
git add CHANGELOG.md
|
git add CHANGELOG.md
|
||||||
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
|
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
|
||||||
@@ -457,7 +426,7 @@ jobs:
|
|||||||
&& echo "main branch pushed to GitHub mirror" \
|
&& echo "main branch pushed to GitHub mirror" \
|
||||||
|| echo "WARNING: GitHub mirror push failed"
|
|| echo "WARNING: GitHub mirror push failed"
|
||||||
|
|
||||||
- name: "Step 11: Delete rc branch (dev reset moved to cascade-dev.yml)"
|
- name: "Step 11: Delete rc branch 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: |
|
||||||
@@ -469,9 +438,17 @@ jobs:
|
|||||||
"${API_BASE}/branches/rc" 2>/dev/null \
|
"${API_BASE}/branches/rc" 2>/dev/null \
|
||||||
&& echo "Deleted rc branch" || echo "rc branch not found"
|
&& echo "Deleted rc branch" || echo "rc branch not found"
|
||||||
|
|
||||||
# dev is reset from main by the dedicated "Cascade Main -> Dev" workflow
|
# Delete dev branch
|
||||||
# (cascade-dev.yml), which runs after this release completes.
|
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||||
echo "rc cleaned; dev reset handled by cascade-dev.yml" >> $GITHUB_STEP_SUMMARY
|
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
|
||||||
|
|
||||||
|
# Recreate dev from main (now includes version bump + changelog promotion)
|
||||||
|
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"${API_BASE}/branches" \
|
||||||
|
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
|
||||||
|
|
||||||
|
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
- name: "Step 12: Create version branch from main"
|
- name: "Step 12: Create version branch from main"
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Universal
|
# INGROUP: MokoStandards.Universal
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||||
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Delete feature branches after PR merge
|
# BRIEF: Delete feature branches after PR merge
|
||||||
@@ -33,8 +33,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||||
# URL-encode the branch name's slashes (no PHP dependency on the runner)
|
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
||||||
ENCODED=$(printf '%s' "${BRANCH}" | sed 's|/|%2F|g')
|
|
||||||
|
|
||||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||||
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
|
|||||||
@@ -1,190 +1,10 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
# DISABLED — auto-release Step 11 recreates dev from main after every release.
|
||||||
#
|
# Cascade-dev is redundant and causes version conflicts when both main and dev
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# have different version numbers in templateDetails.xml / manifest.xml.
|
||||||
#
|
name: "Cascade Main → Dev (DISABLED)"
|
||||||
# FILE INFORMATION
|
on: workflow_dispatch
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
|
||||||
# INGROUP: MokoCLI.Cascade
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
|
||||||
# PATH: /.mokogitea/workflows/cascade-dev.yml
|
|
||||||
# VERSION: 02.01.00
|
|
||||||
# BRIEF: Cascade main -> dev; auto-merge clean, auto-resolve VERSION-stamp-only conflicts, else notify
|
|
||||||
|
|
||||||
name: "Cascade Main -> Dev"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
# Daily safety net: catches drift even when main only received [skip ci] pushes
|
|
||||||
# (which never fire the push trigger above). Off-round minute to avoid a fleet-wide spike.
|
|
||||||
schedule:
|
|
||||||
- cron: '23 7 * * *'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
|
||||||
# ntfy destination is configured via repo or org variables (org vars are inherited).
|
|
||||||
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
|
|
||||||
NTFY_TOPIC: ${{ vars.CASCADE_NTFY_TOPIC || vars.NTFY_TOPIC || 'gitea-releases' }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cascade:
|
noop:
|
||||||
name: Cascade main -> dev
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout (full history for merge/resolve)
|
- run: echo "Cascade disabled — auto-release handles dev recreation"
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
|
|
||||||
- name: Cascade main -> dev (auto-resolve version stamps, else notify)
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
run: |
|
|
||||||
set -uo pipefail
|
|
||||||
API="${MOKOGITEA_URL}/api/v1/repos/${REPO}"
|
|
||||||
AUTH="Authorization: token ${TOKEN}"
|
|
||||||
jqget() { python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('$1',''))" 2>/dev/null; }
|
|
||||||
|
|
||||||
# 0. dev must exist
|
|
||||||
if ! curl -sf -H "$AUTH" "${API}/branches/dev" >/dev/null 2>&1; then
|
|
||||||
echo "No dev branch - nothing to cascade."; exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 1. is main ahead of dev?
|
|
||||||
AHEAD=$(curl -sf -H "$AUTH" "${API}/compare/dev...main" \
|
|
||||||
| python3 -c "import sys,json; print(json.load(sys.stdin).get('total_commits',0))" 2>/dev/null || echo 0)
|
|
||||||
if [ "${AHEAD:-0}" -eq 0 ]; then
|
|
||||||
echo "dev already up to date with main."; exit 0
|
|
||||||
fi
|
|
||||||
echo "main is ${AHEAD} commit(s) ahead of dev."
|
|
||||||
|
|
||||||
# 2. reuse an open main->dev PR, else create one
|
|
||||||
PR=$(curl -sf -H "$AUTH" "${API}/pulls?state=open&base=dev" \
|
|
||||||
| python3 -c "import sys,json; d=json.load(sys.stdin); print(next((str(p['number']) for p in d if p.get('head',{}).get('ref')=='main'), ''))" 2>/dev/null || echo "")
|
|
||||||
if [ -z "$PR" ]; then
|
|
||||||
RESP=$(curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST "${API}/pulls" \
|
|
||||||
-d '{"head":"main","base":"dev","title":"chore(sync): cascade main -> dev","body":"Automated cascade of main into dev. Auto-merges when conflict-free, auto-resolves VERSION-stamp-only conflicts, otherwise left open for manual resolution."}')
|
|
||||||
PR=$(printf '%s' "$RESP" | jqget number)
|
|
||||||
if [ -z "$PR" ]; then
|
|
||||||
echo "::warning::Could not open cascade PR: $RESP"; exit 0
|
|
||||||
fi
|
|
||||||
echo "Opened cascade PR #${PR}"
|
|
||||||
else
|
|
||||||
echo "Reusing open cascade PR #${PR}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
notify() {
|
|
||||||
curl -sS \
|
|
||||||
-H "Title: ${REPO}: dev cascade needs manual merge" \
|
|
||||||
-H "Tags: warning,twisted_rightwards_arrows" \
|
|
||||||
-H "Priority: high" \
|
|
||||||
-H "Click: ${MOKOGITEA_URL}/${REPO}/pulls/${PR}" \
|
|
||||||
-d "main -> dev cascade PR #${PR} $1 It was NOT auto-merged; resolve it manually." \
|
|
||||||
"${NTFY_URL}/${NTFY_TOPIC}" || true
|
|
||||||
}
|
|
||||||
|
|
||||||
# 3. wait for MokoGitea to compute mergeability (conflict detection)
|
|
||||||
MERGEABLE=""
|
|
||||||
for _ in 1 2 3 4 5 6; do
|
|
||||||
MERGEABLE=$(curl -sf -H "$AUTH" "${API}/pulls/${PR}" | jqget mergeable)
|
|
||||||
case "$MERGEABLE" in True|False) break ;; esac
|
|
||||||
sleep 3
|
|
||||||
done
|
|
||||||
echo "mergeable=${MERGEABLE}"
|
|
||||||
|
|
||||||
# 4a. conflict-free -> merge via API (existing behaviour)
|
|
||||||
if [ "$MERGEABLE" = "True" ]; then
|
|
||||||
CODE=$(curl -s -o /tmp/merge.json -w "%{http_code}" -H "$AUTH" -H "Content-Type: application/json" \
|
|
||||||
-X POST "${API}/pulls/${PR}/merge" -d '{"Do":"merge","merge_when_checks_succeed":true}')
|
|
||||||
if [ "$CODE" -ge 200 ] && [ "$CODE" -lt 300 ]; then
|
|
||||||
echo "Cascade PR #${PR} merged (or scheduled to merge when checks pass)."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "::warning::Auto-merge returned HTTP ${CODE}: $(cat /tmp/merge.json)"
|
|
||||||
notify "could not be auto-merged (HTTP ${CODE})."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4b. conflicts -> try to auto-resolve if they are ONLY VERSION-stamp lines.
|
|
||||||
echo "PR not cleanly mergeable; checking whether conflicts are VERSION-stamp-only..."
|
|
||||||
git config user.name "MokoGitea Cascade"
|
|
||||||
git config user.email "actions@mokoconsulting.tech"
|
|
||||||
git fetch --quiet origin main dev
|
|
||||||
git checkout -B dev origin/dev
|
|
||||||
|
|
||||||
if git merge --no-ff --no-commit origin/main >/dev/null 2>&1; then
|
|
||||||
# Became clean at git level (e.g. mergeability was still computing) -> commit + push.
|
|
||||||
git commit -m "chore(sync): cascade main -> dev [skip ci]" >/dev/null
|
|
||||||
git push origin dev
|
|
||||||
echo "Cascade merged cleanly at git level and pushed to dev."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
CONFLICTS=$(git diff --name-only --diff-filter=U)
|
|
||||||
echo "Conflicted files:"; echo "${CONFLICTS}"
|
|
||||||
|
|
||||||
# A conflict is "stamp-only" when every line inside every conflict block matches
|
|
||||||
# a version-stamp pattern (VERSION: header, <version> element, or CHANGELOG title).
|
|
||||||
is_stamp_only() {
|
|
||||||
awk '
|
|
||||||
/^<<<<<<< / { inc=1; next }
|
|
||||||
inc && /^=======$/ { next }
|
|
||||||
/^>>>>>>> / { inc=0; next }
|
|
||||||
inc { if ($0 !~ /(VERSION:|<version>|# Changelog)/) { bad=1 } }
|
|
||||||
END { exit(bad ? 1 : 0) }
|
|
||||||
' "$1"
|
|
||||||
}
|
|
||||||
# Resolve a stamp-only file by keeping dev (ours) for the conflicting lines,
|
|
||||||
# preserving all auto-merged content around them.
|
|
||||||
keep_ours() {
|
|
||||||
awk '
|
|
||||||
/^<<<<<<< / { inc=1; side="ours"; next }
|
|
||||||
inc && /^=======$/ { side="theirs"; next }
|
|
||||||
/^>>>>>>> / { inc=0; next }
|
|
||||||
{ if (!inc) { print; next } if (side=="ours") print }
|
|
||||||
' "$1" > "$1.resolved" && mv "$1.resolved" "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
ALL_STAMP=1
|
|
||||||
for f in ${CONFLICTS}; do
|
|
||||||
if ! is_stamp_only "$f"; then
|
|
||||||
echo "::notice::$f has non-stamp conflicts -> manual resolution required."
|
|
||||||
ALL_STAMP=0; break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$ALL_STAMP" != "1" ]; then
|
|
||||||
git merge --abort || true
|
|
||||||
notify "has non-version-stamp conflicts and cannot be auto-resolved."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "All conflicts are VERSION-stamp-only; resolving in favour of dev."
|
|
||||||
for f in ${CONFLICTS}; do
|
|
||||||
keep_ours "$f"
|
|
||||||
git add "$f"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Best-effort: normalise stamps to dev's version if mokocli is available.
|
|
||||||
if [ -f /opt/mokocli/cli/version_check.php ]; then
|
|
||||||
php /opt/mokocli/cli/version_check.php --fix || true
|
|
||||||
git add -A
|
|
||||||
fi
|
|
||||||
|
|
||||||
git commit -m "chore(sync): cascade main -> dev (auto-resolved version stamps) [skip ci]" >/dev/null
|
|
||||||
git push origin dev
|
|
||||||
echo "Cascade auto-resolved and pushed to dev."
|
|
||||||
|
|
||||||
# Close the now-redundant PR (its changes are in dev) with an explanatory comment.
|
|
||||||
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST "${API}/issues/${PR}/comments" \
|
|
||||||
-d '{"body":"Auto-resolved VERSION-stamp-only conflicts and pushed the merge to dev. Closing."}' >/dev/null || true
|
|
||||||
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH "${API}/pulls/${PR}" \
|
|
||||||
-d '{"state":"closed"}' >/dev/null || true
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.CI
|
# INGROUP: MokoStandards.CI
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
||||||
# PATH: /.mokogitea/workflows/ci-generic.yml
|
# PATH: /.gitea/workflows/ci-generic.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
|
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
|
||||||
|
|
||||||
@@ -32,8 +32,6 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
name: Lint & Validate
|
name: Lint & Validate
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip on template repos (Template-*) — they hold placeholder scaffolding, not buildable source.
|
|
||||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -131,11 +129,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
name: Tests
|
name: Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Independent job (no `needs: lint`): the MokoGitea Actions scheduler does not
|
needs: lint
|
||||||
# offer the dependent 2nd job of a needs-chain to runners, so it stalls in
|
|
||||||
# "waiting" and is reaped by ABANDONED_JOB_TIMEOUT. Guard template repos
|
|
||||||
# directly (same condition lint uses) instead of gating on lint's result.
|
|
||||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Universal
|
# INGROUP: mokocli.Universal
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||||
# PATH: /.mokogitea/workflows/ci-issue-reporter.yml
|
# PATH: /.mokogitea/workflows/ci-issue-reporter.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Reusable workflow — creates/updates a MokoGitea issue when a CI gate fails.
|
# BRIEF: Reusable workflow — creates/updates a MokoGitea issue when a CI gate fails.
|
||||||
@@ -52,7 +52,7 @@ jobs:
|
|||||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||||
git clone --depth 1 --filter=blob:none --sparse "${MOKOGITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
git clone --depth 1 --filter=blob:none --sparse "${MOKOGITEA_URL}/MokoConsulting/MokoCLI.git" /tmp/mokocli
|
||||||
cd /tmp/mokocli && git sparse-checkout set cli/ci_issue_reporter.sh
|
cd /tmp/mokocli && git sparse-checkout set cli/ci_issue_reporter.sh
|
||||||
|
|
||||||
- name: Report CI failure
|
- name: Report CI failure
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Maintenance
|
# INGROUP: MokoStandards.Maintenance
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||||
# PATH: /.mokogitea/workflows/cleanup.yml
|
# PATH: /.gitea/workflows/cleanup.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
for BRANCH in $BRANCHES; do
|
for BRANCH in $BRANCHES; do
|
||||||
# Skip protected branches
|
# Skip protected branches
|
||||||
case "$BRANCH" in
|
case "$BRANCH" in
|
||||||
main|master|dev|develop|rc|beta|alpha|release|release/*|production|stable|staging|hotfix/*|version/*) continue ;;
|
main|master|develop|release/*|hotfix/*) continue ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Check if branch is merged into main
|
# Check if branch is merged into main
|
||||||
|
|||||||
@@ -1,25 +1,40 @@
|
|||||||
# 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
|
||||||
# BRIEF: Build and deploy MokoGitea to dev environment on push to dev branch.
|
# BRIEF: Build and deploy to the Dev environment on push to the dev branch.
|
||||||
# Production deploy (deploy-mokogitea.yml) only succeeds if dev is healthy.
|
# Portable across Go server repos: ALL deployment config comes from repo
|
||||||
|
# Actions variables (vars.*) and secrets (secrets.*), nothing hardcoded.
|
||||||
|
# The dev branch is the ongoing integration branch.
|
||||||
|
# OWNER: Template-Go (canonical source; syncs to the root workflows dir). See Template-Go#3.
|
||||||
|
#
|
||||||
|
# Required repo VARIABLES (all tier-scoped so each environment is independent —
|
||||||
|
# a repo may host its rc/dev/prod tiers on different machines):
|
||||||
|
# DEV_SSH_HOST, DEV_SSH_PORT, DEV_SSH_USERNAME - SSH deploy target for the dev tier
|
||||||
|
# DEV_REGISTRY, DEV_REGISTRY_USER, DEV_IMAGE - container registry + login user + image
|
||||||
|
# DEV_CONTAINER - compose service/container to recreate
|
||||||
|
# DEV_COMPOSE_PROJECT - docker compose -p project name
|
||||||
|
# DEV_COMPOSE_DIR - dir containing docker-compose.yml on host
|
||||||
|
# DEV_SOURCE_DIR - build source checkout on host
|
||||||
|
# DEV_TAG_ENV - compose env-var name that pins the image tag
|
||||||
|
# DEV_HEALTH_URL - external URL to verify after deploy
|
||||||
|
# Required SECRETS (already configured org-wide; reused, not re-set):
|
||||||
|
# DEPLOY_SSH_KEY - deploy private key (repo/org secret)
|
||||||
|
# MOKOGITEA_TOKEN - registry/API token (org secret)
|
||||||
|
|
||||||
name: Deploy MokoGitea (Dev)
|
name: Deploy (Dev)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
|
# Manual trigger for isolated end-to-end tests.
|
||||||
|
# Runs on the ref it is dispatched from.
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: deploy-mokogitea-dev
|
group: deploy-dev
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: git.mokoconsulting.tech
|
|
||||||
IMAGE: mokoconsulting/mokogitea
|
|
||||||
DEPLOY_HOST: git.mokoconsulting.tech
|
|
||||||
DEPLOY_PORT: 2918
|
|
||||||
DEPLOY_USER: mokoconsulting
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -47,22 +62,21 @@ jobs:
|
|||||||
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
|
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
|
||||||
chmod 600 ~/.ssh/deploy_key
|
chmod 600 ~/.ssh/deploy_key
|
||||||
|
|
||||||
- name: Build and deploy to dev via SSH
|
- name: Build and deploy to RC via SSH
|
||||||
env:
|
env:
|
||||||
REGISTRY_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
TAG: ${{ steps.config.outputs.tag }}
|
TAG: ${{ steps.config.outputs.tag }}
|
||||||
run: |
|
run: |
|
||||||
# Inject runner-side values (TAG, REGISTRY_TOKEN) into the remote shell's
|
# Runner-side values (TAG, REGISTRY_TOKEN) are injected into the remote shell
|
||||||
# environment via a command prefix, then use a *quoted* heredoc so every
|
# via an env prefix; a *quoted* heredoc keeps every $var expanding once, on the
|
||||||
# $var below expands in exactly one place: the remote dev host. This avoids
|
# remote. Repo variables (vars.*) are substituted inline by Actions before ssh.
|
||||||
# the local-vs-remote expansion trap that previously left TAG empty.
|
ssh -i ~/.ssh/deploy_key -p ${{ vars.DEV_SSH_PORT }} \
|
||||||
ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} \
|
|
||||||
-o ConnectTimeout=30 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
-o ConnectTimeout=30 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||||
-o ServerAliveInterval=30 -o ServerAliveCountMax=10 \
|
-o ServerAliveInterval=30 -o ServerAliveCountMax=10 \
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
${{ vars.DEV_SSH_USERNAME }}@${{ vars.DEV_SSH_HOST }} \
|
||||||
"TAG='$TAG' REGISTRY_TOKEN='$REGISTRY_TOKEN' bash -s" <<'DEPLOY_EOF'
|
"TAG='$TAG' REGISTRY_TOKEN='$REGISTRY_TOKEN' bash -s" <<'DEPLOY_EOF'
|
||||||
set -e
|
set -e
|
||||||
echo 'SSH connected to dev environment'
|
echo 'SSH connected to Dev environment'
|
||||||
|
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
echo 'ERROR: TAG is empty; refusing to build an untagged image' >&2
|
echo 'ERROR: TAG is empty; refusing to build an untagged image' >&2
|
||||||
@@ -76,52 +90,47 @@ jobs:
|
|||||||
docker image prune -af 2>/dev/null || true
|
docker image prune -af 2>/dev/null || true
|
||||||
|
|
||||||
echo 'Pulling source...'
|
echo 'Pulling source...'
|
||||||
SOURCE_DIR=/opt/gitea-dev/source
|
SOURCE_DIR='${{ vars.DEV_SOURCE_DIR }}'
|
||||||
if [ ! -d "$SOURCE_DIR/.git" ]; then
|
if [ ! -d "$SOURCE_DIR/.git" ]; then
|
||||||
git clone -b dev https://git.mokoconsulting.tech/MokoConsulting/MokoGitea-Fork.git "$SOURCE_DIR"
|
git clone -b dev https://x-access-token:${REGISTRY_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git "$SOURCE_DIR"
|
||||||
fi
|
fi
|
||||||
cd "$SOURCE_DIR"
|
cd "$SOURCE_DIR"
|
||||||
git remote set-url origin https://git.mokoconsulting.tech/MokoConsulting/MokoGitea-Fork.git 2>/dev/null || true
|
git remote set-url origin https://x-access-token:${REGISTRY_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git 2>/dev/null || true
|
||||||
git fetch origin dev
|
git fetch origin dev
|
||||||
git reset --hard origin/dev
|
git reset --hard origin/dev
|
||||||
|
|
||||||
echo "Building Docker image: ${{ env.REGISTRY }}/${{ env.IMAGE }}:$TAG"
|
echo "Building image: ${{ vars.DEV_REGISTRY }}/${{ vars.DEV_IMAGE }}:$TAG"
|
||||||
docker build --no-cache --build-arg GOFLAGS='-p 1' \
|
docker build --no-cache --build-arg GOFLAGS='-p 1' \
|
||||||
--tag "${{ env.REGISTRY }}/${{ env.IMAGE }}:$TAG" \
|
--tag "${{ vars.DEV_REGISTRY }}/${{ vars.DEV_IMAGE }}:$TAG" \
|
||||||
-f Dockerfile .
|
-f Dockerfile .
|
||||||
|
|
||||||
echo 'Pushing to registry...'
|
echo 'Pushing to registry...'
|
||||||
echo "$REGISTRY_TOKEN" | docker login ${{ env.REGISTRY }} -u ${{ env.DEPLOY_USER }} --password-stdin
|
echo "$REGISTRY_TOKEN" | docker login ${{ vars.DEV_REGISTRY }} -u ${{ vars.DEV_REGISTRY_USER }} --password-stdin
|
||||||
docker push "${{ env.REGISTRY }}/${{ env.IMAGE }}:$TAG"
|
docker push "${{ vars.DEV_REGISTRY }}/${{ vars.DEV_IMAGE }}:$TAG"
|
||||||
|
|
||||||
echo 'Restarting dev container...'
|
echo 'Restarting Dev container...'
|
||||||
cd /opt/gitea-dev
|
cd '${{ vars.DEV_COMPOSE_DIR }}'
|
||||||
# The dev service in the SHARED compose file reads its image tag from
|
# Drive the rc service image tag via its compose env-var (no sed on the shared
|
||||||
# ${MOKOGITEA_DEV_TAG}. Drive it from the freshly built tag instead of
|
# file); remove any lingering fixed-name container first, then force-recreate.
|
||||||
# rewriting the file with sed: the old sed pattern matched the *prod*
|
docker rm -f '${{ vars.DEV_CONTAINER }}' 2>/dev/null || true
|
||||||
# service line (container_name: mokogitea) and left the dev service pinned
|
${{ vars.DEV_TAG_ENV }}="$TAG" docker compose -p '${{ vars.DEV_COMPOSE_PROJECT }}' up -d --force-recreate '${{ vars.DEV_CONTAINER }}'
|
||||||
# to a stale image, so every dev deploy recreated old code while silently
|
|
||||||
# corrupting the prod image pin. The env-var only affects the dev service.
|
|
||||||
# Remove any lingering fixed-name container first so the recreate can't hit
|
|
||||||
# a name conflict, pin the project name for determinism, then force-recreate.
|
|
||||||
docker rm -f mokogitea-dev 2>/dev/null || true
|
|
||||||
MOKOGITEA_DEV_TAG="$TAG" docker compose -p gitea-dev up -d --force-recreate mokogitea-dev
|
|
||||||
|
|
||||||
echo 'Health check...'
|
echo 'Health check...'
|
||||||
for i in 1 2 3 4 5 6 7 8; do
|
for i in 1 2 3 4 5 6 7 8; do
|
||||||
sleep 15
|
sleep 15
|
||||||
if docker inspect --format="$HEALTH_FMT" mokogitea-dev 2>/dev/null | grep -q healthy; then
|
if docker inspect --format="$HEALTH_FMT" '${{ vars.DEV_CONTAINER }}' 2>/dev/null | grep -q healthy; then
|
||||||
echo 'Dev container healthy!'
|
echo 'Dev container healthy!'
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "Waiting... (attempt $i/8)"
|
echo "Waiting... (attempt $i/8)"
|
||||||
done
|
done
|
||||||
echo 'Health check failed'
|
echo 'Health check failed'
|
||||||
docker logs mokogitea-dev --tail 20
|
docker logs '${{ vars.DEV_CONTAINER }}' --tail 20
|
||||||
exit 1
|
exit 1
|
||||||
DEPLOY_EOF
|
DEPLOY_EOF
|
||||||
|
|
||||||
- name: Verify dev instance
|
- name: Verify Dev instance
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
sleep 5
|
sleep 5
|
||||||
curl -sf https://git.dev.mokoconsulting.tech/api/healthz && echo " Dev API healthy"
|
ssh -i ~/.ssh/deploy_key -p ${{ vars.DEV_SSH_PORT }} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ vars.DEV_SSH_USERNAME }}@${{ vars.DEV_SSH_HOST }} "curl -sf '${{ vars.DEV_HEALTH_URL }}'" && echo " Dev API healthy"
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
# BRIEF: Build MokoGitea Docker image, push to registry, and deploy
|
|
||||||
|
|
||||||
name: Deploy MokoGitea
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version:
|
|
||||||
description: 'Version tag'
|
|
||||||
required: true
|
|
||||||
default: 'latest'
|
|
||||||
environment:
|
|
||||||
description: 'Target environment'
|
|
||||||
required: true
|
|
||||||
default: 'dev'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- dev
|
|
||||||
- production
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: deploy-mokogitea
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: git.mokoconsulting.tech
|
|
||||||
IMAGE: mokoconsulting/mokogitea
|
|
||||||
DEPLOY_HOST: git.mokoconsulting.tech
|
|
||||||
DEPLOY_PORT: 2918
|
|
||||||
DEPLOY_USER: mokoconsulting
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
# NOTE: removed a "Verify dev environment is healthy" gate that curled
|
|
||||||
# git.dev and `exit 1` on failure — gating a PROD deploy on a live DEV
|
|
||||||
# health check is a cross-tier false-negative (a transient dev blip blocked
|
|
||||||
# prod). Pre-prod validation is the dev -> rc -> main pipeline + RC env.
|
|
||||||
- name: Checkout source (for version detection)
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Determine settings
|
|
||||||
id: config
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" = "push" ]; then
|
|
||||||
VERSION=$(git describe --tags --always 2>/dev/null || echo "dev-$(git rev-parse --short HEAD)")
|
|
||||||
ENV="production"
|
|
||||||
else
|
|
||||||
VERSION="${{ github.event.inputs.version }}"
|
|
||||||
ENV="${{ github.event.inputs.environment }}"
|
|
||||||
fi
|
|
||||||
if [ "$ENV" = "production" ]; then
|
|
||||||
echo "compose_dir=/opt/gitea-dev" >> $GITHUB_OUTPUT
|
|
||||||
echo "container=mokogitea" >> $GITHUB_OUTPUT
|
|
||||||
echo "source_dir=/opt/gitea/source" >> $GITHUB_OUTPUT
|
|
||||||
echo "branch=main" >> $GITHUB_OUTPUT
|
|
||||||
echo "tag=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "tag_env=MOKOGITEA_TAG" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "compose_dir=/opt/gitea-dev" >> $GITHUB_OUTPUT
|
|
||||||
echo "container=mokogitea-dev" >> $GITHUB_OUTPUT
|
|
||||||
echo "source_dir=/opt/gitea-dev/source" >> $GITHUB_OUTPUT
|
|
||||||
echo "branch=dev" >> $GITHUB_OUTPUT
|
|
||||||
echo "tag=$VERSION-dev" >> $GITHUB_OUTPUT
|
|
||||||
echo "tag_env=MOKOGITEA_DEV_TAG" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Write deploy key
|
|
||||||
env:
|
|
||||||
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
|
|
||||||
chmod 600 ~/.ssh/deploy_key
|
|
||||||
|
|
||||||
- name: Build and deploy via SSH
|
|
||||||
env:
|
|
||||||
REGISTRY_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
TAG: ${{ steps.config.outputs.tag }}
|
|
||||||
BRANCH: ${{ steps.config.outputs.branch }}
|
|
||||||
SOURCE_DIR: ${{ steps.config.outputs.source_dir }}
|
|
||||||
COMPOSE_DIR: ${{ steps.config.outputs.compose_dir }}
|
|
||||||
CONTAINER: ${{ steps.config.outputs.container }}
|
|
||||||
TAG_ENV: ${{ steps.config.outputs.tag_env }}
|
|
||||||
run: |
|
|
||||||
HEALTH_FMT='${{ '{{' }}.State.Health.Status${{ '}}' }}'
|
|
||||||
IMAGE_FMT='Image: ${{ '{{' }}.Config.Image${{ '}}' }}'
|
|
||||||
|
|
||||||
ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} \
|
|
||||||
-o ConnectTimeout=30 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
|
||||||
-o ServerAliveInterval=30 -o ServerAliveCountMax=10 \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} bash -s <<DEPLOY_EOF
|
|
||||||
set -e
|
|
||||||
echo 'SSH connected'
|
|
||||||
|
|
||||||
echo 'Cleaning Docker build cache...'
|
|
||||||
docker builder prune -af 2>/dev/null || true
|
|
||||||
docker image prune -af 2>/dev/null || true
|
|
||||||
free -m | head -3
|
|
||||||
|
|
||||||
echo 'Pulling source...'
|
|
||||||
if [ ! -d $SOURCE_DIR/.git ]; then
|
|
||||||
git clone -b $BRANCH https://git.mokoconsulting.tech/MokoConsulting/MokoGitea-Fork.git $SOURCE_DIR
|
|
||||||
fi
|
|
||||||
cd $SOURCE_DIR
|
|
||||||
# Ensure remote points to MokoGitea-Fork (not the upstream fork)
|
|
||||||
git remote set-url origin https://git.mokoconsulting.tech/MokoConsulting/MokoGitea-Fork.git 2>/dev/null || true
|
|
||||||
git fetch origin $BRANCH
|
|
||||||
git reset --hard origin/$BRANCH
|
|
||||||
|
|
||||||
echo 'Building Docker image...'
|
|
||||||
docker build --no-cache --build-arg GOFLAGS='-p 1' \
|
|
||||||
--tag ${{ env.REGISTRY }}/${{ env.IMAGE }}:$TAG \
|
|
||||||
--tag ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest \
|
|
||||||
-f Dockerfile .
|
|
||||||
|
|
||||||
echo 'Pushing to registry...'
|
|
||||||
echo '$REGISTRY_TOKEN' | docker login ${{ env.REGISTRY }} -u ${{ env.DEPLOY_USER }} --password-stdin
|
|
||||||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:$TAG
|
|
||||||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
|
|
||||||
|
|
||||||
echo 'Restarting container...'
|
|
||||||
cd $COMPOSE_DIR
|
|
||||||
# Drive ONLY the target service's tag via its compose env-var (no sed —
|
|
||||||
# a blanket 'sed s|mokogitea:...|' clobbered the dev + rc service tags too).
|
|
||||||
# Remove any lingering fixed-name container first (avoids the "name already
|
|
||||||
# in use" conflict), then force-recreate under the shared compose project.
|
|
||||||
docker rm -f $CONTAINER 2>/dev/null || true
|
|
||||||
env $TAG_ENV="$TAG" docker compose -p gitea-dev up -d --force-recreate $CONTAINER
|
|
||||||
|
|
||||||
echo 'Health check...'
|
|
||||||
for i in 1 2 3 4 5 6 7 8; do
|
|
||||||
sleep 15
|
|
||||||
if docker inspect --format='$HEALTH_FMT' $CONTAINER 2>/dev/null | grep -q healthy; then
|
|
||||||
echo 'Container healthy!'
|
|
||||||
docker inspect --format='$IMAGE_FMT' $CONTAINER
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Waiting... (attempt \$i/8)"
|
|
||||||
done
|
|
||||||
echo 'Health check failed'
|
|
||||||
docker logs $CONTAINER --tail 20
|
|
||||||
exit 1
|
|
||||||
DEPLOY_EOF
|
|
||||||
|
|
||||||
- name: Verify
|
|
||||||
run: |
|
|
||||||
sleep 5
|
|
||||||
curl -sf https://${{ env.DEPLOY_HOST }}/api/healthz && echo " API healthy"
|
|
||||||
|
|
||||||
- name: Notify on failure
|
|
||||||
if: failure()
|
|
||||||
run: echo "::error::Deploy failed for ${{ steps.config.outputs.tag }}"
|
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# BRIEF: Build and deploy to the Prod environment on push to the main branch.
|
||||||
|
# Portable across Go server repos: ALL deployment config comes from repo
|
||||||
|
# Actions variables (vars.*) and secrets (secrets.*), nothing hardcoded.
|
||||||
|
# Prod deploys on merge to main (dev -> rc -> main pipeline).
|
||||||
|
# OWNER: Template-Go (canonical source; syncs to the root workflows dir). See Template-Go#3.
|
||||||
|
#
|
||||||
|
# Required repo VARIABLES (all tier-scoped so each environment is independent —
|
||||||
|
# a repo may host its rc/dev/prod tiers on different machines):
|
||||||
|
# PROD_SSH_HOST, PROD_SSH_PORT, PROD_SSH_USERNAME - SSH deploy target for the prod tier
|
||||||
|
# PROD_REGISTRY, PROD_REGISTRY_USER, PROD_IMAGE - container registry + login user + image
|
||||||
|
# PROD_CONTAINER - compose service/container to recreate
|
||||||
|
# PROD_COMPOSE_PROJECT - docker compose -p project name
|
||||||
|
# PROD_COMPOSE_DIR - dir containing docker-compose.yml on host
|
||||||
|
# PROD_SOURCE_DIR - build source checkout on host
|
||||||
|
# PROD_TAG_ENV - compose env-var name that pins the image tag
|
||||||
|
# PROD_HEALTH_URL - external URL to verify after deploy
|
||||||
|
# Required SECRETS (already configured org-wide; reused, not re-set):
|
||||||
|
# DEPLOY_SSH_KEY - deploy private key (repo/org secret)
|
||||||
|
# MOKOGITEA_TOKEN - registry/API token (org secret)
|
||||||
|
|
||||||
|
name: Deploy (Prod)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
# Manual trigger for a prod re-deploy.
|
||||||
|
# Runs on the ref it is dispatched from (use main).
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: deploy-prod
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-prod:
|
||||||
|
name: "Build & Deploy to Prod"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout source
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Determine version
|
||||||
|
id: config
|
||||||
|
run: |
|
||||||
|
VERSION=$(git describe --tags --always 2>/dev/null || echo "$(git rev-parse --short HEAD)")
|
||||||
|
echo "tag=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Version: ${VERSION}"
|
||||||
|
|
||||||
|
- name: Write deploy key
|
||||||
|
env:
|
||||||
|
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
|
||||||
|
chmod 600 ~/.ssh/deploy_key
|
||||||
|
|
||||||
|
- name: Build and deploy to RC via SSH
|
||||||
|
env:
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
|
TAG: ${{ steps.config.outputs.tag }}
|
||||||
|
run: |
|
||||||
|
# Runner-side values (TAG, REGISTRY_TOKEN) are injected into the remote shell
|
||||||
|
# via an env prefix; a *quoted* heredoc keeps every $var expanding once, on the
|
||||||
|
# remote. Repo variables (vars.*) are substituted inline by Actions before ssh.
|
||||||
|
ssh -i ~/.ssh/deploy_key -p ${{ vars.PROD_SSH_PORT }} \
|
||||||
|
-o ConnectTimeout=30 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||||
|
-o ServerAliveInterval=30 -o ServerAliveCountMax=10 \
|
||||||
|
${{ vars.PROD_SSH_USERNAME }}@${{ vars.PROD_SSH_HOST }} \
|
||||||
|
"TAG='$TAG' REGISTRY_TOKEN='$REGISTRY_TOKEN' bash -s" <<'DEPLOY_EOF'
|
||||||
|
set -e
|
||||||
|
echo 'SSH connected to Prod environment'
|
||||||
|
|
||||||
|
if [ -z "$TAG" ]; then
|
||||||
|
echo 'ERROR: TAG is empty; refusing to build an untagged image' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HEALTH_FMT='{{.State.Health.Status}}'
|
||||||
|
|
||||||
|
echo 'Cleaning Docker build cache...'
|
||||||
|
docker builder prune -af 2>/dev/null || true
|
||||||
|
docker image prune -af 2>/dev/null || true
|
||||||
|
|
||||||
|
echo 'Pulling source...'
|
||||||
|
SOURCE_DIR='${{ vars.PROD_SOURCE_DIR }}'
|
||||||
|
if [ ! -d "$SOURCE_DIR/.git" ]; then
|
||||||
|
git clone -b main https://x-access-token:${REGISTRY_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git "$SOURCE_DIR"
|
||||||
|
fi
|
||||||
|
cd "$SOURCE_DIR"
|
||||||
|
git remote set-url origin https://x-access-token:${REGISTRY_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git 2>/dev/null || true
|
||||||
|
git fetch origin main
|
||||||
|
git reset --hard origin/main
|
||||||
|
|
||||||
|
echo "Building image: ${{ vars.PROD_REGISTRY }}/${{ vars.PROD_IMAGE }}:$TAG"
|
||||||
|
docker build --no-cache --build-arg GOFLAGS='-p 1' \
|
||||||
|
--tag "${{ vars.PROD_REGISTRY }}/${{ vars.PROD_IMAGE }}:$TAG" \
|
||||||
|
-f Dockerfile .
|
||||||
|
|
||||||
|
echo 'Pushing to registry...'
|
||||||
|
echo "$REGISTRY_TOKEN" | docker login ${{ vars.PROD_REGISTRY }} -u ${{ vars.PROD_REGISTRY_USER }} --password-stdin
|
||||||
|
docker push "${{ vars.PROD_REGISTRY }}/${{ vars.PROD_IMAGE }}:$TAG"
|
||||||
|
|
||||||
|
echo 'Restarting Prod container...'
|
||||||
|
cd '${{ vars.PROD_COMPOSE_DIR }}'
|
||||||
|
# Drive the rc service image tag via its compose env-var (no sed on the shared
|
||||||
|
# file); remove any lingering fixed-name container first, then force-recreate.
|
||||||
|
docker rm -f '${{ vars.PROD_CONTAINER }}' 2>/dev/null || true
|
||||||
|
${{ vars.PROD_TAG_ENV }}="$TAG" docker compose -p '${{ vars.PROD_COMPOSE_PROJECT }}' up -d --force-recreate '${{ vars.PROD_CONTAINER }}'
|
||||||
|
|
||||||
|
echo 'Health check...'
|
||||||
|
for i in 1 2 3 4 5 6 7 8; do
|
||||||
|
sleep 15
|
||||||
|
if docker inspect --format="$HEALTH_FMT" '${{ vars.PROD_CONTAINER }}' 2>/dev/null | grep -q healthy; then
|
||||||
|
echo 'Prod container healthy!'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Waiting... (attempt $i/8)"
|
||||||
|
done
|
||||||
|
echo 'Health check failed'
|
||||||
|
docker logs '${{ vars.PROD_CONTAINER }}' --tail 20
|
||||||
|
exit 1
|
||||||
|
DEPLOY_EOF
|
||||||
|
|
||||||
|
- name: Verify Prod instance
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
sleep 5
|
||||||
|
ssh -i ~/.ssh/deploy_key -p ${{ vars.PROD_SSH_PORT }} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ vars.PROD_SSH_USERNAME }}@${{ vars.PROD_SSH_HOST }} "curl -sf '${{ vars.PROD_HEALTH_URL }}'" && echo " Prod API healthy"
|
||||||
@@ -94,10 +94,10 @@ jobs:
|
|||||||
echo 'Pulling source...'
|
echo 'Pulling source...'
|
||||||
SOURCE_DIR='${{ vars.RC_SOURCE_DIR }}'
|
SOURCE_DIR='${{ vars.RC_SOURCE_DIR }}'
|
||||||
if [ ! -d "$SOURCE_DIR/.git" ]; then
|
if [ ! -d "$SOURCE_DIR/.git" ]; then
|
||||||
git clone -b rc ${{ github.server_url }}/${{ github.repository }}.git "$SOURCE_DIR"
|
git clone -b rc https://x-access-token:${REGISTRY_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git "$SOURCE_DIR"
|
||||||
fi
|
fi
|
||||||
cd "$SOURCE_DIR"
|
cd "$SOURCE_DIR"
|
||||||
git remote set-url origin ${{ github.server_url }}/${{ github.repository }}.git 2>/dev/null || true
|
git remote set-url origin https://x-access-token:${REGISTRY_TOKEN}@git.mokoconsulting.tech/${{ github.repository }}.git 2>/dev/null || true
|
||||||
git fetch origin rc
|
git fetch origin rc
|
||||||
git reset --hard origin/rc
|
git reset --hard origin/rc
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ jobs:
|
|||||||
DEPLOY_EOF
|
DEPLOY_EOF
|
||||||
|
|
||||||
- name: Verify RC instance
|
- name: Verify RC instance
|
||||||
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
sleep 5
|
sleep 5
|
||||||
curl -sf "${{ vars.RC_HEALTH_URL }}" && echo " RC API healthy"
|
ssh -i ~/.ssh/deploy_key -p ${{ vars.RC_SSH_PORT }} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ vars.RC_SSH_USERNAME }}@${{ vars.RC_SSH_HOST }} "curl -sf '${{ vars.RC_HEALTH_URL }}'" && echo " RC API healthy"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Security
|
# INGROUP: MokoStandards.Security
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||||
# PATH: /.mokogitea/workflows/gitleaks.yml
|
# PATH: /templates/workflows/gitleaks.yml.template
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Automation
|
# INGROUP: mokocli.Automation
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Auto-create feature branch when an issue is opened
|
# BRIEF: Auto-create feature branch when an issue is opened
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Notifications
|
# INGROUP: MokoStandards.Notifications
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||||
# PATH: /.mokogitea/workflows/notify.yml
|
# PATH: /.gitea/workflows/notify.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@ name: "Universal: Notifications"
|
|||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows:
|
workflows:
|
||||||
- "Universal: Build & Release"
|
- "Joomla Build & Release"
|
||||||
- "Joomla: Extension CI"
|
- "Joomla Extension CI"
|
||||||
- "Generic: Project CI"
|
- "Deploy"
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
|
|
||||||
@@ -46,13 +46,13 @@ jobs:
|
|||||||
WORKFLOW="${{ github.event.workflow_run.name }}"
|
WORKFLOW="${{ github.event.workflow_run.name }}"
|
||||||
URL="${{ github.event.workflow_run.html_url }}"
|
URL="${{ github.event.workflow_run.html_url }}"
|
||||||
|
|
||||||
curl -sS --retry 3 --retry-connrefused --retry-delay 2 --max-time 20 \
|
curl -sS \
|
||||||
-H "Title: ${REPO} released" \
|
-H "Title: ${REPO} released" \
|
||||||
-H "Tags: white_check_mark,package" \
|
-H "Tags: white_check_mark,package" \
|
||||||
-H "Priority: default" \
|
-H "Priority: default" \
|
||||||
-H "Click: ${URL}" \
|
-H "Click: ${URL}" \
|
||||||
-d "${WORKFLOW} completed successfully." \
|
-d "${WORKFLOW} completed successfully." \
|
||||||
"${NTFY_URL}/${NTFY_TOPIC}" || echo "::warning::ntfy notification could not be delivered (non-fatal)"
|
"${NTFY_URL}/${NTFY_TOPIC}"
|
||||||
|
|
||||||
- name: Notify on failure
|
- name: Notify on failure
|
||||||
if: github.event.workflow_run.conclusion == 'failure'
|
if: github.event.workflow_run.conclusion == 'failure'
|
||||||
@@ -61,10 +61,10 @@ jobs:
|
|||||||
WORKFLOW="${{ github.event.workflow_run.name }}"
|
WORKFLOW="${{ github.event.workflow_run.name }}"
|
||||||
URL="${{ github.event.workflow_run.html_url }}"
|
URL="${{ github.event.workflow_run.html_url }}"
|
||||||
|
|
||||||
curl -sS --retry 3 --retry-connrefused --retry-delay 2 --max-time 20 \
|
curl -sS \
|
||||||
-H "Title: ${REPO} workflow failed" \
|
-H "Title: ${REPO} workflow failed" \
|
||||||
-H "Tags: x,warning" \
|
-H "Tags: x,warning" \
|
||||||
-H "Priority: high" \
|
-H "Priority: high" \
|
||||||
-H "Click: ${URL}" \
|
-H "Click: ${URL}" \
|
||||||
-d "${WORKFLOW} failed. Check the run for details." \
|
-d "${WORKFLOW} failed. Check the run for details." \
|
||||||
"${NTFY_URL}/${NTFY_TOPIC}" || echo "::warning::ntfy notification could not be delivered (non-fatal)"
|
"${NTFY_URL}/${NTFY_TOPIC}"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.CI
|
# INGROUP: mokocli.CI
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||||
# PATH: /.mokogitea/workflows/pr-check.yml
|
# PATH: /templates/workflows/universal/pr-check.yml.template
|
||||||
# VERSION: 09.23.00
|
# VERSION: 09.23.00
|
||||||
# BRIEF: PR gate — branch policy + code validation before merge
|
# BRIEF: PR gate — branch policy + code validation before merge
|
||||||
|
|
||||||
@@ -47,15 +47,15 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
fix/*|bugfix/*)
|
fix/*|bugfix/*)
|
||||||
if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then
|
if [ "$BASE" != "dev" ]; then
|
||||||
ALLOWED=false
|
ALLOWED=false
|
||||||
REASON="Fix branches must target 'dev' or 'main', not '${BASE}'"
|
REASON="Fix branches must target 'dev', not '${BASE}'"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
patch/*)
|
patch/*)
|
||||||
if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ] && [ "$BASE" != "main" ]; then
|
if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then
|
||||||
ALLOWED=false
|
ALLOWED=false
|
||||||
REASON="Patch branches must target 'dev', 'rc', or 'main', not '${BASE}'"
|
REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
hotfix/*)
|
hotfix/*)
|
||||||
@@ -86,8 +86,7 @@ jobs:
|
|||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
|
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`fix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`patch/*\` → \`dev\`, \`rc\`, or \`main\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -97,80 +96,6 @@ jobs:
|
|||||||
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
||||||
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# ── Docs Update Gate (main PRs) ─────────────────────────────────────────
|
|
||||||
require-docs:
|
|
||||||
name: Require Docs Update
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Enforce only on PRs merging into main: README.md + CHANGELOG.md must both be updated.
|
|
||||||
if: ${{ github.base_ref == 'main' }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Require README.md and CHANGELOG.md in the PR diff
|
|
||||||
run: |
|
|
||||||
BASE="${{ github.event.pull_request.base.sha }}"
|
|
||||||
HEAD="${{ github.event.pull_request.head.sha }}"
|
|
||||||
CHANGED="$(git diff --name-only "$BASE" "$HEAD" 2>/dev/null || true)"
|
|
||||||
if [ -z "$CHANGED" ]; then
|
|
||||||
git fetch -q origin "${{ github.base_ref }}" 2>/dev/null || true
|
|
||||||
CHANGED="$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
echo "Changed files in PR:"
|
|
||||||
echo "$CHANGED"
|
|
||||||
MISSING=""
|
|
||||||
echo "$CHANGED" | grep -qxE 'README\.md' || MISSING="README.md"
|
|
||||||
echo "$CHANGED" | grep -qxE 'CHANGELOG\.md' || MISSING="${MISSING:+$MISSING, }CHANGELOG.md"
|
|
||||||
if [ -n "$MISSING" ]; then
|
|
||||||
echo "::error::PRs into main must update: ${MISSING}"
|
|
||||||
{
|
|
||||||
echo "## Docs Update Required"
|
|
||||||
echo ""
|
|
||||||
echo "PRs merging into \`main\` must update both **README.md** and **CHANGELOG.md**."
|
|
||||||
echo ""
|
|
||||||
echo "Not updated in this PR: **${MISSING}**"
|
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Docs update present (README.md + CHANGELOG.md)"
|
|
||||||
echo "## Docs Update: Passed" >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
|
|
||||||
# ── Wiki Update Reminder (main PRs, non-blocking) ───────────────────────
|
|
||||||
wiki-reminder:
|
|
||||||
name: Wiki Update Reminder
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.base_ref == 'main' }}
|
|
||||||
steps:
|
|
||||||
- name: Remind to update the wiki
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
SERVER: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
PR: ${{ github.event.pull_request.number }}
|
|
||||||
run: |
|
|
||||||
set -uo pipefail
|
|
||||||
{
|
|
||||||
echo "## Wiki Update Reminder"
|
|
||||||
echo ""
|
|
||||||
echo "Docs are **wiki-first** at MokoConsulting. If this change affects behavior, usage, configuration, or standards, update the repo wiki:"
|
|
||||||
echo ""
|
|
||||||
echo "- ${SERVER}/${REPO}/wiki"
|
|
||||||
echo ""
|
|
||||||
echo "_Non-blocking reminder._"
|
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
# Post a single PR comment (idempotent via hidden marker); best-effort, never fails.
|
|
||||||
API="${SERVER}/api/v1/repos/${REPO}/issues/${PR}/comments"
|
|
||||||
if [ -n "${TOKEN:-}" ] && [ -n "${PR:-}" ]; then
|
|
||||||
existing="$(curl -sf -H "Authorization: token ${TOKEN}" "$API" 2>/dev/null | grep -c 'wiki-reminder' || true)"
|
|
||||||
if [ "${existing:-0}" -eq 0 ]; then
|
|
||||||
curl -sf -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -X POST "$API" \
|
|
||||||
-d '{"body":"<!-- wiki-reminder -->\n\n**Wiki reminder:** docs are wiki-first -- if this PR changes behavior, usage, config, or standards, please update the repo wiki before/after merge. _(non-blocking)_"}' >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo "Wiki reminder emitted (non-blocking)."
|
|
||||||
|
|
||||||
# ── Secret Scanning ──────────────────────────────────────────────────
|
# ── Secret Scanning ──────────────────────────────────────────────────
|
||||||
gitleaks:
|
gitleaks:
|
||||||
name: Secret Scan
|
name: Secret Scan
|
||||||
@@ -201,8 +126,6 @@ jobs:
|
|||||||
validate:
|
validate:
|
||||||
name: Validate PR
|
name: Validate PR
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip on template repos (Template-*) — no real manifest/source/changelog to validate.
|
|
||||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -210,7 +133,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Check for merge conflict markers
|
- name: Check for merge conflict markers
|
||||||
run: |
|
run: |
|
||||||
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --exclude-dir='.git' --exclude-dir='.mokogitea' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
|
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
|
||||||
if [ -n "$CONFLICTS" ]; then
|
if [ -n "$CONFLICTS" ]; then
|
||||||
echo "::error::Merge conflict markers found in source files"
|
echo "::error::Merge conflict markers found in source files"
|
||||||
echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY
|
echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -258,9 +181,8 @@ jobs:
|
|||||||
while IFS= read -r -d '' file; do
|
while IFS= read -r -d '' file; do
|
||||||
# Skip vendor, node_modules, and index.html stub files
|
# Skip vendor, node_modules, and index.html stub files
|
||||||
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
|
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
|
||||||
# Scan the whole file for the JEXEC/JPATH guard: it is placed after
|
# Check first 10 lines for JEXEC or JPATH guard
|
||||||
# the SPDX/file-header docblock, which commonly runs past 20 lines.
|
if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then
|
||||||
if ! grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]" "$file"; then
|
|
||||||
echo "::error file=${file}::Missing JEXEC guard: ${file}"
|
echo "::error file=${file}::Missing JEXEC guard: ${file}"
|
||||||
ERRORS=$((ERRORS + 1))
|
ERRORS=$((ERRORS + 1))
|
||||||
fi
|
fi
|
||||||
@@ -351,7 +273,7 @@ jobs:
|
|||||||
joomla)
|
joomla)
|
||||||
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
if [ -z "$MANIFEST" ]; then
|
if [ -z "$MANIFEST" ]; then
|
||||||
echo "::warning::No Joomla manifest found (MokoSuite site)"
|
echo "::warning::No Joomla manifest found (WaaS site)"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
echo "Manifest: ${MANIFEST}"
|
echo "Manifest: ${MANIFEST}"
|
||||||
@@ -571,9 +493,6 @@ jobs:
|
|||||||
name: Build RC Package
|
name: Build RC Package
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [branch-policy, validate]
|
needs: [branch-policy, validate]
|
||||||
# Run only when both gates succeeded; always() forces evaluation so a skipped
|
|
||||||
# validate (e.g. template repos) skips this job cleanly instead of hanging.
|
|
||||||
if: ${{ always() && needs.branch-policy.result == 'success' && needs.validate.result == 'success' }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger RC pre-release
|
- name: Trigger RC pre-release
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Release
|
# INGROUP: mokocli.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||||
# PATH: /.mokogitea/workflows/pre-release.yml
|
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||||
# VERSION: 05.02.01
|
# VERSION: 05.02.00
|
||||||
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
|
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
|
||||||
|
|
||||||
name: "Universal: Pre-Release"
|
name: "Universal: Pre-Release"
|
||||||
@@ -48,13 +48,9 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
|
name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
|
||||||
runs-on: release
|
runs-on: release
|
||||||
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
|
|
||||||
if: >-
|
if: >-
|
||||||
!startsWith(github.event.repository.name, 'Template-') &&
|
github.event_name == 'workflow_dispatch' ||
|
||||||
(
|
github.event_name == 'push'
|
||||||
github.event_name == 'workflow_dispatch' ||
|
|
||||||
github.event_name == 'push'
|
|
||||||
)
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -69,7 +65,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git submodule foreach --quiet 'git checkout main && git pull --quiet origin main' 2>/dev/null || true
|
git submodule foreach --quiet 'git checkout main && git pull --quiet origin main' 2>/dev/null || true
|
||||||
|
|
||||||
- name: Setup MokoCLI tools
|
- name: Setup mokocli tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
@@ -162,13 +158,7 @@ jobs:
|
|||||||
git add -A
|
git add -A
|
||||||
git diff --cached --quiet || {
|
git diff --cached --quiet || {
|
||||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||||
# Push the bump commit, but do NOT fail the release if the target branch
|
git push origin HEAD 2>&1
|
||||||
# is protected and the release identity is not on the push allowlist.
|
|
||||||
# The build proceeds from the in-tree bumped version regardless; if the
|
|
||||||
# push is rejected, the next run simply re-bumps from the same base.
|
|
||||||
if ! git push origin HEAD 2>&1; then
|
|
||||||
echo "::warning::Version-bump commit could not be pushed (protected branch?). Building from in-tree version ${VERSION} anyway."
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Auto-detect element via manifest_element.php
|
# Auto-detect element via manifest_element.php
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Universal
|
# INGROUP: mokocli.Universal
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||||
# PATH: /.mokogitea/workflows/rc-revert.yml
|
# PATH: /.mokogitea/workflows/rc-revert.yml
|
||||||
# VERSION: 09.23.00
|
# VERSION: 09.23.00
|
||||||
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
|
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
|
||||||
@@ -25,8 +25,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >-
|
if: >-
|
||||||
github.event.pull_request.merged == false &&
|
github.event.pull_request.merged == false &&
|
||||||
startsWith(github.event.pull_request.head.ref, 'rc/') &&
|
startsWith(github.event.pull_request.head.ref, 'rc/')
|
||||||
!startsWith(github.event.repository.name, 'Template-')
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Rename branch
|
- name: Rename branch
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow
|
# DEFGROUP: MokoGitea.Workflow
|
||||||
# INGROUP: MokoCLI.Validation
|
# INGROUP: mokocli.Validation
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||||
# PATH: /.mokogitea/workflows/repo-health.yml
|
# PATH: /templates/workflows/joomla/repo_health.yml.template
|
||||||
# VERSION: 09.23.00
|
# VERSION: 09.23.00
|
||||||
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
|
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: MokoGitea.Workflow.Template
|
# DEFGROUP: MokoGitea.Workflow.Template
|
||||||
# INGROUP: MokoCLI.CI
|
# INGROUP: MokoStandards.CI
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
|
||||||
# PATH: /.mokogitea/workflows/version-set.yml
|
# PATH: /.mokogitea/workflows/version-set.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
@@ -34,7 +34,6 @@ jobs:
|
|||||||
set-version:
|
set-version:
|
||||||
name: Set Version to ${{ inputs.version }}
|
name: Set Version to ${{ inputs.version }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Validate version format
|
- name: Validate version format
|
||||||
|
|||||||
Reference in New Issue
Block a user