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 # INGROUP: MokoStandards.Deploy
# REPO: https://github.com/mokoconsulting-tech/MokoStandards # REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/deploy-dev.yml # 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 # 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. # 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. # 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: on:
push: push:
branches: branches:
- main
- master
- 'dev/**' - 'dev/**'
- develop - develop
- development - development
paths:
- 'src/**'
pull_request: pull_request:
types: [opened, synchronize, reopened, closed] types: [opened, synchronize, reopened, closed]
branches: branches:
- main
- master
- 'dev/**' - 'dev/**'
- develop - develop
- development - development
paths:
- 'src/**'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
clear_remote: clear_remote:
@@ -89,52 +89,82 @@ jobs:
REPO="${{ github.repository }}" REPO="${{ github.repository }}"
ORG="${{ github.repository_owner }}" ORG="${{ github.repository_owner }}"
# Try the per-repo collaborator endpoint first. METHOD=""
# This returns 404 for org owners who are not listed as explicit AUTHORIZED="false"
# collaborators, so we fall back to the org membership role check.
PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
--jq '.permission' 2>/dev/null)
if [ -z "$PERMISSION" ]; then # Hardcoded authorized users — always allowed to deploy
# Collaborator endpoint returned nothing — try org membership. AUTHORIZED_USERS="jmiller-moko github-actions[bot]"
# Requires a token with read:org scope (secrets.GH_TOKEN). for user in $AUTHORIZED_USERS; do
# github.token alone is insufficient for this endpoint. if [ "$ACTOR" = "$user" ]; then
ORG_ROLE=$(gh api "orgs/${ORG}/memberships/${ACTOR}" \ AUTHORIZED="true"
--jq '.role' 2>/dev/null) METHOD="hardcoded allowlist"
if [ "$ORG_ROLE" = "owner" ]; then
PERMISSION="admin" PERMISSION="admin"
echo " ${ACTOR} is an org owner — granting admin access" break
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"
fi 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 fi
case "$PERMISSION" in # Write detailed summary
admin|maintain) {
echo "✅ ${ACTOR} has '${PERMISSION}' permission — authorized to deploy" echo "## 🔐 Deploy Authorization"
;; echo ""
*) echo "| Field | Value |"
echo "❌ Deployment requires admin or maintain role." echo "|-------|-------|"
echo " ${ACTOR} has '${PERMISSION}' — contact your org administrator." echo "| **Actor** | \`${ACTOR}\` |"
exit 1 echo "| **Repository** | \`${REPO}\` |"
;; echo "| **Permission** | \`${PERMISSION}\` |"
esac 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: deploy:
name: SFTP Deploy → Dev name: SFTP Deploy → Dev
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [check-permission] needs: [check-permission]
if: >- 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 == 'push' ||
(github.event_name == 'pull_request' && (github.event_name == 'pull_request' &&
(github.event.action == 'opened' || (github.event.action == 'opened' ||
github.event.action == 'synchronize' || github.event.action == 'synchronize' ||
github.event.action == 'reopened' || github.event.action == 'reopened' ||
github.event.pull_request.merged == true)) github.event.pull_request.merged == true)))
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -214,9 +244,11 @@ jobs:
fi fi
done done
$SKIP && continue $SKIP && continue
if [ -f ".gitignore" ] && git check-ignore -q "$rel" 2>/dev/null; then if [ -f ".gitignore" ]; then
IGNORED_FILES+=("$rel | .gitignore") git check-ignore -q "$rel" 2>/dev/null && {
continue IGNORED_FILES+=("$rel | .gitignore")
continue
} || true
fi fi
WILL_UPLOAD+=("$rel") WILL_UPLOAD+=("$rel")
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z) done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
@@ -542,6 +574,25 @@ jobs:
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD") DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
fi 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[@]}" php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively) # (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
# Remove temp files that should never be left behind # Remove temp files that should never be left behind