Template
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65f57f466e | |||
| 95b542bff2 | |||
| 749e5ec149 | |||
| ac2d35439c | |||
| 5c28793132 | |||
| 3428e59893 | |||
| 6b55296699 | |||
| 23493fb182 | |||
| 447dd488ac | |||
| b0beef55ed |
+1
-1
@@ -41,7 +41,7 @@
|
||||
# CI and automation
|
||||
.github/ export-ignore
|
||||
.github/workflows/ export-ignore
|
||||
.mokogit/ export-ignore
|
||||
.gitea/ export-ignore
|
||||
.gitlab/ export-ignore
|
||||
|
||||
# Development and tooling
|
||||
|
||||
@@ -205,14 +205,6 @@ hypothesis/
|
||||
profile.ps1
|
||||
.mcp.json
|
||||
|
||||
# ============================================================
|
||||
# AI client working directories and instruction files
|
||||
# ============================================================
|
||||
.gemini/
|
||||
.mokoai/
|
||||
/GEMINI.md
|
||||
/MOKOAI.md
|
||||
|
||||
# ============================================================
|
||||
# Local wiki clone (not version controlled)
|
||||
# ============================================================
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# {{REPO_NAME}}
|
||||
|
||||
{{REPO_DESCRIPTION}}
|
||||
|
||||
> AI-client instructions for **Gemini**. Mirrors `.mokogit/CLAUDE.md` — keep CLAUDE.md, GEMINI.md, and MOKOAI.md in sync.
|
||||
|
||||
## First-Run Setup
|
||||
|
||||
> Before starting any work, complete these steps:
|
||||
>
|
||||
> 1. Replace all `{{REPO_NAME}}` placeholders with the actual repository name
|
||||
> 2. Replace `{{REPO_DESCRIPTION}}` with a one-line project description
|
||||
> 3. Replace `{{PLATFORM_TYPE}}` with the project type (e.g. `generic`, `npm`, `joomla`)
|
||||
> 4. Replace `{{PRIMARY_LANGUAGE}}` with the dominant language
|
||||
> 5. Set repository metadata via the MokoGIT **Metadata** field — repository **Settings → Metadata**, or the REST API `PUT /api/v1/repos/{owner}/{repo}/manifest` (identity, platform, language, package-type, entry-point). There is no metadata file in the repo.
|
||||
> 6. Update the **Architecture** section for your project
|
||||
> 7. Update the Commands section with actual build commands
|
||||
> 8. Delete this setup block after completing all steps
|
||||
> 9. Commit: `chore: initialize {{REPO_NAME}}`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Platform** | {{PLATFORM_TYPE}} |
|
||||
| **Language** | {{PRIMARY_LANGUAGE}} |
|
||||
| **Branch** | develop on `dev`, merge to `main` (protected) |
|
||||
| **Wiki** | [Wiki](https://git.mokoconsulting.tech/MokoConsulting/{{REPO_NAME}}/wiki) |
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
make build # Build the project
|
||||
make lint # Run linters
|
||||
make validate # Validate structure
|
||||
make release # Full release pipeline
|
||||
make clean # Clean build artifacts
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
<!-- Update this section for your project -->
|
||||
|
||||
## Rules
|
||||
|
||||
- **Never commit** `.gemini/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
|
||||
- **Attribution**: `Authored-by: Moko Consulting`
|
||||
- **Workflow directory**: `.mokogit/` (not `.gitea/` or `.github/`)
|
||||
- **Metadata**: set via the MokoGIT **Metadata** field (API / Settings → Metadata), never a committed manifest file
|
||||
- **Wiki**: documentation lives in the MokoGIT wiki, not `docs/` files
|
||||
- **Standards**: [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home)
|
||||
@@ -1,51 +0,0 @@
|
||||
# {{REPO_NAME}}
|
||||
|
||||
{{REPO_DESCRIPTION}}
|
||||
|
||||
> AI-client instructions for **MokoAI**. Mirrors `.mokogit/CLAUDE.md` — keep CLAUDE.md, GEMINI.md, and MOKOAI.md in sync.
|
||||
|
||||
## First-Run Setup
|
||||
|
||||
> Before starting any work, complete these steps:
|
||||
>
|
||||
> 1. Replace all `{{REPO_NAME}}` placeholders with the actual repository name
|
||||
> 2. Replace `{{REPO_DESCRIPTION}}` with a one-line project description
|
||||
> 3. Replace `{{PLATFORM_TYPE}}` with the project type (e.g. `generic`, `npm`, `joomla`)
|
||||
> 4. Replace `{{PRIMARY_LANGUAGE}}` with the dominant language
|
||||
> 5. Set repository metadata via the MokoGIT **Metadata** field — repository **Settings → Metadata**, or the REST API `PUT /api/v1/repos/{owner}/{repo}/manifest` (identity, platform, language, package-type, entry-point). There is no metadata file in the repo.
|
||||
> 6. Update the **Architecture** section for your project
|
||||
> 7. Update the Commands section with actual build commands
|
||||
> 8. Delete this setup block after completing all steps
|
||||
> 9. Commit: `chore: initialize {{REPO_NAME}}`
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Platform** | {{PLATFORM_TYPE}} |
|
||||
| **Language** | {{PRIMARY_LANGUAGE}} |
|
||||
| **Branch** | develop on `dev`, merge to `main` (protected) |
|
||||
| **Wiki** | [Wiki](https://git.mokoconsulting.tech/MokoConsulting/{{REPO_NAME}}/wiki) |
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
make build # Build the project
|
||||
make lint # Run linters
|
||||
make validate # Validate structure
|
||||
make release # Full release pipeline
|
||||
make clean # Clean build artifacts
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
<!-- Update this section for your project -->
|
||||
|
||||
## Rules
|
||||
|
||||
- **Never commit** `.mokoai/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
|
||||
- **Attribution**: `Authored-by: Moko Consulting`
|
||||
- **Workflow directory**: `.mokogit/` (not `.gitea/` or `.github/`)
|
||||
- **Metadata**: set via the MokoGIT **Metadata** field (API / Settings → Metadata), never a committed manifest file
|
||||
- **Wiki**: documentation lives in the MokoGIT wiki, not `docs/` files
|
||||
- **Standards**: [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home)
|
||||
@@ -1,60 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
||||
# PATH: /.mokogit/workflows/branch-cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Delete feature branches after PR merge
|
||||
|
||||
name: "Branch Cleanup"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Delete merged branch
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.head.ref != 'dev' &&
|
||||
github.event.pull_request.head.ref != 'main'
|
||||
|
||||
steps:
|
||||
- name: Delete source branch
|
||||
# SECURITY: the PR head ref is attacker-controllable. Pass it (and the
|
||||
# repo/token) through env so the Actions engine cannot splice it into the
|
||||
# shell source, and validate it to a safe charset before use.
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
API_BASE: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if ! printf '%s' "${BRANCH}" | grep -Eq '^[A-Za-z0-9._/-]+$'; then
|
||||
echo "::error::unsafe branch name; refusing to proceed"; exit 1
|
||||
fi
|
||||
API="${API_BASE}/api/v1/repos/${REPO}/branches"
|
||||
# URL-encode the branch name's slashes (no PHP dependency on the runner)
|
||||
ENCODED=$(printf '%s' "${BRANCH}" | sed 's|/|%2F|g')
|
||||
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${MOKOGIT_TOKEN}" \
|
||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "204" ]; then
|
||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "$STATUS" = "404" ]; then
|
||||
echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
|
||||
fi
|
||||
@@ -1,114 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
name: "Publish to npm"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GIT_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GIT_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||
!contains(github.event.head_commit.message, '[skip publish]') &&
|
||||
!startsWith(github.event.repository.name, 'Template-')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Auto-bump patch version
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
|
||||
PKG_NAME=$(node -p "require('./package.json').name")
|
||||
CURRENT=$(node -p "require('./package.json').version")
|
||||
PUBLISHED=$(npm view "${PKG_NAME}@latest" version 2>/dev/null || echo "0.0.0")
|
||||
|
||||
if [ "$CURRENT" != "$PUBLISHED" ]; then
|
||||
echo "Version ${CURRENT} not yet published, using as-is."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Bump locally to get the new version
|
||||
npm version patch --no-git-tag-version
|
||||
NEW_VER=$(node -p "require('./package.json').version")
|
||||
echo "Bumping ${CURRENT} -> ${NEW_VER}"
|
||||
|
||||
# Push via MokoGIT API: branch + PR + merge
|
||||
BRANCH="chore/npm-version-bump"
|
||||
FILEPATH="package.json"
|
||||
CONTENT=$(base64 -w 0 < package.json)
|
||||
COMMIT_MSG="chore: bump to ${NEW_VER} [skip ci]"
|
||||
|
||||
# Get current file SHA on main
|
||||
FILE_SHA=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/contents/${FILEPATH}?ref=main" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
|
||||
|
||||
# Create chore branch from main
|
||||
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/branches" \
|
||||
-d "{\"new_branch_name\":\"${BRANCH}\",\"old_branch_name\":\"main\"}" 2>/dev/null || true
|
||||
|
||||
# Get file SHA on chore branch (may differ if branch already existed)
|
||||
BRANCH_SHA=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/contents/${FILEPATH}?ref=${BRANCH}" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
|
||||
[ -n "$BRANCH_SHA" ] && FILE_SHA="$BRANCH_SHA"
|
||||
|
||||
# Push package.json to chore branch
|
||||
PAYLOAD="{\"content\":\"${CONTENT}\",\"message\":\"${COMMIT_MSG}\",\"branch\":\"${BRANCH}\",\"sha\":\"${FILE_SHA}\"}"
|
||||
HTTP=$(curl -sf -o /dev/null -w "%{http_code}" -X PUT \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"${API_BASE}/contents/${FILEPATH}" 2>/dev/null || echo "ERR")
|
||||
echo "File push: HTTP ${HTTP}"
|
||||
|
||||
# Create PR
|
||||
PR_NUM=$(curl -sf -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/pulls" \
|
||||
-d "{\"title\":\"${COMMIT_MSG}\",\"head\":\"${BRANCH}\",\"base\":\"main\"}" \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin).get('number',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$PR_NUM" ]; then
|
||||
# Merge PR
|
||||
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/pulls/${PR_NUM}/merge" \
|
||||
-d "{\"Do\":\"merge\",\"merge_message_field\":\"${COMMIT_MSG}\"}" 2>/dev/null
|
||||
echo "Version bumped via PR #${PR_NUM} (merged)"
|
||||
else
|
||||
echo "::warning::Could not create PR for version bump — publishing with current version"
|
||||
fi
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Publish
|
||||
run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -1,90 +0,0 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# Enforces branch merge policy:
|
||||
# feature/* → dev only
|
||||
# fix/* → dev only
|
||||
# hotfix/* → dev or main (emergency)
|
||||
# dev → main only
|
||||
# alpha/* → dev only
|
||||
# beta/* → dev only
|
||||
# rc/* → main only
|
||||
|
||||
name: Branch Policy Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
|
||||
jobs:
|
||||
check-target:
|
||||
name: Verify merge target
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check branch policy
|
||||
run: |
|
||||
HEAD="${{ github.head_ref }}"
|
||||
BASE="${{ github.base_ref }}"
|
||||
|
||||
echo "PR: ${HEAD} → ${BASE}"
|
||||
|
||||
ALLOWED=true
|
||||
REASON=""
|
||||
|
||||
case "$HEAD" in
|
||||
feature/*|feat/*)
|
||||
if [ "$BASE" != "dev" ]; then
|
||||
ALLOWED=false
|
||||
REASON="Feature branches must target 'dev', not '${BASE}'"
|
||||
fi
|
||||
;;
|
||||
fix/*|bugfix/*)
|
||||
if [ "$BASE" != "dev" ]; then
|
||||
ALLOWED=false
|
||||
REASON="Fix branches must target 'dev', not '${BASE}'"
|
||||
fi
|
||||
;;
|
||||
hotfix/*)
|
||||
if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then
|
||||
ALLOWED=false
|
||||
REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'"
|
||||
fi
|
||||
;;
|
||||
alpha/*|beta/*)
|
||||
if [ "$BASE" != "dev" ]; then
|
||||
ALLOWED=false
|
||||
REASON="Pre-release branches must target 'dev', not '${BASE}'"
|
||||
fi
|
||||
;;
|
||||
rc/*)
|
||||
if [ "$BASE" != "main" ]; then
|
||||
ALLOWED=false
|
||||
REASON="Release candidate branches must target 'main', not '${BASE}'"
|
||||
fi
|
||||
;;
|
||||
dev)
|
||||
if [ "$BASE" != "main" ]; then
|
||||
ALLOWED=false
|
||||
REASON="Dev branch can only merge into 'main', not '${BASE}'"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$ALLOWED" = false ]; then
|
||||
echo "::error::${REASON}"
|
||||
echo ""
|
||||
echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "${REASON}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
||||
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -7,11 +7,11 @@ contact_links:
|
||||
- name: 💬 Ask a Question
|
||||
url: https://mokoconsulting.tech/
|
||||
about: Get help or ask questions through our website
|
||||
- name: 📚 MokoCLI Documentation
|
||||
url: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
- name: 📚 MokoStandards Documentation
|
||||
url: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
about: View our coding standards and best practices
|
||||
- name: 🔒 Report a Security Vulnerability
|
||||
url: https://git.mokoconsulting.tech/MokoConsulting/.github-private/security/advisories/new
|
||||
url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new
|
||||
about: Report security vulnerabilities privately (for critical issues)
|
||||
- name: 💡 Community Discussions
|
||||
url: https://github.com/orgs/mokoconsulting-tech/discussions
|
||||
+1
-1
@@ -42,7 +42,7 @@ Suggested text here
|
||||
<!-- Add any other context, screenshots, or references -->
|
||||
|
||||
## Standards Alignment
|
||||
- [ ] Follows MokoCLI documentation guidelines
|
||||
- [ ] Follows MokoStandards documentation guidelines
|
||||
- [ ] Uses en_US/en_GB localization
|
||||
- [ ] Includes proper SPDX headers where applicable
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ If you have ideas about how this could be implemented, share them here:
|
||||
Add any other context, mockups, or screenshots about the feature request here.
|
||||
|
||||
## Relevant Standards
|
||||
Does this relate to any standards in [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli)?
|
||||
Does this relate to any standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards)?
|
||||
- [ ] Accessibility (WCAG 2.1 AA)
|
||||
- [ ] Localization (en_US/en_GB)
|
||||
- [ ] Security best practices
|
||||
+1
-1
@@ -45,4 +45,4 @@ Are there other ways to accomplish this today? If so, why is an MCP integration
|
||||
- [ ] API documentation is available and accessible
|
||||
- [ ] API supports the required authentication method
|
||||
- [ ] I have tested the API endpoints manually
|
||||
- [ ] The integration follows the Template-NPM architecture pattern
|
||||
- [ ] The integration follows the Template-MCP architecture pattern
|
||||
@@ -35,7 +35,7 @@ Use this template only for:
|
||||
<!-- Describe how this could be addressed -->
|
||||
|
||||
## Standards Reference
|
||||
Does this relate to security standards in [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli)?
|
||||
Does this relate to security standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards)?
|
||||
- [ ] SPDX license identifiers
|
||||
- [ ] Secret management
|
||||
- [ ] Dependency security
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Automation
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/branch-protection.yml
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Automation
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /.gitea/workflows/branch-protection.yml
|
||||
# BRIEF: Apply standardised branch protection rules to all governed repositories
|
||||
#
|
||||
# +========================================================================+
|
||||
@@ -42,8 +42,8 @@ on:
|
||||
default: ''
|
||||
|
||||
env:
|
||||
GIT_URL: https://git.mokoconsulting.tech
|
||||
GIT_ORG: MokoConsulting
|
||||
GITEA_URL: https://git.mokoconsulting.tech
|
||||
GITEA_ORG: MokoConsulting
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -59,11 +59,11 @@ jobs:
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
run: |
|
||||
API="${GIT_URL}/api/v1"
|
||||
API="${GITEA_URL}/api/v1"
|
||||
|
||||
# Platform/standards/infra repos to exclude
|
||||
EXCLUDE="git-org-config org-profile git-private .mokogit-private mokocli MokoTesting"
|
||||
EXCLUDE="$EXCLUDE Template-Client Template-Dolibarr Template-Generic Template-Joomla MokoDoliProjTemplate"
|
||||
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards moko-platform MokoTesting"
|
||||
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
|
||||
|
||||
if [ -n "${{ inputs.repos }}" ]; then
|
||||
# User-specified repos
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
while true; do
|
||||
BATCH=$(curl -sS \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API}/orgs/${GIT_ORG}/repos?page=${PAGE}&limit=50" \
|
||||
"${API}/orgs/${GITEA_ORG}/repos?page=${PAGE}&limit=50" \
|
||||
| jq -r '.[].name // empty')
|
||||
[ -z "$BATCH" ] && break
|
||||
REPOS="$REPOS $BATCH"
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
DRY_RUN: ${{ inputs.dry_run || 'false' }}
|
||||
run: |
|
||||
API="${GIT_URL}/api/v1"
|
||||
API="${GITEA_URL}/api/v1"
|
||||
REPOS="${{ steps.repos.outputs.repos }}"
|
||||
|
||||
SUCCESS=0
|
||||
@@ -215,7 +215,7 @@ jobs:
|
||||
curl -sS -o /dev/null -w "" \
|
||||
-X DELETE \
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API}/repos/${GIT_ORG}/${REPO}/branch_protections/${ENCODED_NAME}" 2>/dev/null || true
|
||||
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections/${ENCODED_NAME}" 2>/dev/null || true
|
||||
|
||||
# Create rule
|
||||
RESPONSE=$(curl -sS -w "\n%{http_code}" \
|
||||
@@ -223,7 +223,7 @@ jobs:
|
||||
-H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$RULE" \
|
||||
"${API}/repos/${GIT_ORG}/${REPO}/branch_protections")
|
||||
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections")
|
||||
|
||||
HTTP=$(echo "$RESPONSE" | tail -1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
MokoStandards Repository Manifest
|
||||
Template: MCP Server
|
||||
See: https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home
|
||||
-->
|
||||
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0" schema-version="1.0">
|
||||
<identity>
|
||||
<name>Template-MCP</name>
|
||||
<org>MokoConsulting</org>
|
||||
<description>Template repository for Model Context Protocol (MCP) servers</description>
|
||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||
</identity>
|
||||
<governance>
|
||||
<platform>mcp</platform>
|
||||
<standards-version>05.00.00</standards-version>
|
||||
<standards-source>https://git.mokoconsulting.tech/MokoConsulting/moko-platform</standards-source>
|
||||
</governance>
|
||||
<build>
|
||||
<language>TypeScript</language>
|
||||
<package-type>mcp-server</package-type>
|
||||
<entry-point>src/</entry-point>
|
||||
</build>
|
||||
</moko-platform>
|
||||
@@ -5,7 +5,7 @@
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Workflows.Shared
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.mokogit/workflows/auto-assign.yml
|
||||
# PATH: /.mokogitea/workflows/auto-assign.yml
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Auto-assign jmiller to unassigned issues and PRs every 15 minutes
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Release
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/auto-bump.yml
|
||||
# PATH: /.mokogitea/workflows/auto-bump.yml
|
||||
# VERSION: 09.02.00
|
||||
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
||||
|
||||
@@ -22,7 +22,7 @@ on:
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -40,10 +40,10 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup MokoCLI tools
|
||||
- name: Setup mokocli tools
|
||||
run: |
|
||||
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
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
||||
/tmp/mokocli
|
||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
|
||||
@@ -62,5 +62,5 @@ jobs:
|
||||
run: |
|
||||
php ${MOKO_CLI}/version_auto_bump.php \
|
||||
--path . --branch "${GITHUB_REF_NAME}" \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}" \
|
||||
--repo-url "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
@@ -11,7 +11,7 @@
|
||||
# PATH: /templates/workflows/shared/auto-dev-issue.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Auto-create tracking issue with sub-issues for dev/rc branch workflow
|
||||
# NOTE: Synced via bulk-repo-sync to .mokogit/workflows/auto-dev-issue.yml in all governed repos.
|
||||
# NOTE: Synced via bulk-repo-sync to .mokogitea/workflows/auto-dev-issue.yml in all governed repos.
|
||||
|
||||
name: "Universal: Dev/RC Branch Issue"
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||
# PATH: /templates/workflows/universal/auto-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: Universal build & release � detects platform from manifest.xml
|
||||
# BRIEF: Universal build & release � detects platform from metadata API
|
||||
#
|
||||
# +=======================================================================+
|
||||
# | UNIVERSAL BUILD & RELEASE PIPELINE |
|
||||
# +=======================================================================+
|
||||
# | |
|
||||
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
||||
# | Reads metadata API (joomla|dolibarr|generic) to branch logic. |
|
||||
# | |
|
||||
# | Platform-specific: |
|
||||
# | joomla: XML manifest, type-prefixed packages |
|
||||
@@ -31,7 +31,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.mokogit/workflows/**'
|
||||
- '.mokogitea/workflows/**'
|
||||
- '*.md'
|
||||
- 'wiki/**'
|
||||
- '.editorconfig'
|
||||
@@ -52,9 +52,9 @@ on:
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GIT_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GIT_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -73,12 +73,12 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup MokoCLI tools
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
run: |
|
||||
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
|
||||
@@ -99,55 +99,30 @@ jobs:
|
||||
|
||||
- name: Rename branch to rc
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
AUTH="Authorization: token ${{ secrets.MOKOGIT_TOKEN }}"
|
||||
FROM="${{ github.event.pull_request.head.ref || 'dev' }}"
|
||||
PR="${{ github.event.pull_request.number }}"
|
||||
|
||||
# Resolve the source branch HEAD commit.
|
||||
SRC_JSON=$(curl -sf -H "$AUTH" "${API_BASE}/branches/${FROM}") \
|
||||
|| { echo "::error::Source branch ${FROM} not found"; exit 1; }
|
||||
SRC_SHA=$(printf '%s' "$SRC_JSON" | python3 -c "import sys, json; print(json.load(sys.stdin)['commit']['id'])" 2>/dev/null || true)
|
||||
[ -n "$SRC_SHA" ] || { echo "::error::Could not resolve HEAD of ${FROM}"; exit 1; }
|
||||
|
||||
# Point rc at the source commit via git push. Git's git/refs PATCH API
|
||||
# returns HTTP 405 on ANY protected branch (force or not, even for a user in
|
||||
# the force-push allowlist), so it cannot move a protected rc. git push honors
|
||||
# the push + force-push allowlists and creates rc if it is absent.
|
||||
PUSH_URL="https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@${MOKOGIT_URL#https://}/${GIT_ORG}/${GIT_REPO}.git"
|
||||
git config --global user.name "mokogit-actions[bot]"
|
||||
git config --global user.email "actions@mokoconsulting.tech"
|
||||
git fetch --no-tags "$PUSH_URL" "${FROM}"
|
||||
git push --force "$PUSH_URL" "FETCH_HEAD:refs/heads/rc" \
|
||||
|| { echo "::error::Failed to point rc at ${FROM} (${SRC_SHA}) via git push"; exit 1; }
|
||||
echo "rc set to ${FROM} (${SRC_SHA})"
|
||||
|
||||
# Repoint the PR at rc, then delete the old source branch (non-fatal).
|
||||
if [ -n "$PR" ]; then
|
||||
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
|
||||
"${API_BASE}/pulls/${PR}" -d '{"head":"rc"}' >/dev/null || true
|
||||
fi
|
||||
curl -s -X DELETE -H "$AUTH" "${API_BASE}/branches/${FROM}" >/dev/null || true
|
||||
echo "Renamed ${FROM} -> rc"
|
||||
php ${MOKO_CLI}/branch_rename.php \
|
||||
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--api-base "${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||
--pr "${{ github.event.pull_request.number }}"
|
||||
|
||||
- name: Checkout rc and configure git
|
||||
run: |
|
||||
git fetch origin rc
|
||||
git checkout rc
|
||||
git config --local user.email "mokogit-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "mokogit-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
|
||||
- name: Publish RC release
|
||||
run: |
|
||||
php ${MOKO_CLI}/release_publish.php \
|
||||
--path . --stability rc --bump minor --branch rc \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}"
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
- name: Update RC release notes from CHANGELOG.md
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Extract [Unreleased] section from changelog
|
||||
NOTES=""
|
||||
@@ -196,14 +171,14 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure git for bot pushes
|
||||
run: |
|
||||
git config --local user.email "mokogit-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "mokogit-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
|
||||
- name: Check for merge conflict markers
|
||||
run: |
|
||||
@@ -218,9 +193,9 @@ jobs:
|
||||
fi
|
||||
echo "No conflict markers found"
|
||||
|
||||
- name: Setup MokoCLI tools
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
|
||||
run: |
|
||||
@@ -267,7 +242,7 @@ jobs:
|
||||
fi
|
||||
php ${MOKO_CLI}/release_publish.php \
|
||||
--path . --stability stable ${BUMP_FLAG} --branch main \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}"
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
- name: "Read published version"
|
||||
id: version
|
||||
@@ -294,8 +269,8 @@ jobs:
|
||||
!startsWith(steps.platform.outputs.platform, 'joomla')
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
SEMVER_TAG="v${VERSION}"
|
||||
|
||||
echo "Creating semver tag: ${SEMVER_TAG}"
|
||||
@@ -319,8 +294,8 @@ jobs:
|
||||
|
||||
- name: Update release notes and promote changelog
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Get the stable release info (version and ID)
|
||||
RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
||||
@@ -388,10 +363,10 @@ jobs:
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_mirror.php \
|
||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "$API_BASE" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
|
||||
--branch main 2>&1 || true
|
||||
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -417,8 +392,8 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Delete rc branch (ephemeral — created by promote-rc)
|
||||
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
@@ -441,8 +416,8 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
BRANCH_NAME="version/${VERSION}"
|
||||
MAIN_SHA=$(git rev-parse HEAD)
|
||||
@@ -462,9 +437,9 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/version_reset_dev.php \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "${API_BASE}" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
||||
--branch dev --path . 2>&1 || true
|
||||
|
||||
# -- Summary --------------------------------------------------------------
|
||||
@@ -488,5 +463,5 @@ jobs:
|
||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
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
|
||||
echo "| Release | [View](${MOKOGITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Delete feature branches after PR merge
|
||||
|
||||
name: "Branch Cleanup"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Delete merged branch
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.head.ref != 'dev' &&
|
||||
github.event.pull_request.head.ref != 'main'
|
||||
|
||||
steps:
|
||||
- name: Delete source branch
|
||||
run: |
|
||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
||||
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "204" ]; then
|
||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "$STATUS" = "404" ]; then
|
||||
echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
|
||||
fi
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
# PATH: /templates/workflows/shared/changelog-validation.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Validates CHANGELOG.md format and version consistency
|
||||
# NOTE: Deployed to .mokogit/workflows/changelog-validation.yml in governed repos.
|
||||
# NOTE: Deployed to .mokogitea/workflows/changelog-validation.yml in governed repos.
|
||||
|
||||
name: "Universal: Changelog Validation"
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.CI
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
||||
# PATH: /.mokogit/workflows/ci-generic.yml
|
||||
# PATH: /.gitea/workflows/ci-generic.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
|
||||
|
||||
+10
-10
@@ -3,12 +3,12 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Universal
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/ci-issue-reporter.yml
|
||||
# PATH: /.mokogitea/workflows/ci-issue-reporter.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Reusable workflow — creates/updates a MokoGIT issue when a CI gate fails.
|
||||
# BRIEF: Reusable workflow — creates/updates a Gitea issue when a CI gate fails.
|
||||
# Clones MokoCLI and runs cli/ci_issue_reporter.sh.
|
||||
|
||||
name: "Universal: CI Issue Reporter"
|
||||
@@ -35,7 +35,7 @@ on:
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
MOKOGIT_TOKEN:
|
||||
MOKOGITEA_TOKEN:
|
||||
required: true
|
||||
|
||||
env:
|
||||
@@ -49,16 +49,16 @@ jobs:
|
||||
steps:
|
||||
- name: Clone MokoCLI
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
MOKOGIT_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 --filter=blob:none --sparse "${MOKOGIT_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
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
|
||||
|
||||
- name: Report CI failure
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
run: |
|
||||
chmod +x /tmp/mokocli/cli/ci_issue_reporter.sh
|
||||
/tmp/mokocli/cli/ci_issue_reporter.sh \
|
||||
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/cleanup.yml
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Maintenance
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# PATH: /.gitea/workflows/cleanup.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
||||
|
||||
@@ -21,7 +21,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
@@ -33,17 +33,17 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Delete merged branches
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Merged Branch Cleanup ==="
|
||||
API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
|
||||
# List branches via API
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${MOKOGIT_TOKEN}" \
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/branches?limit=50" | jq -r '.[].name')
|
||||
|
||||
DELETED=0
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
# Check if branch is merged into main
|
||||
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
|
||||
echo " Deleting merged branch: ${BRANCH}"
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGIT_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/branches/${BRANCH}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
fi
|
||||
@@ -66,20 +66,20 @@ jobs:
|
||||
|
||||
- name: Clean old workflow runs
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Workflow Run Cleanup ==="
|
||||
API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Get old completed runs
|
||||
RUNS=$(curl -sS -H "Authorization: token ${MOKOGIT_TOKEN}" \
|
||||
RUNS=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/actions/runs?status=completed&limit=50" | \
|
||||
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
|
||||
|
||||
DELETED=0
|
||||
for RUN_ID in $RUNS; do
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGIT_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
done
|
||||
@@ -0,0 +1,115 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow.Template
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/generic/codeql-analysis.yml.template
|
||||
# VERSION: 04.05.00
|
||||
# BRIEF: CodeQL security scanning workflow (generic — all repo types)
|
||||
# NOTE: Deployed to .mokogitea/workflows/codeql-analysis.yml in governed repos.
|
||||
# CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell.
|
||||
# For PHP-specific security scanning see standards-compliance.yml.
|
||||
|
||||
name: "Universal: CodeQL Analysis"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
- version/**
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
schedule:
|
||||
# Weekly on Monday at 06:00 UTC
|
||||
- cron: '0 6 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 360
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# CodeQL does not support PHP. Use 'javascript' to scan JSON, YAML,
|
||||
# and shell scripts. Add 'actions' to scan GitHub Actions workflows.
|
||||
language: ['javascript', 'actions']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: security-extended,security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
upload: true
|
||||
output: sarif-results
|
||||
wait-for-processing: true
|
||||
|
||||
- name: Upload SARIF results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.5.0
|
||||
with:
|
||||
name: codeql-results-${{ matrix.language }}
|
||||
path: sarif-results
|
||||
retention-days: 30
|
||||
|
||||
- name: Step summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "### 🔍 CodeQL — ${{ matrix.language }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
URL="https://github.com/${{ github.repository }}/security/code-scanning"
|
||||
echo "See the [Security tab]($URL) for findings." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Severity | SLA |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|----------|-----|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Critical | 7 days |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| High | 14 days |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Medium | 30 days |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Low | 60 days / next release |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
summary:
|
||||
name: Security Scan Summary
|
||||
runs-on: ubuntu-latest
|
||||
needs: analyze
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "### 🛡️ CodeQL Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
SECURITY_URL="https://github.com/${{ github.repository }}/security"
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📊 [View all security alerts]($SECURITY_URL)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-LICENSE-IDENTIFIER: GPL-3.0-or-later
|
||||
#
|
||||
# GitHub Actions workflow for Copilot coding agent
|
||||
# This workflow demonstrates how to use the firewall configuration
|
||||
|
||||
name: "MCP: Copilot Agent"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
copilot-agent:
|
||||
name: Run Copilot Coding Agent
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure Copilot Firewall
|
||||
run: |
|
||||
echo "Configuring firewall allowlist for enterprise-ready sites..."
|
||||
bash .github/copilot/setup-firewall.sh
|
||||
echo "Firewall configuration completed"
|
||||
|
||||
- name: Run Copilot Agent
|
||||
uses: github/copilot-swe-agent@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue_number: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||
env:
|
||||
# Environment variables are set by setup-firewall.sh
|
||||
COPILOT_FIREWALL_ALLOWLIST: ${{ env.COPILOT_FIREWALL_ALLOWLIST }}
|
||||
@@ -0,0 +1,758 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Firewall
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Enterprise firewall configuration — generates outbound allow-rules including SFTP deployment server
|
||||
# NOTE: Reads DEV_FTP_HOST / DEV_FTP_PORT variables to include SFTP egress rules alongside HTTPS rules.
|
||||
|
||||
name: "MCP: Enterprise Firewall"
|
||||
|
||||
# This workflow provides firewall configuration guidance for enterprise-ready sites
|
||||
# It generates firewall rules for allowing outbound access to trusted domains
|
||||
# including license providers, documentation sources, package registries,
|
||||
# and the SFTP deployment server (DEV_FTP_HOST / DEV_FTP_PORT).
|
||||
#
|
||||
# Runs automatically when:
|
||||
# - Coding agent workflows are triggered (pull requests with copilot/ prefix)
|
||||
# - Manual workflow dispatch for custom configurations
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
firewall_type:
|
||||
description: 'Target firewall type'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- 'iptables'
|
||||
- 'ufw'
|
||||
- 'firewalld'
|
||||
- 'aws-security-group'
|
||||
- 'azure-nsg'
|
||||
- 'gcp-firewall'
|
||||
- 'cloudflare'
|
||||
- 'all'
|
||||
default: 'all'
|
||||
output_format:
|
||||
description: 'Output format'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- 'shell-script'
|
||||
- 'json'
|
||||
- 'yaml'
|
||||
- 'markdown'
|
||||
- 'all'
|
||||
default: 'markdown'
|
||||
|
||||
# Auto-run when coding agent creates or updates PRs
|
||||
pull_request:
|
||||
branches:
|
||||
- 'copilot/**'
|
||||
- 'agent/**'
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Auto-run on push to coding agent branches
|
||||
push:
|
||||
branches:
|
||||
- 'copilot/**'
|
||||
- 'agent/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
generate-firewall-rules:
|
||||
name: Generate Firewall Rules
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Apply Firewall Rules to Runner (Auto-run only)
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
env:
|
||||
DEV_FTP_HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
DEV_FTP_PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
run: |
|
||||
echo "🔥 Applying firewall rules for coding agent environment..."
|
||||
echo ""
|
||||
echo "This step ensures the GitHub Actions runner can access trusted domains"
|
||||
echo "including license providers, package registries, and documentation sources."
|
||||
echo ""
|
||||
|
||||
# Note: GitHub Actions runners are ephemeral and run in controlled environments
|
||||
# This step documents what domains are being accessed during the workflow
|
||||
# Actual firewall configuration is managed by GitHub
|
||||
|
||||
cat > /tmp/trusted-domains.txt << 'EOF'
|
||||
# Trusted domains for coding agent environment
|
||||
# License Providers
|
||||
www.gnu.org
|
||||
opensource.org
|
||||
choosealicense.com
|
||||
spdx.org
|
||||
creativecommons.org
|
||||
apache.org
|
||||
fsf.org
|
||||
|
||||
# Documentation & Standards
|
||||
semver.org
|
||||
keepachangelog.com
|
||||
conventionalcommits.org
|
||||
|
||||
# GitHub & Related
|
||||
github.com
|
||||
api.github.com
|
||||
docs.github.com
|
||||
raw.githubusercontent.com
|
||||
ghcr.io
|
||||
|
||||
# Package Registries
|
||||
npmjs.com
|
||||
registry.npmjs.org
|
||||
pypi.org
|
||||
files.pythonhosted.org
|
||||
packagist.org
|
||||
repo.packagist.org
|
||||
rubygems.org
|
||||
|
||||
# Platform-Specific
|
||||
joomla.org
|
||||
downloads.joomla.org
|
||||
docs.joomla.org
|
||||
php.net
|
||||
getcomposer.org
|
||||
dolibarr.org
|
||||
wiki.dolibarr.org
|
||||
docs.dolibarr.org
|
||||
|
||||
# Moko Consulting
|
||||
mokoconsulting.tech
|
||||
|
||||
# SFTP Deployment Server (DEV_FTP_HOST)
|
||||
${DEV_FTP_HOST:-<not configured>}
|
||||
|
||||
# Google Services
|
||||
drive.google.com
|
||||
docs.google.com
|
||||
sheets.google.com
|
||||
accounts.google.com
|
||||
storage.googleapis.com
|
||||
fonts.googleapis.com
|
||||
fonts.gstatic.com
|
||||
|
||||
# GitHub Extended
|
||||
upload.github.com
|
||||
objects.githubusercontent.com
|
||||
user-images.githubusercontent.com
|
||||
codeload.github.com
|
||||
pkg.github.com
|
||||
|
||||
# Developer Reference
|
||||
developer.mozilla.org
|
||||
stackoverflow.com
|
||||
git-scm.com
|
||||
|
||||
# CDN & Infrastructure
|
||||
cdn.jsdelivr.net
|
||||
unpkg.com
|
||||
cdnjs.cloudflare.com
|
||||
img.shields.io
|
||||
|
||||
# Container Registries
|
||||
hub.docker.com
|
||||
registry-1.docker.io
|
||||
|
||||
# CI & Code Quality
|
||||
codecov.io
|
||||
sonarcloud.io
|
||||
|
||||
# Terraform & Infrastructure
|
||||
registry.terraform.io
|
||||
releases.hashicorp.com
|
||||
checkpoint-api.hashicorp.com
|
||||
EOF
|
||||
|
||||
echo "✓ Trusted domains documented for this runner"
|
||||
echo "✓ GitHub Actions runners have network access to these domains"
|
||||
echo ""
|
||||
|
||||
# Test connectivity to key domains
|
||||
echo "Testing connectivity to key domains..."
|
||||
for domain in "github.com" "www.gnu.org" "npmjs.com" "pypi.org"; do
|
||||
if curl -s --max-time 3 -o /dev/null -w "%{http_code}" "https://$domain" | grep -q "200\|301\|302"; then
|
||||
echo " ✓ $domain is accessible"
|
||||
else
|
||||
echo " ⚠️ $domain connectivity check failed (may be expected)"
|
||||
fi
|
||||
done
|
||||
|
||||
# Test SFTP server connectivity (TCP port check)
|
||||
SFTP_HOST="${DEV_FTP_HOST:-}"
|
||||
SFTP_PORT="${DEV_FTP_PORT:-22}"
|
||||
if [ -n "$SFTP_HOST" ]; then
|
||||
# Strip any embedded :port suffix
|
||||
SFTP_HOST="${SFTP_HOST%%:*}"
|
||||
echo ""
|
||||
echo "Testing SFTP deployment server connectivity..."
|
||||
if timeout 5 bash -c "echo >/dev/tcp/${SFTP_HOST}/${SFTP_PORT}" 2>/dev/null; then
|
||||
echo " ✓ SFTP server ${SFTP_HOST}:${SFTP_PORT} is reachable"
|
||||
else
|
||||
echo " ⚠️ SFTP server ${SFTP_HOST}:${SFTP_PORT} is not reachable from runner (firewall rule needed)"
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo " ℹ️ DEV_FTP_HOST not configured — skipping SFTP connectivity check"
|
||||
fi
|
||||
|
||||
- name: Generate Firewall Configuration
|
||||
id: generate
|
||||
env:
|
||||
DEV_FTP_HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
DEV_FTP_PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
run: |
|
||||
cat > generate_firewall_config.py << 'PYTHON_EOF'
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Enterprise Firewall Configuration Generator
|
||||
|
||||
Generates firewall rules for enterprise-ready deployments allowing
|
||||
access to trusted domains including license providers, documentation
|
||||
sources, package registries, and platform-specific sites.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
import sys
|
||||
from typing import List, Dict
|
||||
|
||||
# SFTP deployment server from org variables
|
||||
_sftp_host_raw = os.environ.get("DEV_FTP_HOST", "").strip()
|
||||
_sftp_port = os.environ.get("DEV_FTP_PORT", "").strip() or "22"
|
||||
# Strip embedded :port suffix if present
|
||||
_sftp_host = _sftp_host_raw.split(":")[0] if _sftp_host_raw else ""
|
||||
if ":" in _sftp_host_raw and not _sftp_port:
|
||||
_sftp_port = _sftp_host_raw.split(":")[1]
|
||||
|
||||
SFTP_HOST = _sftp_host
|
||||
SFTP_PORT = int(_sftp_port) if _sftp_port.isdigit() else 22
|
||||
|
||||
# Trusted domains from .github/copilot.yml
|
||||
TRUSTED_DOMAINS = {
|
||||
"license_providers": [
|
||||
"www.gnu.org",
|
||||
"opensource.org",
|
||||
"choosealicense.com",
|
||||
"spdx.org",
|
||||
"creativecommons.org",
|
||||
"apache.org",
|
||||
"fsf.org",
|
||||
],
|
||||
"documentation_standards": [
|
||||
"semver.org",
|
||||
"keepachangelog.com",
|
||||
"conventionalcommits.org",
|
||||
],
|
||||
"github_related": [
|
||||
"github.com",
|
||||
"api.github.com",
|
||||
"docs.github.com",
|
||||
"raw.githubusercontent.com",
|
||||
"ghcr.io",
|
||||
],
|
||||
"package_registries": [
|
||||
"npmjs.com",
|
||||
"registry.npmjs.org",
|
||||
"pypi.org",
|
||||
"files.pythonhosted.org",
|
||||
"packagist.org",
|
||||
"repo.packagist.org",
|
||||
"rubygems.org",
|
||||
],
|
||||
"standards_organizations": [
|
||||
"json-schema.org",
|
||||
"w3.org",
|
||||
"ietf.org",
|
||||
],
|
||||
"platform_specific": [
|
||||
"joomla.org",
|
||||
"downloads.joomla.org",
|
||||
"docs.joomla.org",
|
||||
"php.net",
|
||||
"getcomposer.org",
|
||||
"dolibarr.org",
|
||||
"wiki.dolibarr.org",
|
||||
"docs.dolibarr.org",
|
||||
],
|
||||
"moko_consulting": [
|
||||
"mokoconsulting.tech",
|
||||
],
|
||||
"google_services": [
|
||||
"drive.google.com",
|
||||
"docs.google.com",
|
||||
"sheets.google.com",
|
||||
"accounts.google.com",
|
||||
"storage.googleapis.com",
|
||||
"fonts.googleapis.com",
|
||||
"fonts.gstatic.com",
|
||||
],
|
||||
"github_extended": [
|
||||
"upload.github.com",
|
||||
"objects.githubusercontent.com",
|
||||
"user-images.githubusercontent.com",
|
||||
"codeload.github.com",
|
||||
"pkg.github.com",
|
||||
],
|
||||
"developer_reference": [
|
||||
"developer.mozilla.org",
|
||||
"stackoverflow.com",
|
||||
"git-scm.com",
|
||||
],
|
||||
"cdn_and_infrastructure": [
|
||||
"cdn.jsdelivr.net",
|
||||
"unpkg.com",
|
||||
"cdnjs.cloudflare.com",
|
||||
"img.shields.io",
|
||||
],
|
||||
"container_registries": [
|
||||
"hub.docker.com",
|
||||
"registry-1.docker.io",
|
||||
],
|
||||
"ci_code_quality": [
|
||||
"codecov.io",
|
||||
"sonarcloud.io",
|
||||
],
|
||||
"terraform_infrastructure": [
|
||||
"registry.terraform.io",
|
||||
"releases.hashicorp.com",
|
||||
"checkpoint-api.hashicorp.com",
|
||||
],
|
||||
}
|
||||
|
||||
# Inject SFTP deployment server as a separate category (port 22, not 443)
|
||||
if SFTP_HOST:
|
||||
TRUSTED_DOMAINS["sftp_deployment_server"] = [SFTP_HOST]
|
||||
print(f"ℹ️ SFTP deployment server: {SFTP_HOST}:{SFTP_PORT}")
|
||||
|
||||
def generate_sftp_iptables_rules(host: str, port: int) -> str:
|
||||
"""Generate iptables rules specifically for SFTP egress"""
|
||||
return (
|
||||
f"# Allow SFTP to deployment server {host}:{port}\n"
|
||||
f"iptables -A OUTPUT -p tcp -d $(dig +short {host} | head -1)"
|
||||
f" --dport {port} -j ACCEPT # SFTP deploy\n"
|
||||
)
|
||||
|
||||
def generate_sftp_ufw_rules(host: str, port: int) -> str:
|
||||
"""Generate UFW rules for SFTP egress"""
|
||||
return (
|
||||
f"# Allow SFTP to deployment server\n"
|
||||
f"ufw allow out to $(dig +short {host} | head -1)"
|
||||
f" port {port} proto tcp comment 'SFTP deploy to {host}'\n"
|
||||
)
|
||||
|
||||
def generate_sftp_firewalld_rules(host: str, port: int) -> str:
|
||||
"""Generate firewalld rules for SFTP egress"""
|
||||
return (
|
||||
f"# Allow SFTP to deployment server\n"
|
||||
f"firewall-cmd --permanent --add-rich-rule='"
|
||||
f"rule family=ipv4 destination address=$(dig +short {host} | head -1)"
|
||||
f" port port={port} protocol=tcp accept' # SFTP deploy\n"
|
||||
)
|
||||
|
||||
def generate_iptables_rules(domains: List[str]) -> str:
|
||||
"""Generate iptables firewall rules"""
|
||||
rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - iptables", ""]
|
||||
rules.append("# Allow outbound HTTPS to trusted domains")
|
||||
rules.append("")
|
||||
|
||||
for domain in domains:
|
||||
rules.append(f"# Allow {domain}")
|
||||
rules.append(f"iptables -A OUTPUT -p tcp -d $(dig +short {domain} | head -1) --dport 443 -j ACCEPT")
|
||||
|
||||
rules.append("")
|
||||
rules.append("# Allow DNS lookups")
|
||||
rules.append("iptables -A OUTPUT -p udp --dport 53 -j ACCEPT")
|
||||
rules.append("iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT")
|
||||
|
||||
return "\n".join(rules)
|
||||
|
||||
def generate_ufw_rules(domains: List[str]) -> str:
|
||||
"""Generate UFW firewall rules"""
|
||||
rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - UFW", ""]
|
||||
rules.append("# Allow outbound HTTPS to trusted domains")
|
||||
rules.append("")
|
||||
|
||||
for domain in domains:
|
||||
rules.append(f"# Allow {domain}")
|
||||
rules.append(f"ufw allow out to $(dig +short {domain} | head -1) port 443 proto tcp comment 'Allow {domain}'")
|
||||
|
||||
rules.append("")
|
||||
rules.append("# Allow DNS")
|
||||
rules.append("ufw allow out 53/udp comment 'Allow DNS UDP'")
|
||||
rules.append("ufw allow out 53/tcp comment 'Allow DNS TCP'")
|
||||
|
||||
return "\n".join(rules)
|
||||
|
||||
def generate_firewalld_rules(domains: List[str]) -> str:
|
||||
"""Generate firewalld rules"""
|
||||
rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - firewalld", ""]
|
||||
rules.append("# Add trusted domains to firewall")
|
||||
rules.append("")
|
||||
|
||||
for domain in domains:
|
||||
rules.append(f"# Allow {domain}")
|
||||
rules.append(f"firewall-cmd --permanent --add-rich-rule='rule family=ipv4 destination address=$(dig +short {domain} | head -1) port port=443 protocol=tcp accept'")
|
||||
|
||||
rules.append("")
|
||||
rules.append("# Reload firewall")
|
||||
rules.append("firewall-cmd --reload")
|
||||
|
||||
return "\n".join(rules)
|
||||
|
||||
def generate_aws_security_group(domains: List[str]) -> Dict:
|
||||
"""Generate AWS Security Group rules (JSON format)"""
|
||||
rules = {
|
||||
"SecurityGroupRules": {
|
||||
"Egress": []
|
||||
}
|
||||
}
|
||||
|
||||
for domain in domains:
|
||||
rules["SecurityGroupRules"]["Egress"].append({
|
||||
"Description": f"Allow HTTPS to {domain}",
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 443,
|
||||
"ToPort": 443,
|
||||
"CidrIp": "0.0.0.0/0", # In practice, resolve to specific IPs
|
||||
"Tags": [{
|
||||
"Key": "Domain",
|
||||
"Value": domain
|
||||
}]
|
||||
})
|
||||
|
||||
# Add DNS
|
||||
rules["SecurityGroupRules"]["Egress"].append({
|
||||
"Description": "Allow DNS",
|
||||
"IpProtocol": "udp",
|
||||
"FromPort": 53,
|
||||
"ToPort": 53,
|
||||
"CidrIp": "0.0.0.0/0"
|
||||
})
|
||||
|
||||
return rules
|
||||
|
||||
def generate_markdown_documentation(domains_by_category: Dict[str, List[str]]) -> str:
|
||||
"""Generate markdown documentation"""
|
||||
md = ["# Enterprise Firewall Configuration Guide", ""]
|
||||
md.append("## Overview")
|
||||
md.append("")
|
||||
md.append("This document provides firewall configuration guidance for enterprise-ready deployments.")
|
||||
md.append("It lists trusted domains that should be whitelisted for outbound access to ensure")
|
||||
md.append("proper functionality of license validation, package management, and documentation access.")
|
||||
md.append("")
|
||||
|
||||
md.append("## Trusted Domains by Category")
|
||||
md.append("")
|
||||
|
||||
all_domains = []
|
||||
for category, domains in domains_by_category.items():
|
||||
category_name = category.replace("_", " ").title()
|
||||
md.append(f"### {category_name}")
|
||||
md.append("")
|
||||
md.append("| Domain | Purpose |")
|
||||
md.append("|--------|---------|")
|
||||
|
||||
for domain in domains:
|
||||
all_domains.append(domain)
|
||||
purpose = get_domain_purpose(domain)
|
||||
md.append(f"| `{domain}` | {purpose} |")
|
||||
|
||||
md.append("")
|
||||
|
||||
md.append("## Implementation Examples")
|
||||
md.append("")
|
||||
|
||||
md.append("### iptables Example")
|
||||
md.append("")
|
||||
md.append("```bash")
|
||||
md.append("# Allow HTTPS to trusted domain")
|
||||
md.append(f"iptables -A OUTPUT -p tcp -d $(dig +short {all_domains[0]}) --dport 443 -j ACCEPT")
|
||||
md.append("```")
|
||||
md.append("")
|
||||
|
||||
md.append("### UFW Example")
|
||||
md.append("")
|
||||
md.append("```bash")
|
||||
md.append("# Allow HTTPS to trusted domain")
|
||||
md.append(f"ufw allow out to {all_domains[0]} port 443 proto tcp")
|
||||
md.append("```")
|
||||
md.append("")
|
||||
|
||||
md.append("### AWS Security Group Example")
|
||||
md.append("")
|
||||
md.append("```json")
|
||||
md.append("{")
|
||||
md.append(' "IpPermissions": [{')
|
||||
md.append(' "IpProtocol": "tcp",')
|
||||
md.append(' "FromPort": 443,')
|
||||
md.append(' "ToPort": 443,')
|
||||
md.append(' "IpRanges": [{"CidrIp": "0.0.0.0/0", "Description": "HTTPS to trusted domains"}]')
|
||||
md.append(" }]")
|
||||
md.append("}")
|
||||
md.append("```")
|
||||
md.append("")
|
||||
|
||||
md.append("## Ports Required")
|
||||
md.append("")
|
||||
md.append("| Port | Protocol | Purpose |")
|
||||
md.append("|------|----------|---------|")
|
||||
md.append("| 443 | TCP | HTTPS (secure web access) |")
|
||||
md.append("| 80 | TCP | HTTP (redirects to HTTPS) |")
|
||||
md.append("| 53 | UDP/TCP | DNS resolution |")
|
||||
md.append("")
|
||||
|
||||
md.append("## Security Considerations")
|
||||
md.append("")
|
||||
md.append("1. **DNS Resolution**: Ensure DNS queries are allowed (port 53 UDP/TCP)")
|
||||
md.append("2. **Certificate Validation**: HTTPS requires ability to reach certificate authorities")
|
||||
md.append("3. **Dynamic IPs**: Some domains use CDNs with dynamic IPs - consider using FQDNs in rules")
|
||||
md.append("4. **Regular Updates**: Review and update whitelist as services change")
|
||||
md.append("5. **Logging**: Enable logging for blocked connections to identify missing rules")
|
||||
md.append("")
|
||||
|
||||
md.append("## Compliance Notes")
|
||||
md.append("")
|
||||
md.append("- All listed domains provide read-only access to public information")
|
||||
md.append("- License providers enable GPL compliance verification")
|
||||
md.append("- Package registries support dependency security scanning")
|
||||
md.append("- No authentication credentials are transmitted to these domains")
|
||||
md.append("")
|
||||
|
||||
return "\n".join(md)
|
||||
|
||||
def get_domain_purpose(domain: str) -> str:
|
||||
"""Get human-readable purpose for a domain"""
|
||||
purposes = {
|
||||
"www.gnu.org": "GNU licenses and documentation",
|
||||
"opensource.org": "Open Source Initiative resources",
|
||||
"choosealicense.com": "GitHub license selection tool",
|
||||
"spdx.org": "Software Package Data Exchange identifiers",
|
||||
"creativecommons.org": "Creative Commons licenses",
|
||||
"apache.org": "Apache Software Foundation licenses",
|
||||
"fsf.org": "Free Software Foundation resources",
|
||||
"semver.org": "Semantic versioning specification",
|
||||
"keepachangelog.com": "Changelog format standards",
|
||||
"conventionalcommits.org": "Commit message conventions",
|
||||
"github.com": "GitHub platform access",
|
||||
"api.github.com": "GitHub API access",
|
||||
"docs.github.com": "GitHub documentation",
|
||||
"raw.githubusercontent.com": "GitHub raw content access",
|
||||
"npmjs.com": "npm package registry",
|
||||
"pypi.org": "Python Package Index",
|
||||
"packagist.org": "PHP Composer package registry",
|
||||
"rubygems.org": "Ruby gems registry",
|
||||
"joomla.org": "Joomla CMS platform",
|
||||
"php.net": "PHP documentation and downloads",
|
||||
"dolibarr.org": "Dolibarr ERP/CRM platform",
|
||||
}
|
||||
return purposes.get(domain, "Trusted resource")
|
||||
|
||||
def main():
|
||||
# Use inputs if provided (manual dispatch), otherwise use defaults (auto-run)
|
||||
firewall_type = "${{ github.event.inputs.firewall_type }}" or "all"
|
||||
output_format = "${{ github.event.inputs.output_format }}" or "markdown"
|
||||
|
||||
print(f"Running in {'manual' if '${{ github.event.inputs.firewall_type }}' else 'automatic'} mode")
|
||||
print(f"Firewall type: {firewall_type}")
|
||||
print(f"Output format: {output_format}")
|
||||
print("")
|
||||
|
||||
# Collect all domains
|
||||
all_domains = []
|
||||
for domains in TRUSTED_DOMAINS.values():
|
||||
all_domains.extend(domains)
|
||||
|
||||
# Remove duplicates and sort
|
||||
all_domains = sorted(set(all_domains))
|
||||
|
||||
print(f"Generating firewall rules for {len(all_domains)} trusted domains...")
|
||||
print("")
|
||||
|
||||
# Exclude SFTP server from HTTPS rule generation (different port)
|
||||
https_domains = [d for d in all_domains if d != SFTP_HOST]
|
||||
|
||||
# Generate based on firewall type
|
||||
if firewall_type in ["iptables", "all"]:
|
||||
rules = generate_iptables_rules(https_domains)
|
||||
if SFTP_HOST:
|
||||
rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n"
|
||||
rules += generate_sftp_iptables_rules(SFTP_HOST, SFTP_PORT)
|
||||
with open("firewall-rules-iptables.sh", "w") as f:
|
||||
f.write(rules)
|
||||
print("✓ Generated iptables rules: firewall-rules-iptables.sh")
|
||||
|
||||
if firewall_type in ["ufw", "all"]:
|
||||
rules = generate_ufw_rules(https_domains)
|
||||
if SFTP_HOST:
|
||||
rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n"
|
||||
rules += generate_sftp_ufw_rules(SFTP_HOST, SFTP_PORT)
|
||||
with open("firewall-rules-ufw.sh", "w") as f:
|
||||
f.write(rules)
|
||||
print("✓ Generated UFW rules: firewall-rules-ufw.sh")
|
||||
|
||||
if firewall_type in ["firewalld", "all"]:
|
||||
rules = generate_firewalld_rules(https_domains)
|
||||
if SFTP_HOST:
|
||||
rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n"
|
||||
rules += generate_sftp_firewalld_rules(SFTP_HOST, SFTP_PORT)
|
||||
with open("firewall-rules-firewalld.sh", "w") as f:
|
||||
f.write(rules)
|
||||
print("✓ Generated firewalld rules: firewall-rules-firewalld.sh")
|
||||
|
||||
if firewall_type in ["aws-security-group", "all"]:
|
||||
rules = generate_aws_security_group(all_domains)
|
||||
with open("firewall-rules-aws-sg.json", "w") as f:
|
||||
json.dump(rules, f, indent=2)
|
||||
print("✓ Generated AWS Security Group rules: firewall-rules-aws-sg.json")
|
||||
|
||||
if output_format in ["yaml", "all"]:
|
||||
with open("trusted-domains.yml", "w") as f:
|
||||
yaml.dump(TRUSTED_DOMAINS, f, default_flow_style=False)
|
||||
print("✓ Generated YAML domain list: trusted-domains.yml")
|
||||
|
||||
if output_format in ["json", "all"]:
|
||||
with open("trusted-domains.json", "w") as f:
|
||||
json.dump(TRUSTED_DOMAINS, f, indent=2)
|
||||
print("✓ Generated JSON domain list: trusted-domains.json")
|
||||
|
||||
if output_format in ["markdown", "all"]:
|
||||
md = generate_markdown_documentation(TRUSTED_DOMAINS)
|
||||
with open("FIREWALL_CONFIGURATION.md", "w") as f:
|
||||
f.write(md)
|
||||
print("✓ Generated documentation: FIREWALL_CONFIGURATION.md")
|
||||
|
||||
print("")
|
||||
print("Domain Categories:")
|
||||
for category, domains in TRUSTED_DOMAINS.items():
|
||||
print(f" - {category}: {len(domains)} domains")
|
||||
|
||||
print("")
|
||||
print("Total unique domains: ", len(all_domains))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
PYTHON_EOF
|
||||
|
||||
chmod +x generate_firewall_config.py
|
||||
pip install PyYAML
|
||||
python3 generate_firewall_config.py
|
||||
|
||||
- name: Upload Firewall Configuration Artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: firewall-configurations
|
||||
path: |
|
||||
firewall-rules-*.sh
|
||||
firewall-rules-*.json
|
||||
trusted-domains.*
|
||||
FIREWALL_CONFIGURATION.md
|
||||
retention-days: 90
|
||||
|
||||
- name: Display Summary
|
||||
run: |
|
||||
echo "## Firewall Configuration" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "**Mode**: Manual Execution" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Firewall rules have been generated for enterprise-ready deployments." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "**Mode**: Automatic Execution (Coding Agent Active)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "This workflow ran automatically because a coding agent (GitHub Copilot) is active." >> $GITHUB_STEP_SUMMARY
|
||||
echo "Firewall configuration has been validated for the coding agent environment." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Files Generated" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if ls firewall-rules-* trusted-domains.* FIREWALL_CONFIGURATION.md 2>/dev/null; then
|
||||
ls -lh firewall-rules-* trusted-domains.* FIREWALL_CONFIGURATION.md 2>/dev/null | awk '{print "- " $9 " (" $5 ")"}' >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "- Documentation generated" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "### Download Artifacts" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Download the generated firewall configurations from the workflow artifacts." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "### Trusted Domains Active" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "The coding agent has access to:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- License providers (GPL, OSI, SPDX, Apache, etc.)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Package registries (npm, PyPI, Packagist, RubyGems)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Documentation sources (GitHub, Joomla, Dolibarr, PHP)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Standards organizations (W3C, IETF, JSON Schema)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Usage Instructions:
|
||||
#
|
||||
# This workflow runs in two modes:
|
||||
#
|
||||
# 1. AUTOMATIC MODE (Coding Agent):
|
||||
# - Triggers when coding agent branches (copilot/**, agent/**) are pushed or PR'd
|
||||
# - Validates firewall configuration for the coding agent environment
|
||||
# - Documents accessible domains for compliance
|
||||
# - Ensures license sources and package registries are available
|
||||
#
|
||||
# 2. MANUAL MODE (Enterprise Configuration):
|
||||
# - Manually trigger from the Actions tab
|
||||
# - Select desired firewall type and output format
|
||||
# - Download generated artifacts
|
||||
# - Apply firewall rules to your enterprise environment
|
||||
#
|
||||
# Configuration:
|
||||
# - Trusted domains are sourced from .github/copilot.yml
|
||||
# - Modify copilot.yml to add/remove trusted domains
|
||||
# - Changes automatically propagate to firewall rules
|
||||
#
|
||||
# Important Notes:
|
||||
# - Review generated rules before applying to production
|
||||
# - Some domains may use CDNs with dynamic IPs
|
||||
# - Consider using FQDN-based rules where supported
|
||||
# - Test thoroughly in staging environment first
|
||||
# - Monitor logs for blocked connections
|
||||
# - Update rules as domains/services change
|
||||
@@ -3,9 +3,9 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Security
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||
# PATH: /templates/workflows/gitleaks.yml.template
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
||||
@@ -34,7 +34,7 @@ permissions:
|
||||
|
||||
env:
|
||||
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
|
||||
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'git-security' }}
|
||||
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
|
||||
|
||||
jobs:
|
||||
gitleaks:
|
||||
@@ -3,8 +3,8 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Automation
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Automation
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
@@ -19,7 +19,7 @@ permissions:
|
||||
issues: write
|
||||
|
||||
env:
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
create-branch:
|
||||
@@ -27,22 +27,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create branch and comment
|
||||
# SECURITY: never interpolate github.event.* into a run: script — the
|
||||
# Actions engine splices the raw value into the shell source (command
|
||||
# injection via a crafted issue title). Pass everything through env so
|
||||
# the values arrive as ordinary shell variables that are not re-parsed.
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
ISSUE_NUM: ${{ github.event.issue.number }}
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
run: |
|
||||
TOKEN="${MOKOGIT_TOKEN}"
|
||||
API="${MOKOGIT_URL}/api/v1/repos/${REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
ISSUE_NUM="${{ github.event.issue.number }}"
|
||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||
|
||||
# Build slug from title: lowercase, replace non-alnum with dash, trim.
|
||||
# printf (not echo) so a title beginning with "-" is not read as flags.
|
||||
SLUG=$(printf '%s' "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40)
|
||||
# Build slug from title: lowercase, replace non-alnum with dash, trim
|
||||
SLUG=$(echo "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40)
|
||||
BRANCH="feature/${ISSUE_NUM}-${SLUG}"
|
||||
|
||||
# Check dev branch exists
|
||||
@@ -66,7 +58,7 @@ jobs:
|
||||
echo "Created branch: ${BRANCH}"
|
||||
|
||||
# Comment on issue with branch link
|
||||
REPO_URL="${MOKOGIT_URL}/${REPO}"
|
||||
REPO_URL="${MOKOGITEA_URL}/${{ github.repository }}"
|
||||
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
|
||||
|
||||
curl -sf -X POST \
|
||||
@@ -32,8 +32,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
||||
github.actor != 'github-actions[bot]' &&
|
||||
!startsWith(github.event.repository.name, 'Template-')
|
||||
github.actor != 'github-actions[bot]'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -88,13 +87,13 @@ jobs:
|
||||
done
|
||||
|
||||
# ── Version ──────────────────────────────────────────────────────
|
||||
- name: Setup MokoCLI tools
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/MokoConsulting/mokocli.git" \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/MokoConsulting/moko-platform.git" \
|
||||
/tmp/mokostandards
|
||||
cd /tmp/mokostandards
|
||||
composer install --no-dev --no-interaction --quiet
|
||||
@@ -22,7 +22,6 @@ permissions:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22]
|
||||
@@ -19,7 +19,6 @@ permissions:
|
||||
jobs:
|
||||
check-sdk:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Notifications
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/notify.yml
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Notifications
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
||||
# PATH: /.gitea/workflows/notify.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
||||
|
||||
@@ -26,7 +26,7 @@ permissions:
|
||||
|
||||
env:
|
||||
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
|
||||
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'git-releases' }}
|
||||
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-releases' }}
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
@@ -3,9 +3,9 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.CI
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||
# PATH: /templates/workflows/universal/pr-check.yml.template
|
||||
# VERSION: 09.23.00
|
||||
# BRIEF: PR gate — branch policy + code validation before merge
|
||||
@@ -147,12 +147,11 @@ jobs:
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
# Platform comes from the MokoGIT metadata API (public GET).
|
||||
# Platform comes from the MokoGitea metadata API (public GET).
|
||||
API="${GITHUB_SERVER_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${GITHUB_REPOSITORY}/metadata"
|
||||
PLATFORM="$(curl -sf "$API" 2>/dev/null | python3 -c "import sys, json; print(json.load(sys.stdin).get('platform') or '')" 2>/dev/null || true)"
|
||||
[ -z "$PLATFORM" ] && PLATFORM="generic"
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
echo "Detected platform: $PLATFORM"
|
||||
|
||||
- name: Setup PHP
|
||||
if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr'
|
||||
@@ -283,10 +282,10 @@ jobs:
|
||||
for ELEMENT in name version description; do
|
||||
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
|
||||
done
|
||||
# Block legacy raw/branch update server URLs on MokoGIT
|
||||
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogit\|git\.mokoconsulting\.tech' || true)
|
||||
# Block legacy raw/branch update server URLs on MokoGitea
|
||||
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true)
|
||||
if [ -n "$RAW_URLS" ]; then
|
||||
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGIT. Use the MokoGIT Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
|
||||
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
|
||||
echo "$RAW_URLS"
|
||||
exit 1
|
||||
fi
|
||||
@@ -497,12 +496,12 @@ jobs:
|
||||
steps:
|
||||
- name: Trigger RC pre-release
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ github.head_ref }}
|
||||
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
run: |
|
||||
curl -s -X POST "${MOKOGIT_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${MOKOGIT_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
|
||||
curl -s -X POST "${MOKOGITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${MOKOGITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
|
||||
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -513,7 +512,7 @@ jobs:
|
||||
if: >-
|
||||
always() &&
|
||||
needs.validate.result == 'failure'
|
||||
uses: ./.mokogit/workflows/ci-issue-reporter.yml
|
||||
uses: ./.mokogitea/workflows/ci-issue-reporter.yml
|
||||
with:
|
||||
gate: "PR Validation"
|
||||
workflow: "PR Check"
|
||||
@@ -3,8 +3,8 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Release
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.02.00
|
||||
@@ -40,9 +40,9 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GIT_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GIT_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
ref: ${{ github.ref_name }}
|
||||
submodules: recursive
|
||||
|
||||
@@ -65,9 +65,9 @@ jobs:
|
||||
run: |
|
||||
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:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
run: |
|
||||
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
|
||||
@@ -152,9 +152,9 @@ jobs:
|
||||
fi
|
||||
|
||||
# Commit version bump
|
||||
git config --local user.email "mokogit-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "mokogit-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||
@@ -164,12 +164,12 @@ jobs:
|
||||
# Auto-detect element via manifest_element.php
|
||||
php ${MOKO_CLI}/manifest_element.php \
|
||||
--path . --version "$VERSION" --stability "$STABILITY" \
|
||||
--repo "${GIT_REPO}" --github-output
|
||||
--repo "${GITEA_REPO}" --github-output
|
||||
|
||||
# Read back element outputs
|
||||
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GIT_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
@@ -187,18 +187,18 @@ jobs:
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_create.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GIT_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
||||
|
||||
- name: Update release notes from CHANGELOG.md
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
@@ -209,7 +209,7 @@ jobs:
|
||||
fi
|
||||
|
||||
# Update release body via API
|
||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGIT_TOKEN }}" \
|
||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$RELEASE_ID" ]; then
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
'${API_BASE}/releases/${RELEASE_ID}',
|
||||
data=payload, method='PATCH',
|
||||
headers={
|
||||
'Authorization': 'token ${{ secrets.MOKOGIT_TOKEN }}',
|
||||
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
urllib.request.urlopen(req)
|
||||
@@ -235,21 +235,21 @@ jobs:
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_package.php \
|
||||
--path . --version "$VERSION" --tag "$TAG" \
|
||||
--token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GIT_REPO}" --output /tmp || true
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
--repo "${GITEA_REPO}" --output /tmp || true
|
||||
|
||||
# updates.xml is generated dynamically by MokoGIT license server
|
||||
# updates.xml is generated dynamically by MokoGitea license server
|
||||
# No need to build, commit, or sync updates.xml from workflows
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
php ${MOKO_CLI}/release_cascade.php \
|
||||
--stability "${{ steps.meta.outputs.stability }}" \
|
||||
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Universal
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/rc-revert.yml
|
||||
# PATH: /.mokogitea/workflows/rc-revert.yml
|
||||
# VERSION: 09.23.00
|
||||
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
|
||||
|
||||
@@ -32,8 +32,8 @@ jobs:
|
||||
env:
|
||||
BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
REPO: ${{ github.repository }}
|
||||
GIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# BRANCH is attacker-controlled (PR head ref). Strict allowlist before ANY use.
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
fi
|
||||
SUFFIX="${BRANCH#rc/}"
|
||||
DEV_BRANCH="dev/${SUFFIX}"
|
||||
API="${GIT_URL}/api/v1/repos/${REPO}/branches"
|
||||
API="${GITEA_URL}/api/v1/repos/${REPO}/branches"
|
||||
|
||||
# Create dev/ branch from rc/ branch
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
|
||||
@@ -6,9 +6,9 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Validation
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Validation
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||
# PATH: /templates/workflows/joomla/repo_health.yml.template
|
||||
# VERSION: 09.23.00
|
||||
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
|
||||
@@ -45,7 +45,7 @@ env:
|
||||
SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate
|
||||
|
||||
# Repo health policy
|
||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogit/workflows/
|
||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/
|
||||
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
|
||||
REPO_DISALLOWED_DIRS:
|
||||
REPO_DISALLOWED_FILES: TODO.md,todo.md
|
||||
@@ -56,7 +56,7 @@ env:
|
||||
# File / directory variables
|
||||
DOCS_INDEX: docs/docs-index.md
|
||||
SCRIPT_DIR: scripts
|
||||
WORKFLOWS_DIR: .mokogit/workflows
|
||||
WORKFLOWS_DIR: .mokogitea/workflows
|
||||
SHELLCHECK_PATTERN: '*.sh'
|
||||
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- name: Check actor permission (admin only)
|
||||
id: perm
|
||||
env:
|
||||
TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
REPO: ${{ github.repository }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
run: |
|
||||
@@ -88,14 +88,14 @@ jobs:
|
||||
|
||||
# Hardcoded authorized users — always allowed
|
||||
case "$ACTOR" in
|
||||
jmiller|mokogit-actions[bot])
|
||||
jmiller|gitea-actions[bot])
|
||||
ALLOWED=true
|
||||
PERMISSION=admin
|
||||
METHOD="hardcoded allowlist"
|
||||
;;
|
||||
*)
|
||||
# Detect platform and check permissions via API
|
||||
API_BASE="${GITHUB_API_URL:-${GIT_API_URL:-https://api.github.com}}"
|
||||
API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}"
|
||||
RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}')
|
||||
PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown")
|
||||
@@ -605,7 +605,7 @@ jobs:
|
||||
printf '%s\n' '| Domain | Status | Notes |'
|
||||
printf '%s\n' '|---|---|---|'
|
||||
printf '%s\n' '| Access control | OK | Admin-only execution gate |'
|
||||
printf '%s\n' '| Release policy | N/A | Releases handled by MokoGIT |'
|
||||
printf '%s\n' '| Release policy | N/A | Releases handled by MokoGitea |'
|
||||
printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |'
|
||||
printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |'
|
||||
printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |'
|
||||
@@ -677,7 +677,7 @@ jobs:
|
||||
if: >-
|
||||
always() &&
|
||||
needs.scripts_governance.result == 'failure'
|
||||
uses: ./.mokogit/workflows/ci-issue-reporter.yml
|
||||
uses: ./.mokogitea/workflows/ci-issue-reporter.yml
|
||||
with:
|
||||
gate: "Scripts Governance"
|
||||
workflow: "Repo Health"
|
||||
@@ -691,7 +691,7 @@ jobs:
|
||||
if: >-
|
||||
always() &&
|
||||
needs.repo_health.result == 'failure'
|
||||
uses: ./.mokogit/workflows/ci-issue-reporter.yml
|
||||
uses: ./.mokogitea/workflows/ci-issue-reporter.yml
|
||||
with:
|
||||
gate: "Repository Health"
|
||||
workflow: "Repo Health"
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
# PATH: /templates/workflows/shared/repository-cleanup.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Recurring repository maintenance — labels, branches, workflows, logs, doc indexes
|
||||
# NOTE: Synced via bulk-repo-sync to .mokogit/workflows/repository-cleanup.yml in all governed repos.
|
||||
# NOTE: Synced via bulk-repo-sync to .mokogitea/workflows/repository-cleanup.yml in all governed repos.
|
||||
# Runs on the 1st and 15th of each month at 6:00 AM UTC, and on manual dispatch.
|
||||
|
||||
name: "Universal: Repository Cleanup"
|
||||
+60
-59
@@ -2,14 +2,14 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoCLI.Compliance
|
||||
# REPO: https://github.com/mokoconsulting-tech/mokocli
|
||||
# PATH: /.mokogit/workflows/standards-compliance.yml
|
||||
# INGROUP: MokoStandards.Compliance
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.mokogitea/workflows/standards-compliance.yml
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: MokoCLI compliance validation workflow
|
||||
# BRIEF: MokoStandards compliance validation workflow
|
||||
# NOTE: Validates repository structure, documentation, and coding standards
|
||||
|
||||
name: "Generic: Standards Compliance"
|
||||
name: "MCP: Standards Compliance"
|
||||
|
||||
# ╔════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ MOKOSTANDARDS COMPLIANCE WORKFLOW ║
|
||||
@@ -42,7 +42,7 @@ env:
|
||||
WORKFLOW_VERSION: "04.04.01"
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
# MokoCLI Policy Compliance:
|
||||
# MokoStandards Policy Compliance:
|
||||
# - File formatting: Enforces organizational coding standards
|
||||
# - Reference: docs/policy/file-formatting.md
|
||||
|
||||
@@ -252,7 +252,7 @@ jobs:
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### ❌ Validation Failed: LICENSE File Missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** LICENSE file is required for all MokoCLI-compliant repositories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** LICENSE file is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Action Required:** Add LICENSE file with appropriate open-source license (GPL-3.0-or-later recommended)" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
echo "❌ ERROR: LICENSE file not found - This is a critical requirement"
|
||||
@@ -291,7 +291,7 @@ jobs:
|
||||
|
||||
MISSING=0
|
||||
PRESENT=0
|
||||
TOTAL=1
|
||||
TOTAL=2
|
||||
|
||||
echo "### Required Directories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -299,7 +299,7 @@ jobs:
|
||||
echo "|-----------|--------|-------|------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check required directories
|
||||
for dir in .mokogit; do
|
||||
for dir in docs .github; do
|
||||
if [ -d "$dir" ]; then
|
||||
FILE_COUNT=$(find "$dir" -type f 2>/dev/null | wc -l)
|
||||
DIR_SIZE=$(du -sh "$dir" 2>/dev/null | cut -f1)
|
||||
@@ -320,13 +320,14 @@ jobs:
|
||||
echo "### 🔴 Critical Issues: $MISSING" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Remediation Steps:**" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -d ".mokogit" ] && echo "- Create .mokogit directory: \`mkdir -p .mokogit/workflows\`" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -d "docs" ] && echo "- Create docs directory: \`mkdir docs && echo '# Documentation' > docs/README.md\`" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -d ".github" ] && echo "- Create .github directory: \`mkdir -p .github/workflows\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📚 Reference: [MokoCLI Repository Structure](https://github.com/mokoconsulting-tech/mokocli/tree/main/docs/policy/core-structure.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📚 Reference: [MokoStandards Repository Structure](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/core-structure.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### ❌ Validation Failed: Required Directories Missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Status:** Repository structure does not meet MokoCLI requirements" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Status:** Repository structure does not meet MokoStandards requirements" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Missing:** $MISSING required director(y|ies)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Compliance:** $PERCENT% ($PRESENT/$TOTAL directories present)" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
@@ -383,17 +384,17 @@ jobs:
|
||||
echo "### 🔴 Critical Issues: $MISSING" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Remediation Steps:**" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "README.md" ] && echo "- Create README.md: Use [template](https://github.com/mokoconsulting-tech/mokocli/tree/main/templates/docs/required/README.md)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "README.md" ] && echo "- Create README.md: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/docs/required/README.md)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "LICENSE" ] && echo "- Add LICENSE file: Choose from [OSI-approved licenses](https://opensource.org/licenses)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "CONTRIBUTING.md" ] && echo "- Create CONTRIBUTING.md: Use [template](https://github.com/mokoconsulting-tech/mokocli/tree/main/templates/docs/required/CONTRIBUTING.md)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "SECURITY.md" ] && echo "- Create SECURITY.md: Use [template](https://github.com/mokoconsulting-tech/mokocli/tree/main/templates/docs/required/SECURITY.md)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f ".editorconfig" ] && echo "- Add .editorconfig: Use [template](https://github.com/mokoconsulting-tech/mokocli/tree/main/templates/.editorconfig)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "CONTRIBUTING.md" ] && echo "- Create CONTRIBUTING.md: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/docs/required/CONTRIBUTING.md)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f "SECURITY.md" ] && echo "- Create SECURITY.md: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/docs/required/SECURITY.md)" >> $GITHUB_STEP_SUMMARY
|
||||
[ ! -f ".editorconfig" ] && echo "- Add .editorconfig: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/.editorconfig)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📚 Reference: [MokoCLI File Requirements](https://github.com/mokoconsulting-tech/mokocli/tree/main/docs/policy/file-header-standards.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📚 Reference: [MokoStandards File Requirements](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/file-header-standards.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### ❌ Validation Failed: Required Files Missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Status:** Repository files do not meet MokoCLI requirements" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Status:** Repository files do not meet MokoStandards requirements" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Missing:** $MISSING required file(s)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Compliance:** $PERCENT% ($PRESENT/$TOTAL files present)" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
@@ -436,7 +437,7 @@ jobs:
|
||||
echo "$TABS_IN_SPACES_FILES" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "These languages require spaces (tabs will break): YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST" >> $GITHUB_STEP_SUMMARY
|
||||
echo "All other files (including .md, .ps1, LICENSE, etc.) may use tabs per MokoCLI policy" >> $GITHUB_STEP_SUMMARY
|
||||
echo "All other files (including .md, .ps1, LICENSE, etc.) may use tabs per MokoStandards policy" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "✅ No tabs found in files requiring spaces" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Note: Tabs are allowed in most files (policy default). Only checked files requiring spaces." >> $GITHUB_STEP_SUMMARY
|
||||
@@ -453,7 +454,7 @@ jobs:
|
||||
! -path "./vendor/*" \
|
||||
! -path "./node_modules/*" \
|
||||
! -path "./.git/*" \
|
||||
-exec file {} \; | grep -v "UTF-8" | grep -v "ASCII" | head -5 || true)
|
||||
-exec file {} \; | grep -v "UTF-8" | grep -v "ASCII" | head -5)
|
||||
|
||||
if [ -n "$NON_UTF8" ]; then
|
||||
echo "⚠️ Non-UTF-8 files detected:" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -475,14 +476,14 @@ jobs:
|
||||
! -path "./vendor/*" \
|
||||
! -path "./node_modules/*" \
|
||||
! -path "./.git/*" \
|
||||
-exec file {} \; | grep "CRLF" | head -5 || true)
|
||||
-exec file {} \; | grep "CRLF" | head -5)
|
||||
|
||||
if [ -n "$CRLF_FILES" ]; then
|
||||
echo "⚠️ Files with CRLF line endings found:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "$CRLF_FILES" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "MokoCLI requires LF line endings" >> $GITHUB_STEP_SUMMARY
|
||||
echo "MokoStandards requires LF line endings" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "✅ Line endings are consistent (LF)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -503,13 +504,13 @@ jobs:
|
||||
tools: composer
|
||||
coverage: none
|
||||
|
||||
- name: Setup MokoCLI tools
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/mokocli.git" \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards" ] && [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards
|
||||
@@ -523,7 +524,7 @@ jobs:
|
||||
echo "## 🔢 Version Consistency Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Use MokoCLI tools (no Composer needed on the governed repo)
|
||||
# Use MokoStandards tools (no Composer needed on the governed repo)
|
||||
if [ -f "/tmp/mokostandards/api/validate/check_version_consistency.php" ]; then
|
||||
php /tmp/mokostandards/api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
@@ -531,7 +532,7 @@ jobs:
|
||||
php api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
else
|
||||
echo "⏭️ MokoCLI tools not available — skipping version check" >> $GITHUB_STEP_SUMMARY
|
||||
echo "⏭️ MokoStandards tools not available — skipping version check" >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -563,17 +564,17 @@ jobs:
|
||||
set -x
|
||||
echo "### GitHub Actions Workflows" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
WORKFLOWS_DIR=".mokogit/workflows"
|
||||
WORKFLOWS_DIR=".github/workflows"
|
||||
|
||||
if [ ! -d "$WORKFLOWS_DIR" ]; then
|
||||
echo "❌ No workflows directory found" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### ❌ Validation Failed: Workflows Directory Missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** .mokogit/workflows directory is required for CI/CD automation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Action Required:** Create .mokogit/workflows directory and add GitHub Actions workflows" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** .github/workflows directory is required for CI/CD automation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Action Required:** Create .github/workflows directory and add GitHub Actions workflows" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
echo "❌ ERROR: .mokogit/workflows directory not found"
|
||||
echo "❌ ERROR: .github/workflows directory not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -596,12 +597,12 @@ jobs:
|
||||
echo "⚠️ CodeQL workflow not found" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check for MokoCLI-synced workflows
|
||||
# Check for MokoStandards-synced workflows
|
||||
for wf in deploy-dev.yml deploy-demo.yml deploy-rs.yml sync-version-on-merge.yml auto-release.yml standards-compliance.yml enterprise-firewall-setup.yml; do
|
||||
if [ -f "$WORKFLOWS_DIR/$wf" ]; then
|
||||
echo "✅ ${wf}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ ${wf} not found (synced from MokoCLI)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "⚠️ ${wf} not found (synced from MokoStandards)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -612,7 +613,7 @@ jobs:
|
||||
echo "### Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
INVALID=0
|
||||
for workflow in $(find .mokogit/workflows -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null); do
|
||||
for workflow in $(find .github/workflows -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null); do
|
||||
if [ -f "$workflow" ]; then
|
||||
if python3 -c "import yaml, sys; yaml.safe_load(open(sys.argv[1]))" "$workflow" 2>/dev/null; then
|
||||
echo "✅ $(basename $workflow)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -629,7 +630,7 @@ jobs:
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** $INVALID workflow file(s) have invalid YAML syntax" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Action Required:** Fix YAML syntax errors in the marked workflow files" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tool:** Run \`python3 -c \"import yaml; yaml.safe_load(open('.mokogit/workflows/FILE.yml'))\"\` locally" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Tool:** Run \`python3 -c \"import yaml; yaml.safe_load(open('.github/workflows/FILE.yml'))\"\` locally" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
echo "❌ ERROR: $INVALID workflow file(s) with invalid YAML syntax"
|
||||
exit 1
|
||||
@@ -641,7 +642,7 @@ jobs:
|
||||
echo "✅ SUCCESS: All workflow files passed YAML validation"
|
||||
|
||||
- name: Validate CodeQL Configuration
|
||||
if: hashFiles('.mokogit/workflows/codeql-analysis.yml') != ''
|
||||
if: hashFiles('.github/workflows/codeql-analysis.yml') != ''
|
||||
run: |
|
||||
set -e
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -649,7 +650,7 @@ jobs:
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Inline validation (rewritten from Python to bash for PHP-only architecture)
|
||||
CODEQL_FILE=".mokogit/workflows/codeql-analysis.yml"
|
||||
CODEQL_FILE=".github/workflows/codeql-analysis.yml"
|
||||
|
||||
if [ ! -f "$CODEQL_FILE" ]; then
|
||||
echo "⚠️ CodeQL workflow file not found" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -808,7 +809,7 @@ jobs:
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### ❌ Validation Failed: README.md Missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** README.md is required for all MokoCLI-compliant repositories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** README.md is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Action Required:** Create README.md with project description, setup instructions, and usage examples" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
echo "❌ ERROR: README.md not found - This is a critical requirement"
|
||||
@@ -914,7 +915,7 @@ jobs:
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### ❌ Validation Failed: CHANGELOG.md Missing" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** CHANGELOG.md is required for all MokoCLI-compliant repositories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Error:** CHANGELOG.md is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Action Required:** Create CHANGELOG.md following [Keep a Changelog](https://keepachangelog.com/) format" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
echo "❌ ERROR: CHANGELOG.md not found - This is a critical requirement"
|
||||
@@ -1182,11 +1183,11 @@ jobs:
|
||||
# Install yamllint if not present
|
||||
:
|
||||
else
|
||||
pip install yamllint >/dev/null 2>&1 || true
|
||||
pip install yamllint >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Run yamllint and count line-length warnings
|
||||
YAML_OUTPUT=$(yamllint .mokogit/workflows/*.yml 2>&1 | grep "line too long" || true)
|
||||
YAML_OUTPUT=$(yamllint .github/workflows/*.yml 2>&1 | grep "line too long" || true)
|
||||
if [ -n "$YAML_OUTPUT" ]; then
|
||||
YAML_VIOLATIONS=$(echo "$YAML_OUTPUT" | wc -l)
|
||||
echo "⚠️ Found $YAML_VIOLATIONS lines exceeding 180 characters in YAML files" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -1213,7 +1214,7 @@ jobs:
|
||||
! -path "./.git/*" \
|
||||
! -path "./build/*" \
|
||||
! -path "./dist/*" \
|
||||
-exec awk 'length > 120 { print FILENAME ":" NR ": " length " chars" }' {} \; 2>/dev/null | head -20 || true)
|
||||
-exec awk 'length > 120 { print FILENAME ":" NR ": " length " chars" }' {} \; 2>/dev/null | head -20)
|
||||
|
||||
if [ -n "$LONG_LINES" ]; then
|
||||
LINE_COUNT=$(echo "$LONG_LINES" | wc -l)
|
||||
@@ -1975,10 +1976,10 @@ jobs:
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
||||
else
|
||||
echo "No composer.json — pulling MokoCLI tools"
|
||||
echo "No composer.json — pulling MokoStandards tools"
|
||||
if [ ! -d "/tmp/mokostandards" ]; then
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/mokocli.git" \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
@@ -2047,10 +2048,10 @@ jobs:
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
||||
else
|
||||
echo "No composer.json — pulling MokoCLI tools"
|
||||
echo "No composer.json — pulling MokoStandards tools"
|
||||
if [ ! -d "/tmp/mokostandards" ]; then
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/mokocli.git" \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
@@ -2130,17 +2131,17 @@ jobs:
|
||||
WARNINGS=0
|
||||
ERRORS=0
|
||||
|
||||
# 1. Check .mokogit/config.tf location (not root override files)
|
||||
# 1. Check .github/config.tf location (not root override files)
|
||||
echo "### Override Configuration Check" >> $GITHUB_STEP_SUMMARY
|
||||
LEGACY_OVERRIDES=$(find . -maxdepth 1 -name "*override*.tf" -o -name "MokoCLI.override.tf" 2>/dev/null | wc -l || echo 0)
|
||||
LEGACY_OVERRIDES=$(find . -maxdepth 1 -name "*override*.tf" -o -name "MokoStandards.override.tf" 2>/dev/null | wc -l || echo 0)
|
||||
if [ "$LEGACY_OVERRIDES" -gt 0 ]; then
|
||||
echo "⚠️ Found legacy override files in root directory" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Expected Location**: .mokogit/config.tf" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Expected Location**: .github/config.tf" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Legacy files found**: $LEGACY_OVERRIDES" >> $GITHUB_STEP_SUMMARY
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
if [ -f ".mokogit/config.tf" ]; then
|
||||
echo "✅ Override configuration in correct location (.mokogit/config.tf)" >> $GITHUB_STEP_SUMMARY
|
||||
if [ -f ".github/config.tf" ]; then
|
||||
echo "✅ Override configuration in correct location (.github/config.tf)" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "ℹ️ No override configuration found" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -2298,7 +2299,7 @@ jobs:
|
||||
- name: Generate Compliance Report
|
||||
run: |
|
||||
set -x
|
||||
echo "# 📊 MokoCLI Compliance Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "# 📊 MokoStandards Compliance Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Calculate overall status
|
||||
@@ -2485,7 +2486,7 @@ jobs:
|
||||
else
|
||||
echo "## 🎉 Excellent!" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Your repository is **fully compliant** with MokoCLI!" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Your repository is **fully compliant** with MokoStandards!" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Achievements:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- ✅ All required directories and files present" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -2500,12 +2501,12 @@ jobs:
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📚 **Resources:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [MokoCLI Documentation](https://github.com/mokoconsulting-tech/mokocli)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [Repository Structure Guide](https://github.com/mokoconsulting-tech/mokocli/tree/main/docs/policy/core-structure.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [Documentation Standards](https://github.com/mokoconsulting-tech/mokocli/tree/main/docs/policy/document-formatting.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [Coding Standards](https://github.com/mokoconsulting-tech/mokocli/tree/main/docs/policy/coding-style-guide.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [MokoStandards Documentation](https://github.com/mokoconsulting-tech/MokoStandards)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [Repository Structure Guide](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/core-structure.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [Documentation Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/document-formatting.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [Coding Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/coding-style-guide.md)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "_Generated by MokoCLI Compliance Workflow v${WORKFLOW_VERSION}_" >> $GITHUB_STEP_SUMMARY
|
||||
echo "_Generated by MokoStandards Compliance Workflow v${WORKFLOW_VERSION}_" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Create tracking issue for non-compliance if on push
|
||||
if [ "$COMPLIANCE_PERCENT" -lt 100 ] && [ "${{ github.event_name }}" = "push" ]; then
|
||||
@@ -2529,9 +2530,9 @@ jobs:
|
||||
echo "### ✅ Full Standards Compliance Achieved" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Overall Compliance:** 100%" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Status:** Repository meets all MokoCLI requirements" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Status:** Repository meets all MokoStandards requirements" >> $GITHUB_STEP_SUMMARY
|
||||
echo ""
|
||||
echo "✅ SUCCESS: Repository is fully MokoCLI compliant"
|
||||
echo "✅ SUCCESS: Repository is fully MokoStandards compliant"
|
||||
|
||||
- name: Create or reopen tracking issue for standards violations
|
||||
if: failure()
|
||||
+15
-12
@@ -11,7 +11,7 @@
|
||||
# PATH: /templates/workflows/shared/sync-version-on-merge.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Auto-bump patch version on every push to main and propagate to all file headers
|
||||
# NOTE: Synced via bulk-repo-sync to .mokogit/workflows/sync-version-on-merge.yml in all governed repos.
|
||||
# NOTE: Synced via bulk-repo-sync to .mokogitea/workflows/sync-version-on-merge.yml in all governed repos.
|
||||
# README.md is the single source of truth for the repository version.
|
||||
|
||||
name: "Universal: Sync Version on Merge"
|
||||
@@ -47,19 +47,22 @@ jobs:
|
||||
token: ${{ secrets.GH_TOKEN || github.token }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@fcafdd6392932010c2bd5094439b8e33be2a8a09 # v2.37.0
|
||||
with:
|
||||
php-version: '8.1'
|
||||
tools: composer
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
# Use pre-installed mokoplatform on runner host (symlink /opt/mokoplatform -> /opt/moko-platform)
|
||||
if [ -d /opt/mokoplatform/api/cli ] && [ -f /opt/mokoplatform/vendor/autoload.php ]; then
|
||||
ln -sf /opt/mokoplatform /tmp/mokostandards
|
||||
echo "Using pre-installed /opt/mokoplatform"
|
||||
elif [ -d /opt/moko-platform/api/cli ]; then
|
||||
ln -sf /opt/moko-platform /tmp/mokostandards
|
||||
echo "Using pre-installed /opt/moko-platform"
|
||||
else
|
||||
echo "::warning::MokoStandards tools not found on runner - skipping"
|
||||
echo "MOKO_SKIP=true" >> "$GITHUB_ENV"
|
||||
fi
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards
|
||||
cd /tmp/mokostandards
|
||||
composer install --no-dev --no-interaction --quiet
|
||||
|
||||
- name: Auto-bump patch version
|
||||
if: ${{ github.event_name == 'push' && github.actor != 'github-actions[bot]' }}
|
||||
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow.Template
|
||||
# INGROUP: MokoCLI.CI
|
||||
# DEFGROUP: Gitea.Workflow.Template
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
|
||||
# PATH: /.mokogit/workflows/version-set.yml
|
||||
# PATH: /.mokogitea/workflows/version-set.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Set or reset the extension version across all version-bearing files
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.MOKOGIT_TOKEN || github.token }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
ref: ${{ inputs.branch || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
+12
-14
@@ -3,10 +3,10 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: MokoGIT.Workflow
|
||||
# INGROUP: MokoCLI.Universal
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogit/workflows/workflow-sync-trigger.yml
|
||||
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
|
||||
# VERSION: 01.01.00
|
||||
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
|
||||
|
||||
@@ -27,10 +27,9 @@ jobs:
|
||||
name: Sync workflows to live repos
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
startsWith(github.event.repository.name, 'Template-') &&
|
||||
(github.event_name == 'workflow_dispatch' ||
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true &&
|
||||
!contains(github.event.pull_request.title, '[skip sync]')))
|
||||
!contains(github.event.pull_request.title, '[skip sync]'))
|
||||
|
||||
steps:
|
||||
- name: Determine platform from repo name
|
||||
@@ -41,19 +40,19 @@ jobs:
|
||||
Template-Joomla) PLATFORM="joomla" ;;
|
||||
Template-Dolibarr) PLATFORM="dolibarr" ;;
|
||||
Template-Go) PLATFORM="go" ;;
|
||||
Template-NPM) PLATFORM="npm" ;;
|
||||
Template-MCP) PLATFORM="mcp" ;;
|
||||
Template-Generic) PLATFORM="" ;;
|
||||
*) PLATFORM="" ;;
|
||||
esac
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
echo "Platform: ${PLATFORM:-all}"
|
||||
|
||||
- name: Clone MokoCLI
|
||||
- name: Clone mokocli
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
MOKOGIT_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 "${MOKOGIT_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 "${MOKOGITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
||||
|
||||
- name: Install PHP
|
||||
run: |
|
||||
@@ -68,12 +67,11 @@ jobs:
|
||||
|
||||
- name: Run workflow sync
|
||||
env:
|
||||
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
ARGS="--token ${MOKOGIT_TOKEN}"
|
||||
ARGS="--token ${MOKOGITEA_TOKEN}"
|
||||
ARGS="${ARGS} --org ${{ vars.GITEA_ORG || github.repository_owner }}"
|
||||
ARGS="${ARGS} --phase repos"
|
||||
ARGS="${ARGS} --delete-orphans"
|
||||
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
if [ -n "$PLATFORM" ]; then
|
||||
+2
-9
@@ -3,16 +3,9 @@
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
- Branding: Git -> MokoGIT in workflow DEFGROUP tags, committer identity (git-actions[bot] -> mokogit-actions[bot]), and platform prose. Functional tokens (GIT_TOKEN/GITHUB_TOKEN, GIT_ACTIONS_*, vars.GITEA_*, git.mokoconsulting.tech) preserved.
|
||||
|
||||
### Removed
|
||||
- CI: stripped GitHub-only workflows (DEFGROUP GitHub.Workflow), which do not run on MokoGIT Actions: auto-dev-issue.yml changelog-validation.yml standards-compliance.yml sync-version-on-merge.yml.
|
||||
|
||||
### Changed
|
||||
- Migrated all workflow and template paths from `.github/` to `.mokogit/`
|
||||
- Template source paths updated: `templates/git/` to `templates/mokogit/`
|
||||
- Migrated all workflow and template paths from `.github/` to `.mokogitea/`
|
||||
- Template source paths updated: `templates/gitea/` to `templates/mokogitea/`
|
||||
- HCL definition files removed -- Template repos are now the canonical source
|
||||
|
||||
### Added
|
||||
- CI: gate build/CI/release/version workflows so they skip template repos; smart-gate the sync workflows to run only on Template-* repos; normalize mokocli/MokoCLI refs and drop the stale mokoconsulting-tech org slug.
|
||||
- `branch-cleanup.yml`: auto-delete merged feature branches after PR merge
|
||||
|
||||
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code when working with this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Template-NPM** -- Template repository for creating MokoCLI-compliant MCP API servers
|
||||
**Template-MCP** -- Template repository for creating MokoStandards-compliant MCP API servers
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
@@ -12,8 +12,8 @@ This file provides guidance to Claude Code when working with this repository.
|
||||
| **Language** | TypeScript |
|
||||
| **Default branch** | main |
|
||||
| **License** | GPL-3.0-or-later |
|
||||
| **Wiki** | [Template-NPM Wiki](https://git.mokoconsulting.tech/MokoConsulting/Template-NPM/wiki) |
|
||||
| **Standards** | [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home) |
|
||||
| **Wiki** | [Template-MCP Wiki](https://git.mokoconsulting.tech/MokoConsulting/Template-MCP/wiki) |
|
||||
| **Standards** | [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) |
|
||||
|
||||
## Common Commands
|
||||
|
||||
@@ -42,11 +42,11 @@ This is an MCP (Model Context Protocol) server. Key files:
|
||||
|
||||
## Rules
|
||||
|
||||
- **Workflow directory**: `.mokogit/` (not `.gitea/` or `.github/`)
|
||||
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
|
||||
|
||||
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, or `*.min.css`/`*.min.js`
|
||||
- **Attribution**: use `Authored-by: Moko Consulting` in commits
|
||||
- **Branch strategy**: develop on `dev`, merge to `main` for release
|
||||
- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper for Joomla templates)
|
||||
- **Wiki**: documentation lives in the MokoGIT wiki, not in `docs/` files
|
||||
- **Standards**: this repo follows [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home)
|
||||
- **Wiki**: documentation lives in the Gitea wiki, not in `docs/` files
|
||||
- **Standards**: this repo follows [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
# FILE INFORMATION
|
||||
DEFGROUP:
|
||||
INGROUP: Project.Documentation
|
||||
REPO: MokoConsulting/Template-NPM
|
||||
REPO: mokoconsulting-tech/MokoStandards-Template-Generic
|
||||
VERSION: 00.00.01
|
||||
PATH: ./CODE_OF_CONDUCT.md
|
||||
BRIEF: Contributor Covenant Code of Conduct version 1.3.0
|
||||
|
||||
+5
-5
@@ -13,13 +13,13 @@
|
||||
# FILE INFORMATION
|
||||
DEFGROUP:
|
||||
INGROUP: Project.Documentation
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-NPM
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-Template-Generic
|
||||
VERSION: 00.00.01
|
||||
PATH: ./CONTRIBUTING.md
|
||||
BRIEF: Contribution guidelines for the project
|
||||
-->
|
||||
|
||||
# Contributing to Template-NPM
|
||||
# Contributing to MokoStandards-Template-Generic
|
||||
|
||||
We appreciate your interest in contributing to this project! This document provides guidelines for contributing.
|
||||
|
||||
@@ -148,13 +148,13 @@ Every repo maintains 5 standard release channel tags:
|
||||
|
||||
### CI/CD
|
||||
|
||||
- MokoGIT Actions runs all CI workflows
|
||||
- Gitea Actions runs all CI workflows
|
||||
- GitHub Actions are disabled on mirrored repos
|
||||
- Workflows live in both `.github/workflows/` and `.mokogit/workflows/`
|
||||
- Workflows live in both `.github/workflows/` and `.gitea/workflows/`
|
||||
|
||||
### Update Servers (Joomla)
|
||||
|
||||
In manifest `<updateservers>`, MokoGIT must be priority 1, GitHub priority 2.
|
||||
In manifest `<updateservers>`, Gitea must be priority 1, GitHub priority 2.
|
||||
|
||||
### Secrets
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
You should have received a copy of the GNU General Public License (./LICENSE).
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoCLI
|
||||
INGROUP: MokoCLI.Documentation
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli/
|
||||
DEFGROUP: MokoStandards
|
||||
INGROUP: MokoStandards.Documentation
|
||||
REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards/
|
||||
VERSION: 05.00.00
|
||||
PATH: ./LICENSE
|
||||
BRIEF: Project license (GPL-3.0-or-later)
|
||||
|
||||
@@ -23,7 +23,7 @@ install-deps: ## Install dependencies
|
||||
@$(NPM) install
|
||||
@echo "$(COLOR_GREEN)✓ Dependencies installed$(COLOR_RESET)"
|
||||
|
||||
MOKO_PLATFORM ?= $(or $(wildcard ../mokocli),$(wildcard $(HOME)/mokocli),$(wildcard /opt/mokocli))
|
||||
MOKO_PLATFORM ?= $(or $(wildcard ../moko-platform),$(wildcard $(HOME)/moko-platform),$(wildcard /opt/moko-platform))
|
||||
MINIFY_SCRIPT := $(MOKO_PLATFORM)/build/minify.js
|
||||
|
||||
.PHONY: minify
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Template-NPM
|
||||
# Template-MCP
|
||||
|
||||
   
|
||||
   
|
||||
|
||||
Template repository for creating MokoCLI-compliant Model Context Protocol (MCP) API servers. Provides the scaffolding, configuration patterns, and architecture conventions used by all Moko Consulting MCP servers, enabling AI assistants like Claude to interact with REST APIs through a standardized tool interface.
|
||||
Template repository for creating MokoStandards-compliant Model Context Protocol (MCP) API servers. Provides the scaffolding, configuration patterns, and architecture conventions used by all Moko Consulting MCP servers, enabling AI assistants like Claude to interact with REST APIs through a standardized tool interface.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -11,7 +11,7 @@ Template repository for creating MokoCLI-compliant Model Context Protocol (MCP)
|
||||
- **REST API bridge architecture** -- standardized pattern for bridging any REST API as MCP tools, with typed request/response handling
|
||||
- **TypeScript-first** -- full TypeScript setup with `tsconfig.json`, strict mode, and ES module output
|
||||
- **Build tooling** -- Makefile with `build`, `dev`, `clean`, and `lint` targets; npm scripts for compilation and development
|
||||
- **MokoCLI compliant** -- follows all Moko Consulting governance conventions: file headers, commit messages, `.gitattributes`, `.gitmessage`, PR/issue templates
|
||||
- **MokoStandards compliant** -- follows all Moko Consulting governance conventions: file headers, commit messages, `.gitattributes`, `.gitmessage`, PR/issue templates
|
||||
- **Example configuration** -- `config.example.json` demonstrates the connection configuration schema
|
||||
- **Documentation templates** -- wiki pages for API reference, architecture overview, and installation pre-generated
|
||||
|
||||
@@ -19,7 +19,7 @@ Template repository for creating MokoCLI-compliant Model Context Protocol (MCP)
|
||||
|
||||
1. Create a new repository from this template (or clone directly):
|
||||
```bash
|
||||
git clone https://git.mokoconsulting.tech/MokoConsulting/Template-NPM.git my-mcp-server
|
||||
git clone https://git.mokoconsulting.tech/MokoConsulting/Template-MCP.git my-mcp-server
|
||||
cd my-mcp-server
|
||||
```
|
||||
2. Install dependencies:
|
||||
@@ -61,11 +61,11 @@ Template repository for creating MokoCLI-compliant Model Context Protocol (MCP)
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation is available on the [Wiki](https://git.mokoconsulting.tech/MokoConsulting/Template-NPM/wiki), including:
|
||||
Full documentation is available on the [Wiki](https://git.mokoconsulting.tech/MokoConsulting/Template-MCP/wiki), including:
|
||||
|
||||
- [API](https://git.mokoconsulting.tech/MokoConsulting/Template-NPM/wiki/API) -- tool reference and connection parameter documentation
|
||||
- [ARCHITECTURE](https://git.mokoconsulting.tech/MokoConsulting/Template-NPM/wiki/ARCHITECTURE) -- MCP server design and REST API bridge pattern
|
||||
- [INSTALLATION](https://git.mokoconsulting.tech/MokoConsulting/Template-NPM/wiki/INSTALLATION) -- setup prerequisites and configuration guide
|
||||
- [API](https://git.mokoconsulting.tech/MokoConsulting/Template-MCP/wiki/API) -- tool reference and connection parameter documentation
|
||||
- [ARCHITECTURE](https://git.mokoconsulting.tech/MokoConsulting/Template-MCP/wiki/ARCHITECTURE) -- MCP server design and REST API bridge pattern
|
||||
- [INSTALLATION](https://git.mokoconsulting.tech/MokoConsulting/Template-MCP/wiki/INSTALLATION) -- setup prerequisites and configuration guide
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -77,8 +77,4 @@ This project is licensed under the GNU General Public License v3.0 or later -- s
|
||||
|
||||
---
|
||||
|
||||
*[Moko Consulting](https://mokoconsulting.tech) -- [MokoCLI](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home)*
|
||||
|
||||
## Template CI Notes
|
||||
|
||||
Build/CI/release/version workflows are guarded to skip template repos (Template-*) and only run in repos created from them.
|
||||
*[Moko Consulting](https://mokoconsulting.tech) -- [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)*
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Automation.CI
|
||||
# INGROUP: moko-platform.Automation
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /automation/ci-issue-reporter.sh
|
||||
# VERSION: 09.23.00
|
||||
# BRIEF: Creates or updates a Gitea issue when a CI gate fails.
|
||||
# Deduplicates by searching open issues with the "ci-auto" label
|
||||
# whose title matches the gate. If a matching issue exists, a comment
|
||||
# is appended instead of opening a duplicate.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ────────────────────────────────────────────────────────────────
|
||||
GITEA_URL="${GITEA_URL:-https://git.mokoconsulting.tech}"
|
||||
GITEA_TOKEN="${GITEA_TOKEN:-}"
|
||||
REPO="${GITHUB_REPOSITORY:-}"
|
||||
RUN_URL="${GITHUB_SERVER_URL:-${GITEA_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}"
|
||||
LABEL_NAME="ci-auto"
|
||||
LABEL_COLOR="#e11d48"
|
||||
|
||||
GATE=""
|
||||
DETAILS=""
|
||||
SEVERITY="error"
|
||||
WORKFLOW=""
|
||||
|
||||
# ── Parse arguments ─────────────────────────────────────────────────────────
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: ci-issue-reporter.sh --gate NAME --details TEXT [OPTIONS]
|
||||
|
||||
Required:
|
||||
--gate CI gate name (e.g. "Code Quality", "Self-Health")
|
||||
--details Human-readable failure description
|
||||
|
||||
Optional:
|
||||
--severity "error" (default) or "warning"
|
||||
--workflow Workflow name for the issue title
|
||||
--repo owner/repo (default: \$GITHUB_REPOSITORY)
|
||||
--run-url URL to the CI run (auto-detected from env)
|
||||
--token Gitea API token (default: \$GITEA_TOKEN)
|
||||
--url Gitea base URL (default: \$GITEA_URL)
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--gate) GATE="$2"; shift 2 ;;
|
||||
--details) DETAILS="$2"; shift 2 ;;
|
||||
--severity) SEVERITY="$2"; shift 2 ;;
|
||||
--workflow) WORKFLOW="$2"; shift 2 ;;
|
||||
--repo) REPO="$2"; shift 2 ;;
|
||||
--run-url) RUN_URL="$2"; shift 2 ;;
|
||||
--token) GITEA_TOKEN="$2"; shift 2 ;;
|
||||
--url) GITEA_URL="$2"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown option: $1"; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$GATE" ]] && { echo "ERROR: --gate is required"; usage; }
|
||||
[[ -z "$DETAILS" ]] && { echo "ERROR: --details is required"; usage; }
|
||||
[[ -z "$GITEA_TOKEN" ]] && { echo "ERROR: GITEA_TOKEN not set"; exit 1; }
|
||||
[[ -z "$REPO" ]] && { echo "ERROR: GITHUB_REPOSITORY not set"; exit 1; }
|
||||
|
||||
API="${GITEA_URL}/api/v1/repos/${REPO}"
|
||||
|
||||
# ── Build title ─────────────────────────────────────────────────────────────
|
||||
if [[ -n "$WORKFLOW" ]]; then
|
||||
TITLE="[CI] ${WORKFLOW}: ${GATE} failed"
|
||||
else
|
||||
TITLE="[CI] ${GATE} failed"
|
||||
fi
|
||||
|
||||
# ── Ensure label exists ─────────────────────────────────────────────────────
|
||||
ensure_label() {
|
||||
local exists
|
||||
exists=$(curl -sf -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$exists" == "200" ]]; then
|
||||
# Check if label already exists
|
||||
local found
|
||||
found=$(curl -sf \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null \
|
||||
| grep -o "\"name\":\"${LABEL_NAME}\"" || true)
|
||||
|
||||
if [[ -z "$found" ]]; then
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/labels" \
|
||||
-d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Search for existing open issue ──────────────────────────────────────────
|
||||
find_existing_issue() {
|
||||
# URL-encode the gate name for the query
|
||||
local query
|
||||
query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g')
|
||||
|
||||
local response
|
||||
response=$(curl -sf \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \
|
||||
2>/dev/null || echo "[]")
|
||||
|
||||
# Extract the first matching issue number
|
||||
echo "$response" \
|
||||
| grep -oP '"number":\s*\K[0-9]+' \
|
||||
| head -1
|
||||
}
|
||||
|
||||
# ── Build issue body ────────────────────────────────────────────────────────
|
||||
build_body() {
|
||||
local severity_badge
|
||||
if [[ "$SEVERITY" == "error" ]]; then
|
||||
severity_badge="**Severity:** Error"
|
||||
else
|
||||
severity_badge="**Severity:** Warning"
|
||||
fi
|
||||
|
||||
cat <<BODY
|
||||
## CI Gate Failure: ${GATE}
|
||||
|
||||
${severity_badge}
|
||||
**Workflow:** ${WORKFLOW:-unknown}
|
||||
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
||||
**Commit:** \`${GITHUB_SHA:0:8}\`
|
||||
**Run:** [View CI run](${RUN_URL})
|
||||
|
||||
### Details
|
||||
|
||||
${DETAILS}
|
||||
|
||||
### Resolution
|
||||
|
||||
Fix the issue described above and push a new commit. This issue will be closed automatically when the gate passes, or can be closed manually.
|
||||
|
||||
---
|
||||
*Auto-created by [ci-issue-reporter](${GITEA_URL}/${REPO}/src/branch/main/automation/ci-issue-reporter.sh)*
|
||||
BODY
|
||||
}
|
||||
|
||||
# ── Build comment body (for existing issues) ────────────────────────────────
|
||||
build_comment() {
|
||||
cat <<COMMENT
|
||||
### CI failure recurrence
|
||||
|
||||
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
||||
**Commit:** \`${GITHUB_SHA:0:8}\`
|
||||
**Run:** [View CI run](${RUN_URL})
|
||||
|
||||
${DETAILS}
|
||||
COMMENT
|
||||
}
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────────────
|
||||
ensure_label
|
||||
|
||||
EXISTING=$(find_existing_issue)
|
||||
|
||||
if [[ -n "$EXISTING" ]]; then
|
||||
# Append comment to existing issue
|
||||
COMMENT_BODY=$(build_comment)
|
||||
COMMENT_JSON=$(printf '%s' "$COMMENT_BODY" | python3 -c "
|
||||
import sys, json
|
||||
print(json.dumps({'body': sys.stdin.read()}))" 2>/dev/null)
|
||||
|
||||
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${EXISTING}/comments" \
|
||||
-d "${COMMENT_JSON}" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$HTTP" == "201" ]]; then
|
||||
echo "Commented on existing issue #${EXISTING}"
|
||||
else
|
||||
echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})"
|
||||
fi
|
||||
else
|
||||
# Create new issue
|
||||
ISSUE_BODY=$(build_body)
|
||||
ISSUE_JSON=$(python3 -c "
|
||||
import sys, json
|
||||
body = sys.stdin.read()
|
||||
print(json.dumps({
|
||||
'title': sys.argv[1],
|
||||
'body': body,
|
||||
'labels': []
|
||||
}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null)
|
||||
|
||||
# Create the issue
|
||||
RESPONSE=$(curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues" \
|
||||
-d "${ISSUE_JSON}" 2>/dev/null || echo "{}")
|
||||
|
||||
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
|
||||
|
||||
if [[ -n "$ISSUE_NUM" ]]; then
|
||||
# Apply label (separate call — more reliable across Gitea versions)
|
||||
LABEL_ID=$(curl -sf \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null \
|
||||
| grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \
|
||||
| head -1 || true)
|
||||
|
||||
if [[ -n "$LABEL_ID" ]]; then
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${ISSUE_NUM}/labels" \
|
||||
-d "{\"labels\":[${LABEL_ID}]}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
echo "Created issue #${ISSUE_NUM}: ${TITLE}"
|
||||
else
|
||||
echo "WARNING: Failed to create issue"
|
||||
echo "Response: ${RESPONSE}"
|
||||
fi
|
||||
fi
|
||||
Reference in New Issue
Block a user