diff --git a/.github/scripts/rename-template.py b/.github/scripts/rename-template.py new file mode 100644 index 0000000..119d09e --- /dev/null +++ b/.github/scripts/rename-template.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""Rename template placeholders when a new repo is created from the template.""" + +import os +import re + + +def to_kebab(name: str) -> str: + return name.lower().replace("_", "-") + + +def to_snake(name: str) -> str: + return name.lower().replace("-", "_") + + +def to_title(name: str) -> str: + return " ".join(word.capitalize() for word in re.split(r"[-_]", name)) + + +def to_pascal(name: str) -> str: + return "".join(word.capitalize() for word in re.split(r"[-_]", name)) + + +def sanitize_docker(name: str) -> str: + if name[0].isdigit(): + name = "app-" + name + return re.sub(r"[^a-z0-9-]", "-", name) + + +def main(): + repo_full = os.environ.get("GITHUB_REPOSITORY", "") + if not repo_full: + raise RuntimeError("GITHUB_REPOSITORY not set") + + repo_name = repo_full.split("/")[-1] + kebab = sanitize_docker(to_kebab(repo_name)) + snake = to_snake(repo_name) + title = to_title(repo_name) + pascal = to_pascal(repo_name) + + replacements = [ + ( + "compose.yaml", + [ + (r"^ frankenphp:\n", f" {kebab}:\n"), + (r"^ container_name: frankenphp\n", f" container_name: {kebab}\n"), + ], + ), + ( + ".devcontainer/devcontainer.json", + [ + ('"name": "PHP Starter Kit"', f'"name": "{title}"'), + ('"service": "frankenphp"', f'"service": "{kebab}"'), + ], + ), + ( + "makefile", + [ + (r"exec --user=robbyte frankenphp", f"exec --user=robbyte {kebab}"), + (r"exec frankenphp cat", f"exec {kebab} cat"), + ], + ), + ( + "build/prod/docker-entrypoint.sh", + [ + ('echo "PHP Starter Kit - Starting..."', f'echo "{title} - Starting..."'), + ], + ), + ( + "src/public/index.php", + [ + ("PHP Starter Kit — Setup Wizard", f"{title} — Setup Wizard"), + ('
PHP Starter Kit
', f'
{title}
'), + ("PHP Starter Kit ©", f"{title} ©"), + ], + ), + ( + "README.md", + [ + ("# PHP Starter Kit", f"# {title}"), + ("https://github.com/rdurica/php_starter_kit.git", f"https://github.com/{repo_full}.git"), + ("https://github.com/rdurica/php_starter_kit/actions", f"https://github.com/{repo_full}/actions"), + ("cd php_starter_kit", f"cd {repo_name}"), + ], + ), + ( + "AGENTS.md", + [ + ("# Agent Context for PHP Starter Kit", f"# Agent Context for {title}"), + ("docker compose exec frankenphp", f"docker compose exec {kebab}"), + ], + ), + ] + + for filepath, rules in replacements: + if not os.path.exists(filepath): + print(f"Warning: {filepath} not found, skipping") + continue + + with open(filepath, "r", encoding="utf-8") as f: + content = f.read() + + original = content + for pattern, replacement in rules: + flags = re.MULTILINE if pattern.startswith("^") else 0 + content = re.sub(pattern, replacement, content, flags=flags) + + if content != original: + with open(filepath, "w", encoding="utf-8") as f: + f.write(content) + print(f"Updated: {filepath}") + else: + print(f"No changes: {filepath}") + + with open(".template-configured", "w", encoding="utf-8") as f: + f.write(f"Configured from template for {repo_full}\n") + print("Created: .template-configured") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 29287bf..b86200f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,9 +25,6 @@ jobs: - name: Validate CI Compose config run: docker compose -f compose.ci.yaml config - - name: Validate demo Compose config - run: docker compose -f compose.demo.yaml config - tests: name: Framework Tests runs-on: ubuntu-latest diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 7f1deec..d30aaaa 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -25,9 +25,6 @@ jobs: - name: Validate CI Compose config run: docker compose -f compose.ci.yaml config - - name: Validate demo Compose config - run: docker compose -f compose.demo.yaml config - frontend: name: Frontend Quality runs-on: ubuntu-latest diff --git a/.github/workflows/template-setup.yml b/.github/workflows/template-setup.yml new file mode 100644 index 0000000..4ea0891 --- /dev/null +++ b/.github/workflows/template-setup.yml @@ -0,0 +1,51 @@ +name: Template Setup + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + rename: + name: Rename from template + runs-on: ubuntu-latest + if: github.repository != 'rdurica/php_starter_kit' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if already configured + id: check + run: | + if [ -f ".template-configured" ]; then + echo "already_configured=true" >> "$GITHUB_OUTPUT" + else + echo "already_configured=false" >> "$GITHUB_OUTPUT" + fi + + - name: Run rename script + if: steps.check.outputs.already_configured == 'false' + run: python3 .github/scripts/rename-template.py + + - name: Commit and push renamed files + if: steps.check.outputs.already_configured == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "chore: rename project from template [skip ci]" + git push + + - name: Remove template setup files + if: steps.check.outputs.already_configured == 'false' + run: | + git rm .github/workflows/template-setup.yml + git rm -rf .github/scripts + git commit -m "chore: remove template setup workflow [skip ci]" + git push diff --git a/README.md b/README.md index b3fd975..47a06a0 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ This starter kit provides a ready-to-use, out-of-the-box local development envir - **FrankenPHP** — Modern PHP application server with HTTP/2, HTTP/3, and automatic HTTPS - **Secure by default** — Non-root user, security headers, hardened sessions, no `expose_php` -- **Multi-environment** — Dev, CI, and Demo configurations +- **Multi-environment** — Dev and CI configurations - **Multi-stage production build** — Minimal attack surface, optimized layers - **CI/CD ready** — GitHub Actions with code quality, tests, security scanning - **DevContainer support** — VSCode remote containers out of the box @@ -109,11 +109,6 @@ After installation, you can delete `/setup.php` via the success dialog or manual - SQLite in-memory for fast tests - Ideal for GitHub Actions -### Demo (`compose.demo.yaml`) -- Self-contained stack with PostgreSQL and Redis -- Uses prebuilt GHCR image -- Port 8080 on host - ## Production Build the production image: @@ -122,12 +117,6 @@ Build the production image: docker build -f build/prod/Dockerfile -t myapp:latest . ``` -Run with the demo stack: - -```bash -docker compose -f compose.demo.yaml up -``` - ## CI/CD Three GitHub Actions workflows are included: diff --git a/compose.demo.yaml b/compose.demo.yaml deleted file mode 100644 index 23ac0c8..0000000 --- a/compose.demo.yaml +++ /dev/null @@ -1,39 +0,0 @@ -services: - app: - image: php-starter-kit-demo:local - build: - context: . - dockerfile: build/prod/Dockerfile - env_file: demo.env - ports: - - "8080:80" - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_healthy - - postgres: - image: postgres:17-alpine - environment: - POSTGRES_DB: starter_kit - POSTGRES_USER: starter_kit - POSTGRES_PASSWORD: starter_kit_demo_only - volumes: - - pgdata:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U starter_kit"] - interval: 5s - timeout: 5s - retries: 5 - - redis: - image: redis:8-alpine - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 5s - retries: 5 - -volumes: - pgdata: diff --git a/demo.env b/demo.env deleted file mode 100644 index 2c7d1a6..0000000 --- a/demo.env +++ /dev/null @@ -1,40 +0,0 @@ -# Demo-only local defaults. Replace every secret before any real deployment. -APP_NAME=PHPStarterKit -APP_ENV=production -APP_KEY=base64:ZGVtby1vbmx5LWxvY2FsLWtleS0zMi1ieXRlcyEhISE= -APP_DEBUG=false -APP_URL=http://localhost:8080 - -APP_LOCALE=en -APP_FALLBACK_LOCALE=en - -LOG_CHANNEL=stderr -LOG_LEVEL=warning - -DB_CONNECTION=pgsql -DB_HOST=postgres -DB_PORT=5432 -DB_DATABASE=starter_kit -DB_USERNAME=starter_kit -DB_PASSWORD=starter_kit_demo_only - -SESSION_DRIVER=redis -SESSION_LIFETIME=120 -SESSION_ENCRYPT=true -SESSION_PATH=/ -SESSION_DOMAIN= - -BROADCAST_CONNECTION=log -FILESYSTEM_DISK=local -QUEUE_CONNECTION=redis - -CACHE_STORE=redis - -REDIS_CLIENT=predis -REDIS_HOST=redis -REDIS_PASSWORD= -REDIS_PORT=6379 - -MAIL_MAILER=log - -VITE_APP_NAME="${APP_NAME}"