name: Init # One-shot bootstrap — runs on first push to main, skips if already initialised. # Substitutes all __PROJECT_NAME__ / __MODULE_PATH__ placeholders (file contents # and directory names), then runs context:sync to generate CLAUDE.md and friends. # Deletes itself on completion so it never runs again. on: push: branches: [main] jobs: init: name: Bootstrap project from template runs-on: self-hosted steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check if already initialised id: guard run: | if [ -f CLAUDE.md ]; then echo "skip=true" >> "$GITHUB_OUTPUT" else echo "skip=false" >> "$GITHUB_OUTPUT" fi - name: Derive project variables if: steps.guard.outputs.skip == 'false' id: vars run: | PROJECT_NAME=$(basename "$(git rev-parse --show-toplevel)") GITEA_HOST=$(git remote get-url origin | sed 's|http://||;s|https://||;s|/.*||') OWNER=$(git remote get-url origin | sed 's|.*://[^/]*/\([^/]*\)/.*|\1|') MODULE_PATH="${GITEA_HOST}/${OWNER}/${PROJECT_NAME}" echo "project_name=${PROJECT_NAME}" >> "$GITHUB_OUTPUT" echo "module_path=${MODULE_PATH}" >> "$GITHUB_OUTPUT" echo "→ PROJECT_NAME=${PROJECT_NAME}" echo "→ MODULE_PATH=${MODULE_PATH}" - name: Configure git with token if: steps.guard.outputs.skip == 'false' env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | REMOTE=$(git remote get-url origin) # Inject token into remote URL for push-back AUTH_REMOTE=$(echo "$REMOTE" | sed "s|http://|http://gitea-actions:${GITEA_TOKEN}@|") git remote set-url origin "$AUTH_REMOTE" git config user.name "gitea-actions[bot]" git config user.email "gitea-actions[bot]@noreply.local" - name: Substitute placeholders in file contents if: steps.guard.outputs.skip == 'false' env: PROJECT_NAME: ${{ steps.vars.outputs.project_name }} MODULE_PATH: ${{ steps.vars.outputs.module_path }} run: | set -euo pipefail while IFS= read -r f; do if grep -qE '__PROJECT_NAME__|__MODULE_PATH__' "$f" 2>/dev/null; then sed -i \ -e "s|__PROJECT_NAME__|${PROJECT_NAME}|g" \ -e "s|__MODULE_PATH__|${MODULE_PATH}|g" \ "$f" echo " substituted: $f" fi done < <(git ls-files | grep -v '^\.gitea/workflows/init\.yml$') - name: Rename templated directories if: steps.guard.outputs.skip == 'false' env: PROJECT_NAME: ${{ steps.vars.outputs.project_name }} run: | set -euo pipefail if [ -d "cmd/__PROJECT_NAME__" ]; then git mv "cmd/__PROJECT_NAME__" "cmd/${PROJECT_NAME}" echo " renamed: cmd/__PROJECT_NAME__ → cmd/${PROJECT_NAME}" fi - name: Install task if missing if: steps.guard.outputs.skip == 'false' run: | which task 2>/dev/null || go install github.com/go-task/task/v3/cmd/task@latest - name: Run context:sync if: steps.guard.outputs.skip == 'false' run: task context:sync - name: Remove this init workflow if: steps.guard.outputs.skip == 'false' run: | git rm .gitea/workflows/init.yml echo " removed: .gitea/workflows/init.yml" - name: Commit and push if: steps.guard.outputs.skip == 'false' env: PROJECT_NAME: ${{ steps.vars.outputs.project_name }} run: | git add -A git commit -m "chore: bootstrap ${PROJECT_NAME} from template" git push origin main echo "✓ project initialised"