chore: Sync MokoStandards 04.01.00 #100

Merged
jmiller-moko merged 32 commits from chore/sync-mokostandards-v04.01.00 into main 2026-03-27 00:00:09 +00:00
Showing only changes of commit 2076815ac3 - Show all commits

View File

@@ -22,7 +22,7 @@
# INGROUP: MokoStandards.Deploy
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/deploy-dev.yml
# VERSION: 04.00.27
# VERSION: 04.01.00
# BRIEF: SFTP deployment workflow for development server — synced to all governed repos
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-dev.yml in all governed repos.
# Port is resolved in order: DEV_FTP_PORT variable → :port suffix in DEV_FTP_HOST → 22.
@@ -48,19 +48,19 @@ name: Deploy to Dev Server (SFTP)
on:
push:
branches:
- main
- master
- 'dev/**'
- develop
- development
paths:
- 'src/**'
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
- master
- 'dev/**'
- develop
- development
paths:
- 'src/**'
workflow_dispatch:
inputs:
clear_remote:
@@ -89,52 +89,82 @@ jobs:
REPO="${{ github.repository }}"
ORG="${{ github.repository_owner }}"
# Try the per-repo collaborator endpoint first.
# This returns 404 for org owners who are not listed as explicit
# collaborators, so we fall back to the org membership role check.
PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
--jq '.permission' 2>/dev/null)
METHOD=""
AUTHORIZED="false"
if [ -z "$PERMISSION" ]; then
# Collaborator endpoint returned nothing — try org membership.
# Requires a token with read:org scope (secrets.GH_TOKEN).
# github.token alone is insufficient for this endpoint.
ORG_ROLE=$(gh api "orgs/${ORG}/memberships/${ACTOR}" \
--jq '.role' 2>/dev/null)
if [ "$ORG_ROLE" = "owner" ]; then
# Hardcoded authorized users — always allowed to deploy
AUTHORIZED_USERS="jmiller-moko github-actions[bot]"
for user in $AUTHORIZED_USERS; do
if [ "$ACTOR" = "$user" ]; then
AUTHORIZED="true"
METHOD="hardcoded allowlist"
PERMISSION="admin"
echo " ${ACTOR} is an org owner — granting admin access"
else
# Both checks failed — token may lack read:org scope.
echo "⚠️ Could not determine permission for ${ACTOR}."
echo " Add GH_TOKEN (PAT with read:org scope) as an org secret to fix this."
PERMISSION="none"
break
fi
done
# For other actors, check repo/org permissions via API
if [ "$AUTHORIZED" != "true" ]; then
PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
--jq '.permission' 2>/dev/null)
METHOD="repo collaborator API"
if [ -z "$PERMISSION" ]; then
ORG_ROLE=$(gh api "orgs/${ORG}/memberships/${ACTOR}" \
--jq '.role' 2>/dev/null)
METHOD="org membership API"
if [ "$ORG_ROLE" = "owner" ]; then
PERMISSION="admin"
else
PERMISSION="none"
fi
fi
case "$PERMISSION" in
admin|maintain) AUTHORIZED="true" ;;
esac
fi
case "$PERMISSION" in
admin|maintain)
echo "✅ ${ACTOR} has '${PERMISSION}' permission — authorized to deploy"
;;
*)
echo "❌ Deployment requires admin or maintain role."
echo " ${ACTOR} has '${PERMISSION}' — contact your org administrator."
exit 1
;;
esac
# Write detailed summary
{
echo "## 🔐 Deploy Authorization"
echo ""
echo "| Field | Value |"
echo "|-------|-------|"
echo "| **Actor** | \`${ACTOR}\` |"
echo "| **Repository** | \`${REPO}\` |"
echo "| **Permission** | \`${PERMISSION}\` |"
echo "| **Method** | ${METHOD} |"
echo "| **Authorized** | ${AUTHORIZED} |"
echo "| **Trigger** | \`${{ github.event_name }}\` |"
echo "| **Branch** | \`${{ github.ref_name }}\` |"
echo ""
} >> "$GITHUB_STEP_SUMMARY"
if [ "$AUTHORIZED" = "true" ]; then
echo "✅ ${ACTOR} authorized to deploy (${METHOD})" >> "$GITHUB_STEP_SUMMARY"
else
echo "❌ ${ACTOR} is NOT authorized to deploy." >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Deployment requires one of:" >> "$GITHUB_STEP_SUMMARY"
echo "- Being in the hardcoded allowlist" >> "$GITHUB_STEP_SUMMARY"
echo "- Having \`admin\` or \`maintain\` role on the repository" >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
deploy:
name: SFTP Deploy → Dev
runs-on: ubuntu-latest
needs: [check-permission]
if: >-
github.event_name == 'workflow_dispatch' ||
!startsWith(github.head_ref || github.ref_name, 'chore/') &&
(github.event_name == 'workflow_dispatch' ||
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
(github.event.action == 'opened' ||
github.event.action == 'synchronize' ||
github.event.action == 'reopened' ||
github.event.pull_request.merged == true))
github.event.pull_request.merged == true)))
steps:
- name: Checkout repository
@@ -214,9 +244,11 @@ jobs:
fi
done
$SKIP && continue
if [ -f ".gitignore" ] && git check-ignore -q "$rel" 2>/dev/null; then
IGNORED_FILES+=("$rel | .gitignore")
continue
if [ -f ".gitignore" ]; then
git check-ignore -q "$rel" 2>/dev/null && {
IGNORED_FILES+=("$rel | .gitignore")
continue
} || true
fi
WILL_UPLOAD+=("$rel")
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
@@ -542,6 +574,25 @@ jobs:
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
fi
# ── For Dolibarr (crm-module): set version to "development" before deploy ─
PLATFORM=""
if [ -f ".moko-standards" ]; then
PLATFORM=$(grep -E '^platform:' .moko-standards | sed 's/.*:[[:space:]]*//' | tr -d '"')
fi
if [ "$PLATFORM" = "crm-module" ]; then
echo "📦 Dolibarr dev deploy — setting module version to 'development'"
find "$SOURCE_DIR" -path "*/core/modules/mod*.class.php" -exec \
sed -i "s/\(\$this->version\s*=\s*\)['\"][^'\"]*['\"]/\1'development'/" {} + 2>/dev/null || true
fi
if [ "$PLATFORM" = "waas-component" ]; then
echo "📦 Joomla dev deploy — setting manifest version to 'development'"
find "$SOURCE_DIR" -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | while read -r manifest; do
sed -i "s|<version>[^<]*</version>|<version>development</version>|" "$manifest" 2>/dev/null || true
done
fi
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
# Remove temp files that should never be left behind