Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 26 additions & 46 deletions .github/scripts/ciScript.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
const isTestFile = (file) => /\.(test|spec)\.[jt]sx?$/.test(file);

const deriveTestFiles = (files) => {
return files.map((file) => {
if (isTestFile(file)) return file;

const withoutExt = file.replace(/\.[jt]sx?$/, '');
const parts = withoutExt.split('/');
const baseName = parts[parts.length - 1];
const dir = parts.slice(0, -1).join('/');

return `${dir}/__tests__/${baseName}.test.ts`;
});
};

module.exports = async ({ github, context, core }) => {
const owner = context.repo.owner;
const repo = context.repo.repo;
Expand All @@ -6,7 +21,6 @@ module.exports = async ({ github, context, core }) => {
const prState = pr.state;

const backendFiles = [];
const backendTests = [];
const mobileFiles = [];
const webFiles = [];

Expand Down Expand Up @@ -34,60 +48,26 @@ module.exports = async ({ github, context, core }) => {

if (fileName.startsWith('apps/backend/')) {
backendFiles.push(fileName);

const relative = fileName.replace('apps/backend/src/', '');
const baseName = relative
.split('/')
.pop()
?.replace(/\.(ts|tsx|js|jsx)$/, '');

if (baseName) {
backendTests.push(`src/__tests__/${baseName}.test.ts`);
}

} else if (fileName.startsWith('apps/mobile/')) {
mobileFiles.push(fileName);
} else if (fileName.startsWith('apps/web/')) {
webFiles.push(fileName);
}
});

console.log({
backendFiles,
backendTests,
mobileFiles,
webFiles,
});
const strippedBackend = backendFiles.map(f => f.replace('apps/backend/', ''));
const strippedMobile = mobileFiles.map(f => f.replace('apps/mobile/', ''));

core.setOutput(
"backendFiles",
backendFiles
.map(file => file.replace("apps/backend/", ""))
.join(" ")
);

core.setOutput(
"backendTests",
[...new Set(backendTests)].join(" ")
);

core.setOutput(
"mobileFiles",
mobileFiles
.map(file => file.replace("apps/mobile/", ""))
.join(" ")
);

core.setOutput(
"webFiles",
webFiles
.map(file => file.replace("apps/web/", ""))
.join(" ")
);
console.log({ backendFiles, mobileFiles, webFiles });

core.setOutput("backendChanged", backendFiles.length > 0);
core.setOutput("mobileChanged", mobileFiles.length > 0);
core.setOutput("webChanged", webFiles.length > 0);
core.setOutput('backendFiles', strippedBackend.join(' '));
core.setOutput('mobileFiles', strippedMobile.join(' '));
core.setOutput('webFiles', webFiles.map(f => f.replace('apps/web/', '')).join(' '));
core.setOutput('backendTestFiles', deriveTestFiles(strippedBackend).join(' '));
core.setOutput('mobileTestFiles', deriveTestFiles(strippedMobile).join(' '));
core.setOutput('backendChanged', backendFiles.length > 0);
core.setOutput('mobileChanged', mobileFiles.length > 0);
core.setOutput('webChanged', webFiles.length > 0);

} catch (error) {
console.error(error);
Expand Down
69 changes: 34 additions & 35 deletions .github/scripts/commentResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,60 @@ module.exports = async ({
mobileLint,
mobileTest,
webCheck,
webBuild
webBuild,
backendLintOutput,
mobileLintOutput,
}) => {
const owner = context.repo.owner;
const repo = context.repo.repo;
const prNumber = context.payload.pull_request.number;

const emoji = (status) => {
if (status === 'success') return '';
if (status === 'failure') return '';
if (status === 'skipped') return '⏭️';
return '';
const status = (s) => {
if (s === 'success') return 'PASS';
if (s === 'failure') return 'FAIL';
if (s === 'skipped') return 'SKIP';
return '-';
};

const label = (status) => {
if (!status) return '⚪ unknown';
return `${emoji(status)} ${status}`;
const lintDetails = (output) => {
if (!output || !output.trim()) return '';
return `\n<details>\n<summary>View lint errors</summary>\n\n\`\`\`\n${output.trim()}\n\`\`\`\n</details>`;
};

const anyFailure = [
backend,
mobile,
web
].includes('failure');

const title = anyFailure
? '❌ Some checks failed'
: '✅ CI completed';

const anyFailure = [backend, mobile, web].includes('failure');
const title = anyFailure ? 'CI — Checks Failed' : 'CI — All Checks Passed';
const timestamp = new Date().toUTCString();

const body = `## CI Results — ${title}
const body = `## ${title}

### Backend — ${status(backend)}

### 🖥️ Backend (${label(backend)})
| Check | Status |
| Check | Result |
|---|---|
| Lint | ${label(backendLint)} |
| Test | ${label(backendTest)} |
| Typecheck | ${label(backendTypecheck)} |
| Lint | ${status(backendLint)} |
| Test | ${status(backendTest)} |
| Typecheck | ${status(backendTypecheck)} |
${backendLint === 'failure' ? lintDetails(backendLintOutput) : ''}

### 📱 Mobile (${label(mobile)})
| Check | Status |
### Mobile — ${status(mobile)}

| Check | Result |
|---|---|
| Lint | ${label(mobileLint)} |
| Test | ${label(mobileTest)} |
| Lint | ${status(mobileLint)} |
| Test | ${status(mobileTest)} |
${mobileLint === 'failure' ? lintDetails(mobileLintOutput) : ''}

### Web — ${status(web)}

### 🌐 Web (${label(web)})
| Check | Status |
| Check | Result |
|---|---|
| Check | ${label(webCheck)} |
| Build | ${label(webBuild)} |
| Check | ${status(webCheck)} |
| Build | ${status(webBuild)} |

---
🕐 Last updated: \`${timestamp}\``;
Last updated: \`${timestamp}\``;

const COMMENT_MARKER = '## CI Results —';
const COMMENT_MARKER = '## CI —';

try {
const comments = await github.paginate(
Expand Down
96 changes: 55 additions & 41 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,20 @@ jobs:
runs-on: ubuntu-latest

outputs:
backendChanged: ${{ steps.detect.outputs.backendChanged }}
mobileChanged: ${{ steps.detect.outputs.mobileChanged }}
webChanged: ${{ steps.detect.outputs.webChanged }}
backendFiles: ${{ steps.detect.outputs.backendFiles }}
mobileFiles: ${{ steps.detect.outputs.mobileFiles }}
webFiles: ${{ steps.detect.outputs.webFiles }}
backendTests: ${{ steps.detect.outputs.backendTests }}
backendChanged: ${{ steps.detect.outputs.backendChanged }}
mobileChanged: ${{ steps.detect.outputs.mobileChanged }}
webChanged: ${{ steps.detect.outputs.webChanged }}
backendFiles: ${{ steps.detect.outputs.backendFiles }}
mobileFiles: ${{ steps.detect.outputs.mobileFiles }}
webFiles: ${{ steps.detect.outputs.webFiles }}
backendTestFiles: ${{ steps.detect.outputs.backendTestFiles }}
mobileTestFiles: ${{ steps.detect.outputs.mobileTestFiles }}

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}


- name: Detect changed files
id: detect
Expand All @@ -37,13 +41,15 @@ jobs:
if: needs.detect-changes.outputs.backendChanged == 'true'
runs-on: ubuntu-latest

outputs:
backend_lint: ${{ steps.backend_lint.outcome }}
backend_test: ${{ steps.backend_test.outcome }}
backend_typecheck: ${{ steps.backend_typecheck.outcome }}
outputs:
lint_result: ${{ steps.backend_lint.outcome }}
test_result: ${{ steps.backend_test.outcome }}
typecheck_result: ${{ steps.backend_typecheck.outcome }}

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
Expand All @@ -55,21 +61,25 @@ jobs:

- name: Backend lint
id: backend_lint
continue-on-error: true
continue-on-error: true
run: cd apps/backend && pnpm eslint ${{ needs.detect-changes.outputs.backendFiles }}

- name: Backend test
id: backend_test
if: needs.detect-changes.outputs.backendTestFiles != ''
continue-on-error: true
run: cd apps/backend && pnpm test ${{ needs.detect-changes.outputs.backendTests }}
run: cd apps/backend && pnpm test --passWithNoTests ${{ needs.detect-changes.outputs.backendTestFiles }}

- name: Backend typecheck
id: backend_typecheck
continue-on-error: true
run: cd apps/backend && pnpm typecheck ${{ needs.detect-changes.outputs.backendFiles }}
run: cd apps/backend && pnpm typecheck

- name: Fail backend if checks failed
if: steps.backend_lint.outcome == 'failure' || steps.backend_test.outcome == 'failure' || steps.backend_typecheck.outcome == 'failure'
- name: Fail job if any check failed
if: >
steps.backend_lint.outcome == 'failure' ||
steps.backend_test.outcome == 'failure' ||
steps.backend_typecheck.outcome == 'failure'
run: exit 1

web-ci:
Expand All @@ -78,11 +88,13 @@ jobs:
runs-on: ubuntu-latest

outputs:
web_check: ${{ steps.web_check.outcome }}
web_build: ${{ steps.web_build.outcome }}
check_result: ${{ steps.web_check.outcome }}
build_result: ${{ steps.web_build.outcome }}

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
Expand All @@ -102,8 +114,10 @@ jobs:
continue-on-error: true
run: cd apps/web && pnpm build

- name: Fail web if checks failed
if: steps.web_check.outcome == 'failure' || steps.web_build.outcome == 'failure'
- name: Fail job if any check failed
if: >
steps.web_check.outcome == 'failure' ||
steps.web_build.outcome == 'failure'
run: exit 1

mobile-ci:
Expand All @@ -112,11 +126,13 @@ jobs:
runs-on: ubuntu-latest

outputs:
mobile_lint: ${{ steps.mobile_lint.outcome }}
mobile_test: ${{ steps.mobile_test.outcome }}
lint_result: ${{ steps.mobile_lint.outcome }}
test_result: ${{ steps.mobile_test.outcome }}

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
Expand All @@ -133,11 +149,14 @@ jobs:

- name: Mobile test
id: mobile_test
if: needs.detect-changes.outputs.mobileTestFiles != ''
continue-on-error: true
run: cd apps/mobile && pnpm test
run: cd apps/mobile && pnpm test --passWithNoTests ${{ needs.detect-changes.outputs.mobileTestFiles }}

- name: Fail mobile if checks failed
if: steps.mobile_lint.outcome == 'failure' || steps.mobile_test.outcome == 'failure'
- name: Fail job if any check failed
if: >
steps.mobile_lint.outcome == 'failure' ||
steps.mobile_test.outcome == 'failure'
run: exit 1

comment-results:
Expand All @@ -157,22 +176,17 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const script = require('./.github/scripts/commentResults.js');

await script({
github,
context,

backend: '${{ needs.backend-ci.result }}',
web: '${{ needs.web-ci.result }}',
mobile: '${{ needs.mobile-ci.result }}',

backendLint: '${{ needs.backend-ci.outputs.backend_lint }}',
backendTest: '${{ needs.backend-ci.outputs.backend_test }}',
backendTypecheck: '${{ needs.backend-ci.outputs.backend_typecheck }}',

mobileLint: '${{ needs.mobile-ci.outputs.mobile_lint }}',
mobileTest: '${{ needs.mobile-ci.outputs.mobile_test }}',

webCheck: '${{ needs.web-ci.outputs.web_check }}',
webBuild: '${{ needs.web-ci.outputs.web_build }}'
});
backend: '${{ needs.backend-ci.result }}',
web: '${{ needs.web-ci.result }}',
mobile: '${{ needs.mobile-ci.result }}',
backendLint: '${{ needs.backend-ci.outputs.lint_result }}',
backendTest: '${{ needs.backend-ci.outputs.test_result }}',
backendTypecheck: '${{ needs.backend-ci.outputs.typecheck_result }}',
webCheck: '${{ needs.web-ci.outputs.check_result }}',
webBuild: '${{ needs.web-ci.outputs.build_result }}',
mobileLint: '${{ needs.mobile-ci.outputs.lint_result }}',
mobileTest: '${{ needs.mobile-ci.outputs.test_result }}',
});