diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..4e8d910 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "schedule": ["before 6am on monday"], + "timezone": "UTC", + "labels": ["dependencies"], + "assigneesFromCodeOwners": true, + "reviewersFromCodeOwners": true, + "lockFileMaintenance": { + "enabled": true, + "schedule": ["before 6am on monday"] + }, + "packageRules": [ + { + "matchPackageNames": ["svelte", "@sveltejs/kit", "@sveltejs/adapter-static"], + "groupName": "svelte core", + "schedule": ["before 6am on monday"], + "minimumReleaseAge": "3 days" + }, + { + "matchPackageNames": ["tailwindcss", "@tailwindcss/vite"], + "groupName": "tailwind", + "schedule": ["before 6am on monday"] + }, + { + "matchPackageNames": [ + "shadcn-svelte", + "@lucide/svelte", + "clsx", + "tailwind-merge", + "tailwind-variants" + ], + "groupName": "shadcn ecosystem", + "schedule": ["before 6am on monday"] + }, + { + "matchPackageNames": ["vitest", "@vitest/browser", "playwright", "@playwright/test"], + "groupName": "testing tools", + "schedule": ["before 6am on monday"] + }, + { + "matchPackageNames": ["eslint", "prettier", "typescript"], + "groupName": "dev tools", + "schedule": ["before 6am on monday"] + }, + { + "matchPackageNames": ["vite"], + "groupName": "build tools", + "schedule": ["before 6am on monday"] + }, + { + "matchDepTypes": ["devDependencies"], + "automerge": true, + "automergeType": "pr", + "requiredStatusChecks": null, + "matchUpdateTypes": ["patch", "minor"] + } + ], + "vulnerabilityAlerts": { + "enabled": true, + "schedule": ["at any time"], + "dependencyDashboardApproval": false + }, + "dependencyDashboard": true, + "dependencyDashboardTitle": "๐Ÿค– Dependency Dashboard", + "dependencyDashboardLabels": ["dependencies"], + "prHourlyLimit": 3, + "prConcurrentLimit": 5, + "gitignore": ["node_modules/", ".svelte-kit/", "build/", "dist/"] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e93597..5c2c141 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,6 @@ jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20, 22] - steps: - name: Checkout code uses: actions/checkout@v4 @@ -23,85 +19,22 @@ jobs: with: bun-version: '1.2.20' - - name: Verify Bun installation - run: bun --version - - - name: Cache dependencies - uses: actions/cache@v4 + - name: Setup Nushell + uses: hustcer/setup-nu@v3 with: - path: | - ~/.bun/install/cache - node_modules - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- + version: '0.99' + + - name: Setup Just + uses: extractions/setup-just@v2 - name: Install dependencies - run: bun install --frozen-lockfile - - - name: Run linting - run: bun run lint - - - name: Run type checking - run: bun run check - - - name: Run unit tests - run: bun run test:unit --run + run: just install - name: Install Playwright browsers - run: bunx playwright install --with-deps + run: just install-browsers - - name: Run E2E tests - run: bun run test:e2e - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: failure() - with: - name: test-results-${{ matrix.node-version }} - path: | - test-results/ - playwright-report/ - retention-days: 30 - - build: - runs-on: ubuntu-latest - needs: test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.2.20' - - - name: Verify Bun installation - run: bun --version - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: | - ~/.bun/install/cache - node_modules - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - - name: Install dependencies - run: bun install --frozen-lockfile - - - name: Build application - run: bun run build - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-files - path: build/ - retention-days: 7 + - name: Run CI pipeline + run: just ci security: runs-on: ubuntu-latest @@ -115,20 +48,63 @@ jobs: with: bun-version: '1.2.20' - - name: Verify Bun installation - run: bun --version + - name: Setup Nushell + uses: hustcer/setup-nu@v3 + with: + version: '0.99' + + - name: Setup Just + uses: extractions/setup-just@v2 - name: Install dependencies - run: bun install --frozen-lockfile + run: just install - name: Run security audit - run: bun audit + run: just audit continue-on-error: true - - name: Run CodeQL Analysis - uses: github/codeql-action/init@v3 + - name: Run Semgrep security scan + uses: semgrep/semgrep-action@v1 with: - languages: javascript + config: >- + p/security-audit + p/secrets + p/owasp-top-ten + p/javascript + p/typescript + generateSarif: '1' + # Token only needed for Semgrep Cloud features (optional) + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + continue-on-error: true - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + docker-build: + runs-on: ubuntu-latest + needs: [test, security] + if: github.ref == 'refs/heads/master' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ vars.DOCKER_REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:latest + ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index e40c8ed..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Deploy - -on: - push: - branches: [master] - workflow_dispatch: - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - - permissions: - contents: read - pages: write - id-token: write - - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.2.20' - - - name: Verify Bun installation - run: bun --version - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: | - ~/.bun/install/cache - node_modules - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - - name: Install dependencies - run: bun install --frozen-lockfile - - - name: Build application - run: bun run build - - - name: Setup Pages - uses: actions/configure-pages@v5 - - - name: Upload to GitHub Pages - uses: actions/upload-pages-artifact@v3 - with: - path: build/ - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - - docker-build: - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=sha - type=raw,value=latest,enable={{is_default_branch}} - - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/gitea-ci.yml b/.github/workflows/gitea-ci.yml deleted file mode 100644 index 9d3db61..0000000 --- a/.github/workflows/gitea-ci.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Gitea CI - -on: - push: - branches: [master, develop] - pull_request: - branches: [master, develop] - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.2.20' - - - name: Verify Bun installation - run: bun --version - - - name: Install dependencies - run: bun install --frozen-lockfile - - - name: Run linting - run: bun run lint - - - name: Run type checking - run: bun run check - - - name: Run unit tests - run: bun run test:unit --run - - - name: Install Playwright browsers - run: bunx playwright install --with-deps - - - name: Run E2E tests - run: bun run test:e2e - - docker-build: - runs-on: ubuntu-latest - needs: test - if: github.ref == 'refs/heads/master' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Log in to Container Registry - uses: docker/login-action@v2 - with: - registry: ${{ vars.DOCKER_REGISTRY }} - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and push Docker image - uses: docker/build-push-action@v4 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:latest - ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_REPOSITORY }}:${{ github.sha }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/README.md b/README.md index e2cf4c7..4974298 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ A modern, production-ready template for building static websites with Svelte 5, - ๐Ÿ“ **TypeScript** - Type safety out of the box - ๐Ÿงช **Testing Suite** - Vitest for unit tests, Playwright for E2E - ๐Ÿ“ **Code Quality** - ESLint + Prettier configured +- ๐Ÿ”’ **Security** - Semgrep static analysis + Bun audit +- ๐Ÿค– **Dependencies** - Renovate for automated updates - ๐Ÿณ **Docker Ready** - Production nginx container - ๐Ÿš€ **Static Generation** - Optimized for deployment anywhere @@ -22,6 +24,14 @@ A modern, production-ready template for building static websites with Svelte 5, # Install dependencies bun install +# Install Playwright browsers (for E2E tests) +just install-browsers +# OR: bunx playwright install + +# Install system dependencies for Playwright (Linux/WSL) +just install-deps +# OR: sudo bunx playwright install-deps + # Start development server bun run dev ``` @@ -111,14 +121,30 @@ Production nginx configuration in `nginx.conf` includes: - Security headers - SPA routing support -## Deployment +## CI/CD & Deployment + +### GitHub Actions / Gitea Actions + +Single workflow (`.github/workflows/ci.yml`) that runs: + +- **Tests & Linting** - Full CI pipeline with Just commands +- **Security Scanning** - Semgrep static analysis + Bun audit +- **Docker Build** - Pushes to your container registry + +**Required Secrets/Variables:** + +- `DOCKER_REGISTRY` - Your container registry URL +- `DOCKER_REPOSITORY` - Your repository path +- `DOCKER_USERNAME` / `DOCKER_PASSWORD` - Registry credentials +- `SEMGREP_APP_TOKEN` - Optional for advanced Semgrep features + +### Deployment Options This template generates a static site that can be deployed to: -- **Static Hosts**: Netlify, Vercel, GitHub Pages -- **Docker**: Any container platform (production nginx setup) -- **CDN**: Any CDN with the built files -- **Traditional Hosting**: Any web server +- **Container Platforms** - Docker with nginx (recommended) +- **Static Hosts** - Netlify, Vercel, any CDN +- **Traditional Hosting** - Any web server ## Contributing diff --git a/bun.lock b/bun.lock index 828dc59..2e7a4ed 100644 --- a/bun.lock +++ b/bun.lock @@ -12,7 +12,7 @@ "@sveltejs/kit": "^2.22.0", "@sveltejs/vite-plugin-svelte": "^6.0.0", "@tailwindcss/vite": "^4.0.0", - "@vitest/browser": "^3.2.3", + "@vitest/browser": "^3.2.4", "clsx": "^2.1.1", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", @@ -33,7 +33,7 @@ "vite": "^7.0.4", "vite-plugin-devtools-json": "^0.2.0", "vitest": "^3.2.3", - "vitest-browser-svelte": "^0.1.0", + "vitest-browser-svelte": "^1.1.0", }, }, }, @@ -660,7 +660,7 @@ "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "vitest-browser-svelte": ["vitest-browser-svelte@0.1.0", "", { "peerDependencies": { "@vitest/browser": "^2.1.0 || ^3.0.0-0", "svelte": ">3.0.0", "vitest": "^2.1.0 || ^3.0.0-0" } }, "sha512-YB6ZUZZQNqU1T9NzvTEDpwpPv35Ng1NZMPBh81zDrLEdOgROGE6nJb79NWb1Eu/a8VkHifqArpOZfJfALge6xQ=="], + "vitest-browser-svelte": ["vitest-browser-svelte@1.1.0", "", { "peerDependencies": { "@vitest/browser": "^2.1.0 || ^3.0.0 || ^4.0.0-0", "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vitest": "^2.1.0 || ^3.0.0 || ^4.0.0-0" } }, "sha512-o98mCzKkWBjvmaGzi69rvyBd1IJ7zFPGI0jcID9vI4F5DmdG//YxkIbeQ7TS27hAVR+MULnBZNja2DUiuUBZyA=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], diff --git a/justfile b/justfile index a19d816..334f565 100644 --- a/justfile +++ b/justfile @@ -35,6 +35,14 @@ test-unit: test-e2e: bun run test:e2e +# Install Playwright browsers +install-browsers: + bunx playwright install + +# Install Playwright system dependencies (requires sudo) +install-deps: + bunx playwright install-deps + # Run tests in watch mode test-watch: bun run test:unit --watch @@ -116,4 +124,4 @@ report: bun run build --reporter=verbose echo "" echo "๐Ÿ“ฆ Build artifacts:" - ls -la build/ \ No newline at end of file + ls -la build/ diff --git a/package.json b/package.json index 8274b46..ba9455b 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@sveltejs/kit": "^2.22.0", "@sveltejs/vite-plugin-svelte": "^6.0.0", "@tailwindcss/vite": "^4.0.0", - "@vitest/browser": "^3.2.3", + "@vitest/browser": "^3.2.4", "clsx": "^2.1.1", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", @@ -46,6 +46,6 @@ "vite": "^7.0.4", "vite-plugin-devtools-json": "^0.2.0", "vitest": "^3.2.3", - "vitest-browser-svelte": "^0.1.0" + "vitest-browser-svelte": "^1.1.0" } } diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..3e6ac92 --- /dev/null +++ b/renovate.json @@ -0,0 +1,3 @@ +{ + "extends": [".github/renovate.json"] +} diff --git a/vite.config.ts b/vite.config.ts index b15c0ae..9e47eef 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,7 +6,6 @@ import { defineConfig } from 'vite'; export default defineConfig({ plugins: [tailwindcss(), sveltekit(), devtoolsJson()], test: { - expect: { requireAssertions: true }, projects: [ { extends: './vite.config.ts', @@ -18,9 +17,7 @@ export default defineConfig({ provider: 'playwright', instances: [{ browser: 'chromium' }] }, - include: ['src/**/*.svelte.{test,spec}.{js,ts}'], - exclude: ['src/lib/server/**'], - setupFiles: ['./vitest-setup-client.ts'] + include: ['src/**/*.svelte.{test,spec}.{js,ts}'] } }, { diff --git a/vitest-setup-client.ts b/vitest-setup-client.ts deleted file mode 100644 index 570b9f0..0000000 --- a/vitest-setup-client.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -///