name: Reusable build and test app for production on: workflow_call: inputs: node-version: required: true type: string skip-e2e-test: type: boolean secrets: SLACK_WEBHOOK_URL: required: true workflow_dispatch: inputs: node-version: required: true type: string default: 24.x skip-e2e-test: type: boolean default: false secrets: SLACK_WEBHOOK_URL: required: true jobs: build-prod: runs-on: ubuntu-latest outputs: PROD_FILES: ${{ steps.archive-prod-files.outputs.file }} steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} cache: 'pnpm' - name: Install turbo run: | pnpm add turbo --global - name: Install dependencies run: | pnpm install --frozen-lockfile - name: Build working-directory: ./apps/app run: | turbo run build --env-mode=loose env: ANALYZE: 1 - name: Assemble production artifacts run: bash apps/app/bin/assemble-prod.sh - name: Check for broken symlinks in .next/node_modules run: bash apps/app/bin/check-next-symlinks.sh - name: Archive production files id: archive-prod-files run: | tar -zcf production.tar.gz --exclude ./apps/app/.next/cache \ package.json \ node_modules \ apps/app/.next \ apps/app/config \ apps/app/dist \ apps/app/public \ apps/app/resource \ apps/app/tmp \ apps/app/.env.production* \ apps/app/node_modules \ apps/app/next.config.js \ apps/app/package.json echo "file=production.tar.gz" >> $GITHUB_OUTPUT - name: Upload production files as artifact uses: actions/upload-artifact@v4 with: name: Production Files (node${{ inputs.node-version }}) path: ${{ steps.archive-prod-files.outputs.file }} - name: Upload report as artifact uses: actions/upload-artifact@v4 with: name: Bundle Analyzing Report (node${{ inputs.node-version }}) path: | apps/app/.next/analyze - name: Slack Notification uses: weseek/ghaction-slack-notification@master if: failure() with: type: ${{ job.status }} job_name: '*Node CI for growi - build-prod (${{ inputs.node-version }})*' channel: '#ci' isCompactMode: true url: ${{ secrets.SLACK_WEBHOOK_URL }} launch-prod: needs: [build-prod] runs-on: ubuntu-latest strategy: matrix: mongodb-version: ['6.0', '8.0'] services: mongodb: image: mongo:${{ matrix.mongodb-version }} ports: - 27017/tcp elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:9.0.1 ports: - 9200/tcp env: discovery.type: single-node steps: - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} - name: Download production files artifact uses: actions/download-artifact@v4 with: name: Production Files (node${{ inputs.node-version }}) - name: Extract production files run: | tar -xf ${{ needs.build-prod.outputs.PROD_FILES }} # Run after extraction so pnpm/action-setup@v4 can read packageManager from package.json - uses: pnpm/action-setup@v4 - name: pnpm run server:ci working-directory: ./apps/app run: | cp config/ci/.env.local.for-ci .env.production.local pnpm run server:ci env: MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi - name: Slack Notification uses: weseek/ghaction-slack-notification@master if: failure() with: type: ${{ job.status }} job_name: '*Node CI for growi - build-prod (${{ inputs.node-version }})*' channel: '#ci' isCompactMode: true url: ${{ secrets.SLACK_WEBHOOK_URL }} run-playwright: needs: [build-prod] if: | github.event_name == 'workflow_dispatch' || (!inputs.skip-e2e-test && startsWith(github.head_ref, 'mergify/merge-queue/')) runs-on: ubuntu-latest container: # Match the Playwright version # https://github.com/microsoft/playwright/issues/20010 image: mcr.microsoft.com/playwright:v1.58.2-jammy strategy: fail-fast: false matrix: browser: [chromium, firefox, webkit] shard: [1/2, 2/2] mongodb-version: ['6.0', '8.0'] services: mongodb: image: mongo:${{ matrix.mongodb-version }} ports: - 27017/tcp elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:9.0.1 ports: - 9200/tcp env: discovery.type: single-node steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} cache: 'pnpm' - name: Install dependencies run: | pnpm install --frozen-lockfile - name: Install Playwright browsers run: | pnpm playwright install --with-deps ${{ matrix.browser }} - name: Download production files artifact uses: actions/download-artifact@v4 with: name: Production Files (node${{ inputs.node-version }}) - name: Extract production files to isolated directory run: | mkdir -p /tmp/growi-prod tar -xf ${{ needs.build-prod.outputs.PROD_FILES }} -C /tmp/growi-prod - name: Copy dotenv file for ci run: | cat apps/app/config/ci/.env.local.for-ci >> /tmp/growi-prod/apps/app/.env.production.local - name: Playwright Run (--project=chromium/installer) if: ${{ matrix.browser == 'chromium' }} working-directory: ./apps/app run: | pnpm playwright test --project=chromium/installer env: DEBUG: pw:api HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500 GROWI_WEBSERVER_COMMAND: 'cd /tmp/growi-prod/apps/app && pnpm run server' MONGO_URI: mongodb://mongodb:27017/growi-playwright-installer ELASTICSEARCH_URI: http://elasticsearch:9200/growi - name: Copy dotenv file for automatic installation run: | cat apps/app/config/ci/.env.local.for-auto-install >> /tmp/growi-prod/apps/app/.env.production.local - name: Playwright Run working-directory: ./apps/app run: | pnpm playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }} env: DEBUG: pw:api HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500 GROWI_WEBSERVER_COMMAND: 'cd /tmp/growi-prod/apps/app && pnpm run server' MONGO_URI: mongodb://mongodb:27017/growi-playwright ELASTICSEARCH_URI: http://elasticsearch:9200/growi - name: Copy dotenv file for automatic installation with allowing guest mode run: | cat apps/app/config/ci/.env.local.for-auto-install-with-allowing-guest >> /tmp/growi-prod/apps/app/.env.production.local - name: Playwright Run (--project=${browser}/guest-mode) working-directory: ./apps/app run: | pnpm playwright test --project=${{ matrix.browser }}/guest-mode --shard=${{ matrix.shard }} env: DEBUG: pw:api HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500 GROWI_WEBSERVER_COMMAND: 'cd /tmp/growi-prod/apps/app && pnpm run server' MONGO_URI: mongodb://mongodb:27017/growi-playwright-guest-mode ELASTICSEARCH_URI: http://elasticsearch:9200/growi - name: Generate shard ID id: shard-id if: always() run: | SHARD_ID=$(echo "${{ matrix.shard }}" | tr '/' '-') echo "shard_id=${SHARD_ID}" >> $GITHUB_OUTPUT - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: name: blob-report-${{ matrix.browser }}-mongo${{ matrix.mongodb-version }}-${{ steps.shard-id.outputs.shard_id }} path: ./apps/app/blob-report retention-days: 30 - name: Slack Notification uses: weseek/ghaction-slack-notification@master if: failure() with: type: ${{ job.status }} job_name: '*Node CI for growi - run-playwright (${{ matrix.browser }}, MongoDB ${{ matrix.mongodb-version }})*' channel: '#ci' isCompactMode: true url: ${{ secrets.SLACK_WEBHOOK_URL }} report-playwright: needs: [run-playwright] if: always() && needs.run-playwright.result != 'skipped' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} cache: 'pnpm' - name: Install dependencies run: | pnpm install --frozen-lockfile - name: Download blob reports uses: actions/download-artifact@v4 with: pattern: blob-report-* path: all-blob-reports merge-multiple: true - name: Merge into HTML Report run: | mkdir -p playwright-report if [ -z "$(ls all-blob-reports/*.zip all-blob-reports/*.blob 2>/dev/null || true)" ]; then echo "
This could be because tests were skipped or all artifacts were not available.
" > playwright-report/index.html else pnpm playwright merge-reports --reporter html all-blob-reports fi - name: Upload HTML report uses: actions/upload-artifact@v4 with: name: html-report path: playwright-report retention-days: 30