Compare commits

..

1 Commits

Author SHA1 Message Date
Moko Consulting fa88e9df6b ci(deploy): rewrite deploy workflows as thin restricted-deploy triggers
Universal: Auto Version Bump / Version Bump (push) Successful in 12s
Generic: Project CI / Tests (pull_request) Successful in 35s
Generic: Project CI / Lint & Validate (pull_request) Successful in 43s
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Successful in 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 40s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Replace the inline build/push/recreate + curl-verify pattern in
deploy-dev/rc/prod with a thin wrapper that hands "<tier> <tag>" to the
forced-command deploy-mokogit user via mokocli cli/deploy.php. The
server-side forced command builds, pushes, recreates and health-checks;
the runner no longer builds, no longer transmits REGISTRY_TOKEN/
MOKOGIT_TOKEN to the remote, and no longer runs the curl Verify step
(the forced command cannot run curl; health is server-side).

Keeps checkout, "Determine version", and "Write deploy key"; adds the
standard MokoCLI setup step (prefer /opt/mokocli, else clone main).

Ref: .vault system/deploy, runbook 16-restricted-deploy-pattern.

Authored-by: Moko Consulting
Claude-Session: https://claude.ai/code/session_015UauDPfYC8S2mdUhoSK8zQ
2026-07-20 08:44:32 -05:00
12 changed files with 167 additions and 253 deletions
View File
-1
View File
@@ -144,4 +144,3 @@ Makefile.local
# ============================================================
wiki/
docs/
.claude/
+1 -55
View File
@@ -7,7 +7,7 @@
# INGROUP: MokoCLI.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.mokogit/workflows/auto-release.yml
# VERSION: 05.02.00
# VERSION: 05.01.02
# BRIEF: Universal build & release detects platform from metadata API
#
# +=======================================================================+
@@ -39,12 +39,6 @@ on:
- '.gitattributes'
- '.gitmessage'
- 'LICENSE'
# Daily safety-net: catch merges whose pull_request event never created a run
# (e.g. this workflow file being re-synced from the template concurrently with the
# merge). Off-round minute to avoid a fleet-wide spike. The safety-net job below
# only dispatches a release when main actually has unreleased changes.
schedule:
- cron: '37 8 * * *'
workflow_dispatch:
inputs:
action:
@@ -511,51 +505,3 @@ jobs:
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](${MOKOGIT_URL}/${GIT_ORG}/${GIT_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi
# ── Scheduled safety-net ─────────────────────────────────────────────────────────
# A merge to main normally fires the `release` job via the pull_request `closed`
# event. If that event never creates a run (observed when this workflow file is
# being re-synced from the template in the same window as the merge), the release
# is silently skipped. This daily job self-heals that: if main's CHANGELOG
# [Unreleased] section still has content — which the release job empties on a
# successful promote — it dispatches a normal release. Dependency-free (awk only)
# so it runs on any runner; never touches the release job's own conditions.
scheduled-safety-net:
name: Scheduled release safety-net
runs-on: ubuntu-latest
if: github.event_name == 'schedule' && !startsWith(github.event.repository.name, 'Template-')
permissions:
contents: read
steps:
- name: Checkout main
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: main
fetch-depth: 1
- name: Dispatch release if main has unreleased changes
env:
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: |
# [Unreleased] is non-empty precisely when changes were merged but not yet
# released (the release job promotes it to a version and empties it).
NOTES=""
if [ -f CHANGELOG.md ]; then
NOTES=$(awk '/^## \[Unreleased\]/{f=1;next} /^## \[/{if(f)exit} f{print}' CHANGELOG.md)
fi
if [ -z "$(printf '%s' "$NOTES" | tr -d '[:space:]')" ]; then
echo "No unreleased changes on main — safety-net has nothing to do."
exit 0
fi
echo "Unreleased changes detected on main — a merge release was likely missed."
echo "Dispatching a release via the safety-net."
HTTP=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"ref":"main","inputs":{"action":"release"}}' \
"${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}/actions/workflows/auto-release.yml/dispatches")
if [ "$HTTP" = "204" ] || [ "$HTTP" = "201" ] || [ "$HTTP" = "200" ]; then
echo "Release dispatched (HTTP $HTTP)."
else
echo "::warning::Safety-net release dispatch failed (HTTP $HTTP)"
fi
+44 -26
View File
@@ -1,16 +1,22 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Deploy to the Dev environment on push to the dev branch. THIN TRIGGER —
# the deploy LOGIC lives in mokocli (cli/deploy.php) and server-side in the
# restricted per-repo deploy pattern (.vault system/deploy, runbook 16).
# This workflow validates + invokes only; it carries NO registry token and
# NO build script. A leaked DEPLOY_SSH_KEY can only redeploy this one repo.
# OWNER: Template-Go (canonical; syncs to each repo's .mokogit/workflows).
# BRIEF: Deploy to the Dev environment on push to the dev branch via the
# restricted per-repo deploy pattern. This is a THIN trigger: it only
# validates the tier+tag and hands "<tier> <tag>" to the forced-command
# deploy-mokogit user over SSH (mokocli cli/deploy.php). The server-side
# forced command sudo's to a root-owned deployer that builds, pushes,
# recreates and health-checks — the runner never builds, holds a registry
# token, or runs the health check. See `.vault system/deploy` and runbook
# 16-restricted-deploy-pattern.
# OWNER: Template-Go (canonical source; syncs to the root workflows dir). See Template-Go#3.
# NOTE: deploy-*.yml are repo-managed (per-repo deploy user + secrets/vars)
# and excluded from template sync — see mokocli workflow_sync PLATFORM_EXCLUDES['go'].
#
# Required repo VARIABLES (tier-scoped): DEV_SSH_HOST, DEV_SSH_PORT, DEV_SSH_USERNAME.
# Required SECRET: DEPLOY_SSH_KEY (the deploy-<repo> private key).
# ONBOARDING: a repo joins the restricted deploy pattern when DEV_SSH_USERNAME is
# set to deploy-<repo>. Un-onboarded go repos skip the job (guards below).
# Required repo VARIABLES:
# DEV_SSH_HOST, DEV_SSH_PORT, DEV_SSH_USERNAME - SSH deploy target for the dev tier
# (DEV_SSH_USERNAME = deploy-mokogit, the forced-command deploy user)
# Required SECRETS (already configured; reused, not re-set):
# DEPLOY_SSH_KEY - deploy-mokogit private key (repo secret)
name: Deploy (Dev)
@@ -18,9 +24,13 @@ on:
push:
branches:
- dev
# Manual trigger for isolated end-to-end tests.
# Runs on the ref it is dispatched from.
workflow_dispatch:
# No `concurrency:` block: triggers a MokoGIT run-creation bug that drops deploys.
# No `concurrency:` block: it triggers a MokoGIT Actions run-creation bug that
# silently drops deploys on rapid pushes to the branch. Do not re-add until the
# upstream bug is confirmed fixed.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -29,7 +39,6 @@ jobs:
deploy-dev:
name: "Deploy to Dev"
runs-on: ubuntu-latest
if: ${{ vars.DEV_SSH_USERNAME != '' }}
steps:
- name: Checkout source
uses: actions/checkout@v4
@@ -51,20 +60,29 @@ jobs:
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
- name: Set up mokocli (deploy logic)
- name: Setup MokoCLI tools
env:
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
command -v php >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq php-cli; }
git clone --depth 1 "https://x-access-token:${MOKOGIT_TOKEN}@git.mokoconsulting.tech/MokoConsulting/MokoCLI.git" "$RUNNER_TEMP/mokocli"
echo "MOKO_CLI=$RUNNER_TEMP/mokocli/cli" >> $GITHUB_ENV
- name: Deploy (mokocli triggers the restricted deploy pattern)
run: |
if [ -z "${{ vars.DEV_SSH_USERNAME }}" ]; then
echo "DEV_SSH_USERNAME unset — repo not onboarded to the restricted deploy pattern; skipping."
exit 0
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
if [ -f /opt/mokocli/cli/deploy.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
php "$MOKO_CLI/deploy.php" --tier dev --tag "${{ steps.config.outputs.tag }}" \
--ssh-host "${{ vars.DEV_SSH_HOST }}" --ssh-port "${{ vars.DEV_SSH_PORT }}" \
--ssh-user "${{ vars.DEV_SSH_USERNAME }}" --ssh-key ~/.ssh/deploy_key
- name: Deploy (dev)
run: |
php ${MOKO_CLI}/deploy.php --tier dev --tag "${{ steps.config.outputs.tag }}" \
--ssh-host ${{ vars.DEV_SSH_HOST }} --ssh-port ${{ vars.DEV_SSH_PORT }} \
--ssh-user ${{ vars.DEV_SSH_USERNAME }} --ssh-key ~/.ssh/deploy_key
+45 -26
View File
@@ -1,16 +1,23 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Deploy to the Prod environment on push to the main branch. THIN TRIGGER —
# the deploy LOGIC lives in mokocli (cli/deploy.php) and server-side in the
# restricted per-repo deploy pattern (.vault system/deploy, runbook 16).
# This workflow validates + invokes only; it carries NO registry token and
# NO build script. A leaked DEPLOY_SSH_KEY can only redeploy this one repo.
# OWNER: Template-Go (canonical; syncs to each repo's .mokogit/workflows).
# BRIEF: Deploy to the Prod environment on push to the main branch via the
# restricted per-repo deploy pattern. This is a THIN trigger: it only
# validates the tier+tag and hands "<tier> <tag>" to the forced-command
# deploy-mokogit user over SSH (mokocli cli/deploy.php). The server-side
# forced command sudo's to a root-owned deployer that builds, pushes,
# recreates and health-checks — the runner never builds, holds a registry
# token, or runs the health check. See `.vault system/deploy` and runbook
# 16-restricted-deploy-pattern.
# 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.
# NOTE: deploy-*.yml are repo-managed (per-repo deploy user + secrets/vars)
# and excluded from template sync — see mokocli workflow_sync PLATFORM_EXCLUDES['go'].
#
# Required repo VARIABLES (tier-scoped): PROD_SSH_HOST, PROD_SSH_PORT, PROD_SSH_USERNAME.
# Required SECRET: DEPLOY_SSH_KEY (the deploy-<repo> private key).
# ONBOARDING: a repo joins the restricted deploy pattern when PROD_SSH_USERNAME is
# set to deploy-<repo>. Un-onboarded go repos skip the job (guards below).
# Required repo VARIABLES:
# PROD_SSH_HOST, PROD_SSH_PORT, PROD_SSH_USERNAME - SSH deploy target for the prod tier
# (PROD_SSH_USERNAME = deploy-mokogit, the forced-command deploy user)
# Required SECRETS (already configured; reused, not re-set):
# DEPLOY_SSH_KEY - deploy-mokogit private key (repo secret)
name: Deploy (Prod)
@@ -18,9 +25,13 @@ on:
push:
branches:
- main
# Manual trigger for a prod re-deploy.
# Runs on the ref it is dispatched from (use main).
workflow_dispatch:
# No `concurrency:` block: triggers a MokoGIT run-creation bug that drops deploys.
# No `concurrency:` block: it triggers a MokoGIT Actions run-creation bug that
# silently drops deploys on rapid pushes to the branch. Do not re-add until the
# upstream bug is confirmed fixed.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -29,7 +40,6 @@ jobs:
deploy-prod:
name: "Deploy to Prod"
runs-on: ubuntu-latest
if: ${{ vars.PROD_SSH_USERNAME != '' }}
steps:
- name: Checkout source
uses: actions/checkout@v4
@@ -51,20 +61,29 @@ jobs:
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
- name: Set up mokocli (deploy logic)
- name: Setup MokoCLI tools
env:
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
command -v php >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq php-cli; }
git clone --depth 1 "https://x-access-token:${MOKOGIT_TOKEN}@git.mokoconsulting.tech/MokoConsulting/MokoCLI.git" "$RUNNER_TEMP/mokocli"
echo "MOKO_CLI=$RUNNER_TEMP/mokocli/cli" >> $GITHUB_ENV
- name: Deploy (mokocli triggers the restricted deploy pattern)
run: |
if [ -z "${{ vars.PROD_SSH_USERNAME }}" ]; then
echo "PROD_SSH_USERNAME unset — repo not onboarded to the restricted deploy pattern; skipping."
exit 0
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
if [ -f /opt/mokocli/cli/deploy.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
php "$MOKO_CLI/deploy.php" --tier prod --tag "${{ steps.config.outputs.tag }}" \
--ssh-host "${{ vars.PROD_SSH_HOST }}" --ssh-port "${{ vars.PROD_SSH_PORT }}" \
--ssh-user "${{ vars.PROD_SSH_USERNAME }}" --ssh-key ~/.ssh/deploy_key
- name: Deploy (prod)
run: |
php ${MOKO_CLI}/deploy.php --tier prod --tag "${{ steps.config.outputs.tag }}" \
--ssh-host ${{ vars.PROD_SSH_HOST }} --ssh-port ${{ vars.PROD_SSH_PORT }} \
--ssh-user ${{ vars.PROD_SSH_USERNAME }} --ssh-key ~/.ssh/deploy_key
+47 -29
View File
@@ -1,17 +1,23 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Deploy to the RC environment on push to the rc branch. THIN TRIGGER —
# the deploy LOGIC lives in mokocli (cli/deploy.php) and server-side in the
# restricted per-repo deploy pattern (.vault system/deploy, runbook 16).
# This workflow validates + invokes only; it carries NO registry token and
# NO build script. A leaked DEPLOY_SSH_KEY can only redeploy this one repo.
# OWNER: Template-Go (canonical; syncs to each repo's .mokogit/workflows).
# BRIEF: Deploy to the RC environment on push to the rc branch via the
# restricted per-repo deploy pattern. This is a THIN trigger: it only
# validates the tier+tag and hands "<tier> <tag>" to the forced-command
# deploy-mokogit user over SSH (mokocli cli/deploy.php). The server-side
# forced command sudo's to a root-owned deployer that builds, pushes,
# recreates and health-checks — the runner never builds, holds a registry
# token, or runs the health check. See `.vault system/deploy` and runbook
# 16-restricted-deploy-pattern.
# The rc branch is created by promote-rc when a PR to main opens.
# OWNER: Template-Go (canonical source; syncs to the root workflows dir). See Template-Go#3.
# NOTE: deploy-*.yml are repo-managed (per-repo deploy user + secrets/vars)
# and excluded from template sync — see mokocli workflow_sync PLATFORM_EXCLUDES['go'].
#
# Required repo VARIABLES (tier-scoped): RC_SSH_HOST, RC_SSH_PORT, RC_SSH_USERNAME.
# Required SECRET: DEPLOY_SSH_KEY (the deploy-<repo> private key).
# ONBOARDING: a repo joins the restricted deploy pattern when RC_SSH_USERNAME is
# set to deploy-<repo>. Un-onboarded go repos skip the job (guards below), so this
# template is safe to cascade to every go repo.
# Required repo VARIABLES:
# RC_SSH_HOST, RC_SSH_PORT, RC_SSH_USERNAME - SSH deploy target for the rc tier
# (RC_SSH_USERNAME = deploy-mokogit, the forced-command deploy user)
# Required SECRETS (already configured; reused, not re-set):
# DEPLOY_SSH_KEY - deploy-mokogit private key (repo secret)
name: Deploy (RC)
@@ -19,9 +25,15 @@ on:
push:
branches:
- rc
# Manual trigger for isolated end-to-end tests without a full RC promotion.
# Runs on the ref it is dispatched from; that ref must carry current source
# (>= the RC database migration version) or the rebuilt image will refuse the
# newer DB. Dispatch from `rc` once `rc` is current.
workflow_dispatch:
# No `concurrency:` block: triggers a MokoGIT run-creation bug that drops deploys.
# No `concurrency:` block: it triggers a MokoGIT Actions run-creation bug that
# silently drops deploys on rapid pushes to the branch. Do not re-add until the
# upstream bug is confirmed fixed.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -30,8 +42,6 @@ jobs:
deploy-rc:
name: "Deploy to RC"
runs-on: ubuntu-latest
# Guard 1 (job-level): skip entirely unless onboarded.
if: ${{ vars.RC_SSH_USERNAME != '' }}
steps:
- name: Checkout source
uses: actions/checkout@v4
@@ -53,21 +63,29 @@ jobs:
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
- name: Set up mokocli (deploy logic)
- name: Setup MokoCLI tools
env:
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: |
command -v php >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq php-cli; }
git clone --depth 1 "https://x-access-token:${MOKOGIT_TOKEN}@git.mokoconsulting.tech/MokoConsulting/MokoCLI.git" "$RUNNER_TEMP/mokocli"
echo "MOKO_CLI=$RUNNER_TEMP/mokocli/cli" >> $GITHUB_ENV
- name: Deploy (mokocli triggers the restricted deploy pattern)
run: |
# Guard 2 (step-level, reliable): no-op if not onboarded.
if [ -z "${{ vars.RC_SSH_USERNAME }}" ]; then
echo "RC_SSH_USERNAME unset — repo not onboarded to the restricted deploy pattern; skipping."
exit 0
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
if [ -f /opt/mokocli/cli/deploy.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
else
echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
fi
rm -rf /tmp/mokocli
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
fi
php "$MOKO_CLI/deploy.php" --tier rc --tag "${{ steps.config.outputs.tag }}" \
--ssh-host "${{ vars.RC_SSH_HOST }}" --ssh-port "${{ vars.RC_SSH_PORT }}" \
--ssh-user "${{ vars.RC_SSH_USERNAME }}" --ssh-key ~/.ssh/deploy_key
- name: Deploy (rc)
run: |
php ${MOKO_CLI}/deploy.php --tier rc --tag "${{ steps.config.outputs.tag }}" \
--ssh-host ${{ vars.RC_SSH_HOST }} --ssh-port ${{ vars.RC_SSH_PORT }} \
--ssh-user ${{ vars.RC_SSH_USERNAME }} --ssh-key ~/.ssh/deploy_key
-1
View File
@@ -3,7 +3,6 @@
## [Unreleased]
### Fixed
- **Update server (Joomla): serve the installable package, not the source archive** — `GenerateJoomlaXML` selected the first `.zip` attachment on a release, so releases carrying extra zips (e.g. `<repo>-<channel>-source.zip` ahead of `pkg_*.zip`) advertised the raw source archive; Joomla then downloaded a non-installable zip and failed with "Install path does not exist" (prerelease/dev channels). New `selectJoomlaArtifact()` prefers an extension-prefixed package (`pkg_`/`com_`/`mod_`/`plg_`/`tpl_`/`lib_`) and never a `-source` archive (hotfixed to prod)
- **Actions: `.mokogit/workflows` custom-path detection restored** — `WorkflowDirs` now scans `.mokogit/workflows`; the legacy `.mokogitea/workflows` is fully retired. Workflow indexing, push-triggered CI/deploys, and `workflow_dispatch` work again (#798)
- **Issue custom-status dropdown shows its options again** — the status `<select>` carried the `ui compact dropdown` class, so fomantic turned it into an overlay menu that painted behind the page content (options were present in the DOM but invisible); it is now a plain native `<select>` with its inline styles moved to a stylesheet (`web_src/css/repo/issue-status.css`)
- Restored the repo's `.mokogit/workflows/` (deploy + org CI, ~20 workflows) from Template-Go after the workflow sync had dropped them
+10 -14
View File
@@ -13,22 +13,18 @@ services:
environment:
- USER_UID=${GIT_UID}
- USER_GID=${GIT_GID}
- MOKOGIT__server__DOMAIN=${GIT_DOMAIN}
- MOKOGIT__server__ROOT_URL=${GIT_ROOT_URL}
- MOKOGIT__server__SSH_DOMAIN=${GIT_SSH_DOMAIN}
- MOKOGIT__server__SSH_PORT=${GIT_SSH_PUBLIC_PORT}
- MOKOGIT__database__DB_TYPE=mysql
- MOKOGIT__database__HOST=${GIT_DB_HOST}
- MOKOGIT__database__NAME=${GIT_DB_NAME}
- MOKOGIT__database__USER=${GIT_DB_USER}
- MOKOGIT__database__PASSWD=${GIT_DB_PASSWD}
- GITEA__server__DOMAIN=${GIT_DOMAIN}
- GITEA__server__ROOT_URL=${GIT_ROOT_URL}
- GITEA__server__SSH_DOMAIN=${GIT_SSH_DOMAIN}
- GITEA__server__SSH_PORT=${GIT_SSH_PUBLIC_PORT}
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=${GIT_DB_HOST}
- GITEA__database__NAME=${GIT_DB_NAME}
- GITEA__database__USER=${GIT_DB_USER}
- GITEA__database__PASSWD=${GIT_DB_PASSWD}
volumes:
# RECONCILE: live app.ini at ${GIT_DATA_DIR}/conf/app.ini (/opt/mokogit/dev/conf).
# Clean disconnect layout (#839): MOKOGIT_CUSTOM=/data/mokogit is baked into the image.
# Parent (/data/mokogit) listed before child (/data/mokogit/conf) so Docker attaches it first.
- "${GIT_DATA_DIR}:/var/lib/gitea"
- "${GIT_DATA_DIR}:/data/mokogit"
- "${GIT_DATA_DIR}/conf:/data/mokogit/conf"
- "${GIT_DATA_DIR}:/data"
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
+10 -14
View File
@@ -17,23 +17,19 @@ services:
environment:
- USER_UID=${GIT_UID}
- USER_GID=${GIT_GID}
- MOKOGIT__server__DOMAIN=${GIT_DOMAIN}
- MOKOGIT__server__ROOT_URL=${GIT_ROOT_URL}
- MOKOGIT__server__SSH_DOMAIN=${GIT_SSH_DOMAIN}
- MOKOGIT__server__SSH_PORT=${GIT_SSH_PUBLIC_PORT}
- GITEA__server__DOMAIN=${GIT_DOMAIN}
- GITEA__server__ROOT_URL=${GIT_ROOT_URL}
- GITEA__server__SSH_DOMAIN=${GIT_SSH_DOMAIN}
- GITEA__server__SSH_PORT=${GIT_SSH_PUBLIC_PORT}
# DB is HOST MySQL (RECONCILE host reachability: host-gateway / socket / LAN IP).
- MOKOGIT__database__DB_TYPE=mysql
- MOKOGIT__database__HOST=${GIT_DB_HOST}
- MOKOGIT__database__NAME=${GIT_DB_NAME}
- MOKOGIT__database__USER=${GIT_DB_USER}
- MOKOGIT__database__PASSWD=${GIT_DB_PASSWD}
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=${GIT_DB_HOST}
- GITEA__database__NAME=${GIT_DB_NAME}
- GITEA__database__USER=${GIT_DB_USER}
- GITEA__database__PASSWD=${GIT_DB_PASSWD}
volumes:
# RECONCILE: live app.ini is at ${GIT_DATA_DIR}/conf/app.ini (/opt/mokogit/prod/conf).
# Clean disconnect layout (#839): MOKOGIT_CUSTOM=/data/mokogit is baked into the image.
# Parent (/data/mokogit) listed before child (/data/mokogit/conf) so Docker attaches it first.
- "${GIT_DATA_DIR}:/var/lib/gitea"
- "${GIT_DATA_DIR}:/data/mokogit"
- "${GIT_DATA_DIR}/conf:/data/mokogit/conf"
- "${GIT_DATA_DIR}:/data"
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
+10 -14
View File
@@ -13,22 +13,18 @@ services:
environment:
- USER_UID=${GIT_UID}
- USER_GID=${GIT_GID}
- MOKOGIT__server__DOMAIN=${GIT_DOMAIN}
- MOKOGIT__server__ROOT_URL=${GIT_ROOT_URL}
- MOKOGIT__server__SSH_DOMAIN=${GIT_SSH_DOMAIN}
- MOKOGIT__server__SSH_PORT=${GIT_SSH_PUBLIC_PORT}
- MOKOGIT__database__DB_TYPE=mysql
- MOKOGIT__database__HOST=${GIT_DB_HOST}
- MOKOGIT__database__NAME=${GIT_DB_NAME}
- MOKOGIT__database__USER=${GIT_DB_USER}
- MOKOGIT__database__PASSWD=${GIT_DB_PASSWD}
- GITEA__server__DOMAIN=${GIT_DOMAIN}
- GITEA__server__ROOT_URL=${GIT_ROOT_URL}
- GITEA__server__SSH_DOMAIN=${GIT_SSH_DOMAIN}
- GITEA__server__SSH_PORT=${GIT_SSH_PUBLIC_PORT}
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=${GIT_DB_HOST}
- GITEA__database__NAME=${GIT_DB_NAME}
- GITEA__database__USER=${GIT_DB_USER}
- GITEA__database__PASSWD=${GIT_DB_PASSWD}
volumes:
# RECONCILE: live app.ini at ${GIT_DATA_DIR}/conf/app.ini (/opt/mokogit/rc/conf).
# Clean disconnect layout (#839): MOKOGIT_CUSTOM=/data/mokogit is baked into the image.
# Parent (/data/mokogit) listed before child (/data/mokogit/conf) so Docker attaches it first.
- "${GIT_DATA_DIR}:/var/lib/gitea"
- "${GIT_DATA_DIR}:/data/mokogit"
- "${GIT_DATA_DIR}/conf:/data/mokogit/conf"
- "${GIT_DATA_DIR}:/data"
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
-10
View File
@@ -200,16 +200,6 @@ func GenerateReleaseArtifacts(ctx context.Context, rel *repo_model.Release) erro
} else if meta != nil {
entryPoint = meta.EntryPoint
}
// Joomla repos are packaged by the CI pipeline (release_package.php),
// which uploads the correct installable zip/tar.gz. Server-generated
// source archives are redundant and confuse users about which file to
// download, so skip them entirely.
if meta != nil && strings.EqualFold(meta.Platform, "joomla") {
log.Info("GenerateReleaseArtifacts: skipping source archives for Joomla repo %d (CI pipeline packages)", rel.RepoID)
return nil
}
wantSource := !isRootEntryPoint(entryPoint)
// Serialize the delete+attach sequence per-release so concurrent
@@ -1,63 +0,0 @@
// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package integration
import (
"net/http"
"net/url"
"testing"
auth_model "code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/auth"
"github.com/stretchr/testify/assert"
)
// apiMetadataResponse mirrors the subset of the repo manifest/metadata API
// payload exercised by this test. The handler struct (routers/api/v1/repo)
// is unexported, so the fields are re-declared here by their JSON tags.
type apiMetadataResponse struct {
Name string `json:"name"`
NodeMinimum string `json:"node_minimum"`
NpmPackage string `json:"npm_package"`
PublishTarget string `json:"publish_target"`
}
// TestAPIRepoMetadataNpmFieldsRoundTrip guards against regression of issue #847
// (from #827): the npm/mcp fields node_minimum, npm_package and publish_target
// must be persisted via PUT and returned via GET on the metadata endpoint,
// rather than being silently dropped by the apiMetadata struct.
func TestAPIRepoMetadataNpmFieldsRoundTrip(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
// user1 is the site admin in the fixtures; the PUT route requires admin.
session := loginUser(t, "user1")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
const owner, repo = "user2", "repo1"
// PUT the three npm/mcp fields.
req := NewRequestWithJSON(t, "PUT", "/api/v1/repos/"+owner+"/"+repo+"/metadata", map[string]string{
"node_minimum": "18",
"npm_package": "@moko/foo",
"publish_target": "npm",
}).AddTokenAuth(token)
resp := session.MakeRequest(t, req, http.StatusOK)
// The PUT response should echo the persisted fields.
var put apiMetadataResponse
DecodeJSON(t, resp, &put)
assert.Equal(t, "18", put.NodeMinimum)
assert.Equal(t, "@moko/foo", put.NpmPackage)
assert.Equal(t, "npm", put.PublishTarget)
// A subsequent GET should read the same values back from the DB.
req = NewRequest(t, "GET", "/api/v1/repos/"+owner+"/"+repo+"/metadata").AddTokenAuth(token)
resp = session.MakeRequest(t, req, http.StatusOK)
var got apiMetadataResponse
DecodeJSON(t, resp, &got)
assert.Equal(t, "18", got.NodeMinimum)
assert.Equal(t, "@moko/foo", got.NpmPackage)
assert.Equal(t, "npm", got.PublishTarget)
})
}