### **PR Type**
Enhancement, Security
___
### **Description**
- Re-enable Content Security Policy (CSP) for production
- Disable CSP headers in development environment
- Improve security configuration in Next.js
___
### Diagram Walkthrough
```mermaid
flowchart LR
A["Environment Check"] --> B{"Is Production?"}
B -->|Yes| C["Apply CSP"]
B -->|No| D["Skip CSP"]
C --> E["Apply Other Security Headers"]
D --> E
```
<details> <summary><h3> File Walkthrough</h3></summary>
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Security</strong></td><td><table>
<tr>
<td>
<details>
<summary><strong>next.config.js</strong><dd><code>Conditional CSP
application based on environment</code>
</dd></summary>
<hr>
dashboard/next.config.js
<ul><li>Added environment check for CSP application<br> <li> Re-enabled
Content-Security-Policy header<br> <li> Removed commented-out CSP
code</ul>
</details>
</td>
<td><a
href="https://github.com/nhost/nhost/pull/3439/files#diff-398ac9b04404f14166a89845539399764fecd520ad3e6f0119f8730c0eefa94a">+8/-4</a>
</td>
</tr>
</table></td></tr></tr></tbody></table>
</details>
___
215 lines
9.3 KiB
YAML
215 lines
9.3 KiB
YAML
name: Continuous Integration
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- 'assets/**'
|
|
- '**.md'
|
|
- 'LICENSE'
|
|
- 'docs/**'
|
|
pull_request:
|
|
types: [opened, synchronize]
|
|
paths-ignore:
|
|
- 'assets/**'
|
|
- '**.md'
|
|
- 'LICENSE'
|
|
- 'docs/**'
|
|
env:
|
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
TURBO_TEAM: nhost
|
|
NEXT_PUBLIC_ENV: dev
|
|
NEXT_TELEMETRY_DISABLED: 1
|
|
NHOST_TEST_DASHBOARD_URL: ${{ vars.NHOST_TEST_DASHBOARD_URL }}
|
|
NHOST_TEST_PROJECT_NAME: ${{ vars.NHOST_TEST_PROJECT_NAME }}
|
|
NHOST_TEST_ORGANIZATION_NAME: ${{ vars.NHOST_TEST_ORGANIZATION_NAME }}
|
|
NHOST_TEST_ORGANIZATION_SLUG: ${{ vars.NHOST_TEST_ORGANIZATION_SLUG }}
|
|
NHOST_TEST_PERSONAL_ORG_SLUG: ${{ vars.NHOST_TEST_PERSONAL_ORG_SLUG }}
|
|
NHOST_TEST_PROJECT_SUBDOMAIN: ${{ vars.NHOST_TEST_PROJECT_SUBDOMAIN }}
|
|
NHOST_PRO_TEST_PROJECT_NAME: ${{ vars.NHOST_PRO_TEST_PROJECT_NAME }}
|
|
NHOST_TEST_USER_EMAIL: ${{ secrets.NHOST_TEST_USER_EMAIL }}
|
|
NHOST_TEST_USER_PASSWORD: ${{ secrets.NHOST_TEST_USER_PASSWORD }}
|
|
NHOST_TEST_PROJECT_ADMIN_SECRET: '${{ secrets.NHOST_TEST_PROJECT_ADMIN_SECRET }}'
|
|
NHOST_TEST_FREE_USER_EMAILS: ${{ secrets.NHOST_TEST_FREE_USER_EMAILS }}
|
|
|
|
jobs:
|
|
build:
|
|
name: Build @nhost packages
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
with:
|
|
fetch-depth: 0
|
|
# * Install Node and dependencies. Package downloads will be cached for the next jobs.
|
|
- name: Install Node and dependencies
|
|
uses: ./.github/actions/install-dependencies
|
|
with:
|
|
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
|
BUILD: 'all'
|
|
- name: Check if the pnpm lockfile changed
|
|
id: changed-lockfile
|
|
uses: tj-actions/changed-files@v37
|
|
with:
|
|
files: pnpm-lock.yaml
|
|
# * Determine a pnpm filter argument for packages that have been modified.
|
|
# * If the lockfile has changed, we don't filter anything in order to run all the e2e tests.
|
|
- name: filter packages
|
|
id: filter-packages
|
|
if: steps.changed-lockfile.outputs.any_changed != 'true' && github.event_name == 'pull_request'
|
|
run: echo "filter=${{ format('--filter=...[origin/{0}]', github.base_ref) }}" >> $GITHUB_OUTPUT
|
|
# * List packagesthat has an `e2e` script, except the root, and return an array of their name and path
|
|
# * In a PR, only include packages that have been modified, and their dependencies
|
|
- name: List examples with an e2e script
|
|
id: set-matrix
|
|
run: |
|
|
PACKAGES=$(pnpm recursive list --depth -1 --parseable --filter='!nhost-root' ${{ steps.filter-packages.outputs.filter }} \
|
|
| xargs -I@ realpath --relative-to=$PWD @ \
|
|
| xargs -I@ jq "if (.scripts.e2e | length) != 0 then {name: .name, path: \"@\"} else null end" @/package.json \
|
|
| awk "!/null/" \
|
|
| jq -c --slurp 'map(select(length > 0))')
|
|
echo "matrix=$PACKAGES" >> $GITHUB_OUTPUT
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
|
|
unit:
|
|
name: Unit tests
|
|
needs: build
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
# * Install Node and dependencies. Package dependencies won't be downloaded again as they have been cached by the `build` job.
|
|
- name: Install Node and dependencies
|
|
uses: ./.github/actions/install-dependencies
|
|
with:
|
|
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
|
# * Run every `test` script in the workspace . Dependencies build is cached by Turborepo
|
|
- name: Run unit tests
|
|
run: pnpm run test:all
|
|
- name: Upload coverage to Codecov
|
|
uses: codecov/codecov-action@v3
|
|
with:
|
|
files: '**/coverage/coverage-final.json'
|
|
name: codecov-umbrella
|
|
- name: Create summary
|
|
run: |
|
|
echo '### Code coverage' >> $GITHUB_STEP_SUMMARY
|
|
echo 'Visit [codecov](https://app.codecov.io/gh/nhost/nhost/) to see the code coverage reports' >> $GITHUB_STEP_SUMMARY
|
|
|
|
lint:
|
|
name: Lint
|
|
needs: build
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
# * Install Node and dependencies. Package dependencies won't be downloaded again as they have been cached by the `build` job.
|
|
- name: Install Node and dependencies
|
|
uses: ./.github/actions/install-dependencies
|
|
with:
|
|
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
|
- name: Enforce Prettier formatting in dashboard
|
|
working-directory: ./dashboard
|
|
run: pnpm prettier --check "./**/*.tsx" --config prettier.config.js
|
|
# * Run every `lint` script in the workspace . Dependencies build is cached by Turborepo
|
|
- name: Lint
|
|
run: pnpm run lint:all
|
|
- name: Audit for vulnerabilities
|
|
run: pnpx audit-ci --config ./audit-ci.jsonc
|
|
|
|
e2e:
|
|
name: 'E2E (Package: ${{ matrix.package.path }})'
|
|
needs: build
|
|
if: ${{ needs.build.outputs.matrix != '[]' && needs.build.outputs.matrix != '' }}
|
|
strategy:
|
|
# * Don't cancel other matrices when one fails
|
|
fail-fast: false
|
|
matrix:
|
|
package: ${{ fromJson(needs.build.outputs.matrix) }}
|
|
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
# * Install Node and dependencies. Package dependencies won't be downloaded again as they have been cached by the `build` job.
|
|
- name: Install Node and dependencies
|
|
uses: ./.github/actions/install-dependencies
|
|
with:
|
|
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
|
|
TURBO_TEAM: ${{ env.TURBO_TEAM }}
|
|
# * Build Dashboard image to test it locally
|
|
- name: Build Dashboard local image
|
|
if: matrix.package.path == 'dashboard'
|
|
run: |
|
|
docker build -t nhost/dashboard:0.0.0-dev -f ${{ matrix.package.path }}/Dockerfile .
|
|
mkdir -p nhost-test-project
|
|
# * Install Nhost CLI if a `nhost/config.yaml` file is found
|
|
- name: Install Nhost CLI
|
|
if: hashFiles(format('{0}/nhost/config.yaml', matrix.package.path)) != '' && matrix.package.path != 'dashboard'
|
|
uses: ./.github/actions/nhost-cli
|
|
# * Install Nhost CLI to test Dashboard locally
|
|
- name: Install Nhost CLI (Local Dashboard tests)
|
|
timeout-minutes: 5
|
|
if: matrix.package.path == 'dashboard'
|
|
uses: ./.github/actions/nhost-cli
|
|
with:
|
|
init: 'true' # Initialize the application
|
|
start: 'true' # Start the application
|
|
path: ./nhost-test-project
|
|
wait: 'true' # Wait until the application is ready
|
|
dashboard-image: 'nhost/dashboard:0.0.0-dev'
|
|
- name: Fetch Dashboard Preview URL
|
|
id: fetch-dashboard-preview-url
|
|
uses: zentered/vercel-preview-url@v1.1.9
|
|
if: github.ref_name != 'main'
|
|
env:
|
|
VERCEL_TOKEN: ${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
|
|
GITHUB_REF: ${{ github.ref_name }}
|
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
with:
|
|
vercel_team_id: ${{ secrets.DASHBOARD_VERCEL_TEAM_ID }}
|
|
vercel_project_id: ${{ secrets.DASHBOARD_STAGING_VERCEL_PROJECT_ID }}
|
|
vercel_state: BUILDING,READY,INITIALIZING
|
|
- name: Set Dashboard Preview URL
|
|
if: steps.fetch-dashboard-preview-url.outputs.preview_url != ''
|
|
run: echo "NHOST_TEST_DASHBOARD_URL=https://${{ steps.fetch-dashboard-preview-url.outputs.preview_url }}" >> $GITHUB_ENV
|
|
- name: Run Onboarding Dashboard e2e tests
|
|
if: matrix.package.path == 'dashboard'
|
|
timeout-minutes: 10
|
|
run: pnpm --filter="${{ matrix.package.name }}" run e2e:onboarding
|
|
# * Run the `ci` script of the current package of the matrix. Dependencies build is cached by Turborepo
|
|
- name: Run e2e tests
|
|
timeout-minutes: 20
|
|
run: pnpm --filter="${{ matrix.package.name }}" run e2e
|
|
# * Run the `e2e-local` script of the dashboard
|
|
- name: Run Local Dashboard e2e tests
|
|
if: matrix.package.path == 'dashboard'
|
|
timeout-minutes: 5
|
|
run: pnpm --filter="${{ matrix.package.name }}" run e2e:local
|
|
|
|
- name: Stop Nhost CLI
|
|
if: matrix.package.path == 'dashboard'
|
|
working-directory: ./nhost-test-project
|
|
run: nhost down
|
|
- name: Stop Nhost CLI for packages
|
|
if: always() && (matrix.package.path == 'packages/hasura-auth-js' || matrix.package.path == 'packages/hasura-storage-js')
|
|
working-directory: ./${{ matrix.package.path }}
|
|
run: nhost down
|
|
|
|
- name: Encrypt Playwright report
|
|
if: ${{ failure() && hashFiles('dashboard/playwright-report/**') != ''}}
|
|
run: |
|
|
tar -czf dashboard/playwright-report.tar.gz dashboard/playwright-report/
|
|
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \
|
|
-in dashboard/playwright-report.tar.gz \
|
|
-out playwright-report.tar.gz.enc \
|
|
-k "${{ secrets.PLAYWRIGHT_REPORT_ENCRYPTION_KEY }}"
|
|
rm dashboard/playwright-report.tar.gz
|
|
|
|
- name: Upload encrypted Playwright report
|
|
uses: actions/upload-artifact@v4
|
|
if: ${{ failure() && hashFiles('dashboard/playwright-report/**') != ''}}
|
|
with:
|
|
name: encrypted-playwright-report-${{ github.run_id }}
|
|
path: playwright-report.tar.gz.enc
|
|
retention-days: 1
|