diff --git a/.github/workflows/ci-internal-apps.yaml b/.github/workflows/ci-internal-apps.yaml new file mode 100644 index 0000000000..6b5c41491e --- /dev/null +++ b/.github/workflows/ci-internal-apps.yaml @@ -0,0 +1,157 @@ +name: CI Internal Apps + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + discover: + timeout-minutes: 5 + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + has_apps: ${{ steps.set-matrix.outputs.has_apps }} + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + + - name: Ensure no .github folder inside app folders + run: | + offenders=$(find packages/twenty-apps/internal -mindepth 2 -maxdepth 2 -type d -name .github) + if [ -n "$offenders" ]; then + echo "::error::Internal apps must not define their own .github folder. Offenders:" + echo "$offenders" + exit 1 + fi + + - name: Detect changed files + id: changed-files + if: github.event_name != 'workflow_dispatch' + uses: tj-actions/changed-files@48d8f15b2aaa3d255ca5af3eba4870f807ce6b3c # v45.0.9 + with: + json: true + escape_json: false + files: packages/twenty-apps/internal/** + + - name: Build matrix of changed apps + id: set-matrix + env: + EVENT_NAME: ${{ github.event_name }} + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + node <<'NODE' + const fs = require('fs'); + const path = require('path'); + const root = 'packages/twenty-apps/internal'; + // CI_EXCLUDED_APPLICATIONS is temporary: it skips applications from CI to + // keep this PR small while the remaining apps are made CI-ready. Remove an + // app from this list once its checks pass, and delete the list entirely + // once every application is covered. + const CI_EXCLUDED_APPLICATIONS = ['call-recording', 'exa', 'people-data-labs', 'self-hosting', 'twenty-discord', 'twenty-fireflies', 'twenty-for-twenty', 'twenty-last-contact', 'twenty-linear', 'twenty-meeting-bot', 'twenty-partners', 'twenty-slack']; + const rootNodeVersion = fs.existsSync('.nvmrc') ? fs.readFileSync('.nvmrc', 'utf8').trim() : '24'; + const eventName = process.env.EVENT_NAME; + const changedFiles = JSON.parse(process.env.CHANGED_FILES || '[]'); + const changedApps = new Set(); + for (const file of changedFiles) { + const match = file.match(/^packages\/twenty-apps\/internal\/([^/]+)\//); + if (match) changedApps.add(match[1]); + } + const matrix = fs.readdirSync(root, { withFileTypes: true }) + .filter((entry) => entry.isDirectory() && entry.name !== 'node_modules') + .map((entry) => entry.name) + .filter((name) => fs.existsSync(path.join(root, name, 'package.json'))) + .filter((name) => !CI_EXCLUDED_APPLICATIONS.includes(name)) + .filter((name) => eventName === 'workflow_dispatch' || changedApps.has(name)) + .map((name) => { + const appPath = path.join(root, name); + const scripts = JSON.parse(fs.readFileSync(path.join(appPath, 'package.json'), 'utf8')).scripts || {}; + const nvmrc = path.join(appPath, '.nvmrc'); + return { + name, + path: appPath, + nodeVersion: fs.existsSync(nvmrc) ? fs.readFileSync(nvmrc, 'utf8').trim() : rootNodeVersion, + hasTypecheck: Boolean(scripts.typecheck), + hasUnit: Boolean(scripts['test:unit']), + hasIntegration: Boolean(scripts.test), + }; + }); + fs.appendFileSync(process.env.GITHUB_OUTPUT, `matrix=${JSON.stringify(matrix)}\n`); + fs.appendFileSync(process.env.GITHUB_OUTPUT, `has_apps=${matrix.length > 0}\n`); + NODE + + ci: + needs: discover + if: needs.discover.outputs.has_apps == 'true' + name: ${{ matrix.app.name }} + timeout-minutes: 30 + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + app: ${{ fromJSON(needs.discover.outputs.matrix) }} + defaults: + run: + working-directory: ${{ matrix.app.path }} + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Enable Corepack + run: corepack enable + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: ${{ matrix.app.nodeVersion }} + cache: yarn + cache-dependency-path: ${{ matrix.app.path }}/yarn.lock + + - name: Install dependencies + run: yarn install --immutable + + - name: Lint + run: yarn lint + + - name: Typecheck + if: matrix.app.hasTypecheck + run: yarn typecheck + + - name: Unit tests + if: matrix.app.hasUnit + run: yarn test:unit + + - name: Spawn Twenty test instance + id: twenty + if: matrix.app.hasIntegration + uses: ./.github/actions/spawn-twenty-app-dev-test + with: + twenty-version: latest + + - name: Integration tests + if: matrix.app.hasIntegration + run: yarn test + env: + TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} + TWENTY_API_KEY: ${{ steps.twenty.outputs.api-key }} + + ci-internal-apps-status-check: + if: always() && !cancelled() + timeout-minutes: 5 + runs-on: ubuntu-latest + needs: [discover, ci] + steps: + - name: Fail job if any needs failed + if: contains(needs.*.result, 'failure') + run: exit 1 diff --git a/packages/create-twenty-app/src/constants/template/README.md b/packages/create-twenty-app/src/constants/template/README.md index 4728d9ffa0..ca0f57eeae 100644 --- a/packages/create-twenty-app/src/constants/template/README.md +++ b/packages/create-twenty-app/src/constants/template/README.md @@ -13,6 +13,9 @@ Run `yarn twenty help` to list all available commands. - `yarn twenty dev` - Start the development server and sync your app - `yarn twenty docker:status` - Check the local Twenty server status - `yarn twenty docker:start` - Start the local Twenty server +- `yarn lint` - Lint the codebase +- `yarn typecheck` - Type-check the codebase +- `yarn test:unit` - Run unit tests - `yarn test` - Run integration tests ## Learn More diff --git a/packages/create-twenty-app/src/constants/template/github/workflows/ci.yml b/packages/create-twenty-app/src/constants/template/github/workflows/ci.yml index 9112ecb495..28104ffadc 100644 --- a/packages/create-twenty-app/src/constants/template/github/workflows/ci.yml +++ b/packages/create-twenty-app/src/constants/template/github/workflows/ci.yml @@ -23,12 +23,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Spawn Twenty test instance - id: twenty - uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main - with: - twenty-version: ${{ env.TWENTY_VERSION }} - - name: Enable Corepack run: corepack enable @@ -41,6 +35,21 @@ jobs: - name: Install dependencies run: yarn install --immutable + - name: Lint + run: yarn lint + + - name: Typecheck + run: yarn typecheck + + - name: Run unit tests + run: yarn test:unit + + - name: Spawn Twenty test instance + id: twenty + uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main + with: + twenty-version: ${{ env.TWENTY_VERSION }} + - name: Run integration tests run: yarn test env: diff --git a/packages/create-twenty-app/src/constants/template/package.json b/packages/create-twenty-app/src/constants/template/package.json index a41cd81902..58dbc8b260 100644 --- a/packages/create-twenty-app/src/constants/template/package.json +++ b/packages/create-twenty-app/src/constants/template/package.json @@ -13,7 +13,9 @@ "twenty": "twenty", "lint": "oxlint -c .oxlintrc.json .", "lint:fix": "oxlint --fix -c .oxlintrc.json .", + "typecheck": "tsc --noEmit -p tsconfig.spec.json", "test": "vitest run", + "test:unit": "vitest run --config vitest.unit.config.ts --passWithNoTests", "test:watch": "vitest" }, "dependencies": {}, diff --git a/packages/create-twenty-app/src/constants/template/vitest.unit.config.ts b/packages/create-twenty-app/src/constants/template/vitest.unit.config.ts new file mode 100644 index 0000000000..e0d140015b --- /dev/null +++ b/packages/create-twenty-app/src/constants/template/vitest.unit.config.ts @@ -0,0 +1,14 @@ +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + tsconfigPaths({ + projects: ['tsconfig.spec.json'], + ignoreConfigErrors: true, + }), + ], + test: { + include: ['src/**/*.test.ts'], + }, +}); diff --git a/packages/twenty-apps/internal/self-hosting/.github/workflows/cd.yml b/packages/twenty-apps/internal/self-hosting/.github/workflows/cd.yml deleted file mode 100644 index 61cbcfd7ab..0000000000 --- a/packages/twenty-apps/internal/self-hosting/.github/workflows/cd.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: CD - -on: - push: - branches: - - main - pull_request: - types: [labeled] - -permissions: - contents: read - -env: - TWENTY_DEPLOY_URL: http://localhost:3000 - -concurrency: - group: cd-${{ github.ref }} - cancel-in-progress: true - -jobs: - deploy-and-install: - if: >- - github.event_name == 'push' || - (github.event_name == 'pull_request' && github.event.label.name == 'deploy') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Deploy - uses: twentyhq/twenty/.github/actions/deploy-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} - - - name: Install - uses: twentyhq/twenty/.github/actions/install-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} diff --git a/packages/twenty-apps/internal/self-hosting/.github/workflows/ci.yml b/packages/twenty-apps/internal/self-hosting/.github/workflows/ci.yml deleted file mode 100644 index 9112ecb495..0000000000 --- a/packages/twenty-apps/internal/self-hosting/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: - - main - pull_request: {} - -permissions: - contents: read - -env: - TWENTY_VERSION: latest - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Spawn Twenty test instance - id: twenty - uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main - with: - twenty-version: ${{ env.TWENTY_VERSION }} - - - name: Enable Corepack - run: corepack enable - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - - - name: Install dependencies - run: yarn install --immutable - - - name: Run integration tests - run: yarn test - env: - TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} - TWENTY_API_KEY: ${{ steps.twenty.outputs.api-key }} diff --git a/packages/twenty-apps/internal/twenty-for-twenty/.github/workflows/cd.yml b/packages/twenty-apps/internal/twenty-for-twenty/.github/workflows/cd.yml deleted file mode 100644 index 61cbcfd7ab..0000000000 --- a/packages/twenty-apps/internal/twenty-for-twenty/.github/workflows/cd.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: CD - -on: - push: - branches: - - main - pull_request: - types: [labeled] - -permissions: - contents: read - -env: - TWENTY_DEPLOY_URL: http://localhost:3000 - -concurrency: - group: cd-${{ github.ref }} - cancel-in-progress: true - -jobs: - deploy-and-install: - if: >- - github.event_name == 'push' || - (github.event_name == 'pull_request' && github.event.label.name == 'deploy') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Deploy - uses: twentyhq/twenty/.github/actions/deploy-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} - - - name: Install - uses: twentyhq/twenty/.github/actions/install-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} diff --git a/packages/twenty-apps/internal/twenty-for-twenty/.github/workflows/ci.yml b/packages/twenty-apps/internal/twenty-for-twenty/.github/workflows/ci.yml deleted file mode 100644 index 9112ecb495..0000000000 --- a/packages/twenty-apps/internal/twenty-for-twenty/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: - - main - pull_request: {} - -permissions: - contents: read - -env: - TWENTY_VERSION: latest - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Spawn Twenty test instance - id: twenty - uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main - with: - twenty-version: ${{ env.TWENTY_VERSION }} - - - name: Enable Corepack - run: corepack enable - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - - - name: Install dependencies - run: yarn install --immutable - - - name: Run integration tests - run: yarn test - env: - TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} - TWENTY_API_KEY: ${{ steps.twenty.outputs.api-key }} diff --git a/packages/twenty-apps/internal/twenty-meeting-bot/.github/workflows/cd.yml b/packages/twenty-apps/internal/twenty-meeting-bot/.github/workflows/cd.yml deleted file mode 100644 index 0ec483fc57..0000000000 --- a/packages/twenty-apps/internal/twenty-meeting-bot/.github/workflows/cd.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: CD - -on: - push: - branches: - - main - pull_request: - types: [labeled] - -permissions: - contents: read - -env: - TWENTY_DEPLOY_URL: http://localhost:2020 - -concurrency: - group: cd-${{ github.ref }} - cancel-in-progress: true - -jobs: - deploy-and-install: - if: >- - github.event_name == 'push' || - (github.event_name == 'pull_request' && github.event.label.name == 'deploy') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Deploy - uses: twentyhq/twenty/.github/actions/deploy-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} - - - name: Install - uses: twentyhq/twenty/.github/actions/install-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} diff --git a/packages/twenty-apps/internal/twenty-meeting-bot/.github/workflows/ci.yml b/packages/twenty-apps/internal/twenty-meeting-bot/.github/workflows/ci.yml deleted file mode 100644 index 9112ecb495..0000000000 --- a/packages/twenty-apps/internal/twenty-meeting-bot/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: - - main - pull_request: {} - -permissions: - contents: read - -env: - TWENTY_VERSION: latest - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Spawn Twenty test instance - id: twenty - uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main - with: - twenty-version: ${{ env.TWENTY_VERSION }} - - - name: Enable Corepack - run: corepack enable - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - - - name: Install dependencies - run: yarn install --immutable - - - name: Run integration tests - run: yarn test - env: - TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} - TWENTY_API_KEY: ${{ steps.twenty.outputs.api-key }} diff --git a/packages/twenty-apps/internal/twenty-partners/.github/workflows/cd.yml b/packages/twenty-apps/internal/twenty-partners/.github/workflows/cd.yml deleted file mode 100644 index 0ec483fc57..0000000000 --- a/packages/twenty-apps/internal/twenty-partners/.github/workflows/cd.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: CD - -on: - push: - branches: - - main - pull_request: - types: [labeled] - -permissions: - contents: read - -env: - TWENTY_DEPLOY_URL: http://localhost:2020 - -concurrency: - group: cd-${{ github.ref }} - cancel-in-progress: true - -jobs: - deploy-and-install: - if: >- - github.event_name == 'push' || - (github.event_name == 'pull_request' && github.event.label.name == 'deploy') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Deploy - uses: twentyhq/twenty/.github/actions/deploy-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} - - - name: Install - uses: twentyhq/twenty/.github/actions/install-twenty-app@main - with: - api-url: ${{ env.TWENTY_DEPLOY_URL }} - api-key: ${{ secrets.TWENTY_DEPLOY_API_KEY }} diff --git a/packages/twenty-apps/internal/twenty-partners/.github/workflows/ci.yml b/packages/twenty-apps/internal/twenty-partners/.github/workflows/ci.yml deleted file mode 100644 index 9112ecb495..0000000000 --- a/packages/twenty-apps/internal/twenty-partners/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: - - main - pull_request: {} - -permissions: - contents: read - -env: - TWENTY_VERSION: latest - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Spawn Twenty test instance - id: twenty - uses: twentyhq/twenty/.github/actions/spawn-twenty-app-dev-test@main - with: - twenty-version: ${{ env.TWENTY_VERSION }} - - - name: Enable Corepack - run: corepack enable - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: yarn - - - name: Install dependencies - run: yarn install --immutable - - - name: Run integration tests - run: yarn test - env: - TWENTY_API_URL: ${{ steps.twenty.outputs.server-url }} - TWENTY_API_KEY: ${{ steps.twenty.outputs.api-key }}