feat: add gitignore validation, move bulk-repo-sync workflow here

- Add REQUIRED_GITIGNORE_ENTRIES constant with mandatory patterns:
  Sublime project/workspace, sftp-config, IDE dirs, secrets, vendor, logs
- Add validateGitignoreEntries() method for checking required entries
- mergeGitConfigFile() still appends missing entries (non-destructive)
- Add .gitea/workflows/bulk-repo-sync.yml (moved from MokoStandards)
  - Runs from this repo directly (checkout self, not remote)
  - Org updated to MokoConsulting (Gitea)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan Miller
2026-04-18 18:17:24 -05:00
parent 784f423973
commit bd53fe834f
2 changed files with 183 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards-API.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
# PATH: /.gitea/workflows/bulk-repo-sync.yml
# VERSION: 04.06.12
# BRIEF: Bulk repo sync — runs from API repo, syncs standards to all governed repos
name: Bulk Repository Sync
on:
schedule:
- cron: '0 0 1 * *'
workflow_dispatch:
inputs:
dry_run:
description: 'Preview mode (no changes)'
required: false
type: boolean
default: true
repos:
description: 'Comma-separated repo names (empty = all)'
required: false
type: string
default: ''
exclude:
description: 'Comma-separated repos to skip'
required: false
type: string
default: ''
force:
description: 'Force overwrite protected files'
required: false
type: boolean
default: false
permissions:
contents: write
issues: write
pull-requests: write
jobs:
bulk-sync:
name: Sync Standards to Repositories
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: json, mbstring, curl
tools: composer
coverage: none
- name: Install Dependencies
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
- name: Build CLI Arguments
id: args
run: |
ARGS="--org MokoConsulting"
if [ "${{ inputs.dry_run }}" = "true" ] || [ "${{ gitea.event_name }}" = "schedule" ]; then
ARGS="$ARGS --dry-run"
fi
if [ -n "${{ inputs.repos }}" ]; then
ARGS="$ARGS --repos ${{ inputs.repos }}"
fi
if [ -n "${{ inputs.exclude }}" ]; then
ARGS="$ARGS --exclude ${{ inputs.exclude }}"
fi
if [ "${{ inputs.force }}" = "true" ]; then
ARGS="$ARGS --force"
fi
ARGS="$ARGS --yes"
echo "args=$ARGS" >> $GITHUB_OUTPUT
- name: Run Bulk Sync
run: |
echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}"
php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log
env:
GA_TOKEN: ${{ secrets.GA_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GIT_PLATFORM: gitea
GITEA_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting
- name: Commit Updated Definitions
if: success() && inputs.dry_run != 'true'
run: |
if [ -n "$(git status --porcelain definitions/sync/)" ]; then
git config user.name "gitea-actions[bot]"
git config user.email "gitea-actions[bot]@git.mokoconsulting.tech"
git add definitions/sync/*.def.tf
git commit -m "chore: update synced repository definitions" || true
git push || true
fi
- name: Upload Sync Log
if: always()
uses: actions/upload-artifact@v4
with:
name: bulk-sync-log-${{ gitea.run_number }}
path: /tmp/bulk_sync.log
retention-days: 30
+71
View File
@@ -903,6 +903,77 @@ HCL;
return $entries;
}
/**
* Required .gitignore entries that MUST exist in every governed repo.
* The sync validates these exist (appending if missing) without
* overwriting custom entries. Repos can add their own patterns freely.
*/
private const REQUIRED_GITIGNORE_ENTRIES = [
// Secrets & environment
'.env',
'.env.local',
'.env.*.local',
'secrets/',
'*.secrets.*',
// Sublime Text project files
'*.sublime-project',
'*.sublime-workspace',
'*.sublime-settings',
// SFTP config (Sublime SFTP, VS Code SFTP, etc.)
'sftp-config*.json',
'sftp-config.json.template',
'sftp-settings.json',
// IDE / editor
'.idea/',
'.vscode/*',
'.claude/',
'*.code-workspace',
// OS cruft
'.DS_Store',
'Thumbs.db',
// Task tracking
'TODO.md',
// Vendor / dependencies
'/vendor/',
'node_modules/',
// Logs
'*.log',
];
/**
* Validate that required .gitignore entries exist in a repo.
* Returns array of missing entries, empty if all present.
*
* @param string $existingContent Current .gitignore content from repo
* @return array<string> Missing required entries
*/
public function validateGitignoreEntries(string $existingContent): array
{
$existingLines = array_map('trim', explode("\n", $existingContent));
$existingSet = [];
foreach ($existingLines as $line) {
if ($line !== '' && !str_starts_with($line, '#')) {
$existingSet[$line] = true;
}
}
$missing = [];
foreach (self::REQUIRED_GITIGNORE_ENTRIES as $entry) {
if (!isset($existingSet[$entry])) {
$missing[] = $entry;
}
}
return $missing;
}
private function mergeGitConfigFile(string $existing, string $template): string
{
$existingLines = array_map('rtrim', explode("\n", $existing));