name: CI - Build Starters Matrix run-name: ${{ github.workflow }} - ${{ github.ref_name }} on: schedule: - cron: "0 2 * * *" workflow_dispatch: push: branches: [main] paths: - "modules/**" - "templates/**" - "test/**" - "scripts/**" - "package.json" - "pnpm-lock.yaml" - "biome.json" - "vite.config.js" pull_request: types: - opened - reopened - synchronize - ready_for_review paths: - "modules/**" - "templates/**" - "test/**" - "scripts/**" - "package.json" - "pnpm-lock.yaml" - "biome.json" - "vite.config.js" permissions: contents: read concurrency: group: templates-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: NODE_VERSION: "22" jobs: audit-template-deps: name: Audit template Tailwind dependencies runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "${{ env.NODE_VERSION }}" - name: Verify every template declares @tailwindcss/cli + tailwindcss # Hugo >= 0.161.0 requires the npm @tailwindcss/cli package; the # standalone tailwindcss binary is no longer accepted. run: node scripts/check-template-deps.mjs build-templates: needs: audit-template-deps runs-on: ubuntu-latest strategy: fail-fast: false matrix: starter: - name: academic-cv path: templates/academic-cv - name: startup-landing-page path: templates/startup-landing-page - name: dev-portfolio path: templates/dev-portfolio steps: - name: Checkout uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 - name: Compute resources cache key id: reskey run: | set -euo pipefail P=${{ matrix.starter.path }} # Hash tracked files under assets/config and key files for invalidation HASH=$(git ls-files -s "$P/assets" "$P/config" "$P/hugoblox.yaml" "$P/package.json" 2>/dev/null | sha256sum | cut -d' ' -f1 || true) if [ -z "$HASH" ]; then HASH="nohash"; fi echo "key=${{ runner.os }}-hugo-resources-${{ matrix.starter.name }}-$HASH" >> $GITHUB_OUTPUT - name: Cache Hugo resources (starter) uses: actions/cache@v5 with: path: ${{ matrix.starter.path }}/resources/ key: ${{ steps.reskey.outputs.key }} restore-keys: | ${{ runner.os }}-hugo-resources-${{ matrix.starter.name }}- - name: Enable Corepack (for pnpm cache) run: corepack enable - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "${{ env.NODE_VERSION }}" cache: "pnpm" - name: Activate pnpm from root package.json run: pnpm --version - name: Install template dependencies working-directory: ${{ matrix.starter.path }} # Don't use --frozen-lockfile as starters have auto-generated package.json with simple deps (Tailwind CLI) # and may not have lock files checked into the repo run: pnpm install --no-frozen-lockfile - name: Verify @tailwindcss/cli resolves via node_modules # Hugo >= 0.161.0 invokes node @tailwindcss/cli/dist/index.mjs directly. # If require.resolve fails here, Hugo will fall back to PATH and emit # "binary tailwindcss is not a Node.js script" at build time. working-directory: ${{ matrix.starter.path }} run: node -e "require.resolve('@tailwindcss/cli/package.json')" - name: Read Hugo version from template id: hugo run: | set -euo pipefail P=${{ matrix.starter.path }} VERSION=$(grep -E "^\s*hugo_version:\s*['\"]?" "$P/hugoblox.yaml" | sed -E "s/.*hugo_version:\s*['\"]?([^'\"]+)['\"]?.*/\1/") if [ -z "$VERSION" ]; then echo "::error::Could not read hugo_version from $P/hugoblox.yaml" exit 1 fi echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Setup Hugo uses: peaceiris/actions-hugo@v3 with: hugo-version: "${{ steps.hugo.outputs.version }}" extended: true - name: Build ${{ matrix.starter.name }} working-directory: ${{ matrix.starter.path }} run: hugo --minify --panicOnWarning smoke-test-latest: name: Test site on latest Hugo (allowed to fail) needs: audit-template-deps runs-on: ubuntu-latest continue-on-error: true steps: - name: Checkout uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 - name: Enable Corepack (for pnpm cache) run: corepack enable - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "${{ env.NODE_VERSION }}" cache: "pnpm" - name: Install root dependencies run: pnpm install --no-frozen-lockfile - name: Install test dependencies working-directory: test run: pnpm install --no-frozen-lockfile - name: Verify @tailwindcss/cli resolves via node_modules working-directory: test run: node -e "require.resolve('@tailwindcss/cli/package.json')" - name: Cache Hugo resources (test) uses: actions/cache@v5 with: path: test/resources/ key: ${{ runner.os }}-hugo-resources-test-${{ hashFiles('test/assets/**/*', 'test/config.yaml', 'test/package.json') }} restore-keys: | ${{ runner.os }}-hugo-resources-test- - name: Setup Hugo (latest) uses: peaceiris/actions-hugo@v3 with: hugo-version: latest extended: true - name: Build test site (latest) working-directory: test run: hugo --minify --panicOnWarning --templateMetrics --templateMetricsHints canary-academic-latest: name: academic-cv on latest Hugo (allowed to fail) needs: audit-template-deps runs-on: ubuntu-latest continue-on-error: true steps: - name: Checkout uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 - name: Enable Corepack (for pnpm cache) run: corepack enable - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "${{ env.NODE_VERSION }}" cache: "pnpm" - name: Install template dependencies working-directory: templates/academic-cv run: pnpm install --no-frozen-lockfile - name: Verify @tailwindcss/cli resolves via node_modules working-directory: templates/academic-cv run: node -e "require.resolve('@tailwindcss/cli/package.json')" - name: Cache Hugo resources (template) uses: actions/cache@v5 with: path: templates/academic-cv/resources/ key: ${{ runner.os }}-hugo-resources-academic-latest-${{ hashFiles('templates/academic-cv/assets/**/*', 'templates/academic-cv/config/**/*', 'templates/academic-cv/hugoblox.yaml', 'templates/academic-cv/package.json') }} restore-keys: | ${{ runner.os }}-hugo-resources-academic-latest- - name: Setup Hugo (latest) uses: peaceiris/actions-hugo@v3 with: hugo-version: latest extended: true - name: Build academic-cv (latest) working-directory: templates/academic-cv run: hugo --minify --panicOnWarning smoke-test-min: name: Test site on minimum supported Hugo needs: audit-template-deps runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 with: submodules: true fetch-depth: 0 - name: Enable Corepack (for pnpm cache) run: corepack enable - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "${{ env.NODE_VERSION }}" cache: "pnpm" - name: Install root dependencies run: pnpm install --no-frozen-lockfile - name: Install test dependencies working-directory: test run: pnpm install --no-frozen-lockfile - name: Verify @tailwindcss/cli resolves via node_modules working-directory: test run: node -e "require.resolve('@tailwindcss/cli/package.json')" - name: Read minimum Hugo version from framework id: min run: | set -euo pipefail VERSION=$(grep -E "^[[:space:]]*min:[[:space:]]*['\"]?" modules/blox/hugo.yaml | head -n1 | sed -E "s/.*min:[[:space:]]*['\"]?([^'\"#]+).*/\\1/") if [ -z "$VERSION" ]; then echo "::error::Could not read module.hugoVersion.min from modules/blox/hugo.yaml" exit 1 fi echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Cache Hugo resources (test - min) uses: actions/cache@v5 with: path: test/resources/ key: ${{ runner.os }}-hugo-resources-test-min-${{ steps.min.outputs.version }}-${{ hashFiles('test/assets/**/*', 'test/config.yaml', 'test/package.json') }} restore-keys: | ${{ runner.os }}-hugo-resources-test-min- - name: Setup Hugo (min) uses: peaceiris/actions-hugo@v3 with: hugo-version: "${{ steps.min.outputs.version }}" extended: true - name: Build test site (min) working-directory: test run: hugo --minify --panicOnWarning --templateMetrics --templateMetricsHints