Skip to content

Commit 0ec47d8

Browse files
authored
workspace: add monorepo level lint, setup JSON and MD lint, fix warnings (#49)
1 parent 28c2bea commit 0ec47d8

35 files changed

+1030
-408
lines changed

.github/workflow-scripts/__tests__/firebaseUtils-test.js

Lines changed: 80 additions & 80 deletions
Large diffs are not rendered by default.

.github/workflow-scripts/__tests__/notifyDiscord-test.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ describe('sendMessageToDiscord', () => {
122122

123123
it('should throw an error if webhook URL is missing', async () => {
124124
await expect(sendMessageToDiscord(null, {})).rejects.toThrow(
125-
'Discord webhook URL is missing',
125+
'Discord webhook URL is missing'
126126
);
127127
});
128128

@@ -134,7 +134,7 @@ describe('sendMessageToDiscord', () => {
134134
});
135135

136136
const webhook = 'https://discord.com/api/webhooks/123/abc';
137-
const message = {content: 'Test message'};
137+
const message = { content: 'Test message' };
138138

139139
await expect(sendMessageToDiscord(webhook, message)).resolves.not.toThrow();
140140

@@ -149,7 +149,7 @@ describe('sendMessageToDiscord', () => {
149149

150150
// Verify console.log was called
151151
expect(console.log).toHaveBeenCalledWith(
152-
'Successfully sent message to Discord',
152+
'Successfully sent message to Discord'
153153
);
154154
});
155155

@@ -162,15 +162,15 @@ describe('sendMessageToDiscord', () => {
162162
});
163163

164164
const webhook = 'https://discord.com/api/webhooks/123/abc';
165-
const message = {content: 'Test message'};
165+
const message = { content: 'Test message' };
166166

167167
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
168-
'HTTP status code: 400',
168+
'HTTP status code: 400'
169169
);
170170

171171
// Verify console.error was called
172172
expect(console.error).toHaveBeenCalledWith(
173-
'Failed to send message to Discord: 400 Bad Request',
173+
'Failed to send message to Discord: 400 Bad Request'
174174
);
175175
});
176176

@@ -180,10 +180,10 @@ describe('sendMessageToDiscord', () => {
180180
global.fetch.mockRejectedValueOnce(networkError);
181181

182182
const webhook = 'https://discord.com/api/webhooks/123/abc';
183-
const message = {content: 'Test message'};
183+
const message = { content: 'Test message' };
184184

185185
await expect(sendMessageToDiscord(webhook, message)).rejects.toThrow(
186-
'Network error',
186+
'Network error'
187187
);
188188
});
189-
});
189+
});

.github/workflow-scripts/collectNightlyOutcomes.js

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,19 @@
77
* @format
88
*/
99

10-
const fs = require('fs');
11-
const path = require('path');
12-
const {
13-
prepareFailurePayload,
14-
prepareComparisonPayload,
15-
sendMessageToDiscord,
16-
} = require('./notifyDiscord');
10+
const fs = require('node:fs');
11+
const path = require('node:path');
12+
1713
const {
1814
FirebaseClient,
1915
compareResults,
2016
getTodayDate,
2117
} = require('./firebaseUtils');
18+
const {
19+
prepareFailurePayload,
20+
prepareComparisonPayload,
21+
sendMessageToDiscord,
22+
} = require('./notifyDiscord');
2223

2324
function readOutcomes() {
2425
const baseDir = '/tmp';
@@ -29,19 +30,21 @@ function readOutcomes() {
2930
fs.readdirSync(fullPath).forEach(subFile => {
3031
const subFullPath = path.join(fullPath, subFile);
3132
if (subFullPath.endsWith('outcome')) {
32-
const [library, status, url] = String(fs.readFileSync(subFullPath, 'utf8'))
33+
const [library, status, url] = String(
34+
fs.readFileSync(subFullPath, 'utf8')
35+
)
3336
.trim()
3437
.split('|');
3538
const platform = subFile.includes('android') ? 'Android' : 'iOS';
3639
const runUrl = status.trim() === 'failure' ? url : undefined;
3740
console.log(
38-
`[${platform}] ${library} completed with status ${status}`,
41+
`[${platform}] ${library} completed with status ${status}`
3942
);
4043
outcomes.push({
4144
library: library.trim(),
4245
platform,
4346
status: status.trim(),
44-
runUrl
47+
runUrl,
4548
});
4649
}
4750
});
@@ -56,7 +59,7 @@ function readOutcomes() {
5659
library: library.trim(),
5760
platform,
5861
status: status.trim(),
59-
runUrl
62+
runUrl,
6063
});
6164
}
6265
});
@@ -69,7 +72,7 @@ function printFailures(outcomes) {
6972
outcomes.forEach(entry => {
7073
if (entry.status !== 'success') {
7174
console.log(
72-
`❌ [${entry.platform}] ${entry.library} failed with status ${entry.status}`,
75+
`❌ [${entry.platform}] ${entry.library} failed with status ${entry.status}`
7376
);
7477
failedLibraries.push({
7578
library: entry.library,
@@ -127,7 +130,7 @@ async function collectResults(discordWebHook) {
127130

128131
// Get the most recent previous results for comparison
129132
console.log(`Looking for most recent previous results before ${today}...`);
130-
const {results: previousResults, date: previousDate} =
133+
const { results: previousResults, date: previousDate } =
131134
await firebaseClient.getLatestResults(today);
132135

133136
let broken = [];
@@ -141,11 +144,11 @@ async function collectResults(discordWebHook) {
141144
recovered = comparison.recovered;
142145

143146
console.log(
144-
`Found ${broken.length} newly broken jobs and ${recovered.length} recovered jobs compared to ${previousDate}`,
147+
`Found ${broken.length} newly broken jobs and ${recovered.length} recovered jobs compared to ${previousDate}`
145148
);
146149
} else {
147150
console.log(
148-
'No previous results found for comparison - this might be the first run or no recent data available',
151+
'No previous results found for comparison - this might be the first run or no recent data available'
149152
);
150153
}
151154

.github/workflow-scripts/firebaseUtils.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class FirebaseClient {
2222
async authenticate() {
2323
if (!this.email || !this.password) {
2424
throw new Error(
25-
'Firebase credentials not found in environment variables',
25+
'Firebase credentials not found in environment variables'
2626
);
2727
}
2828

@@ -36,11 +36,10 @@ class FirebaseClient {
3636
'identitytoolkit.googleapis.com',
3737
`/v1/accounts:signInWithPassword?key=${this.apiKey}`,
3838
'POST',
39-
authData,
39+
authData
4040
);
4141

4242
this.idToken = response.idToken;
43-
return;
4443
}
4544

4645
/**
@@ -105,16 +104,16 @@ class FirebaseClient {
105104
const checkDateStr = checkDate.toISOString().split('T')[0];
106105

107106
console.log(
108-
`Checking for results on ${checkDateStr} (${daysBack} days back)...`,
107+
`Checking for results on ${checkDateStr} (${daysBack} days back)...`
109108
);
110109

111110
try {
112111
const results = await this.getResults(checkDateStr);
113112
if (results && results.length > 0) {
114113
console.log(
115-
`Found results from ${checkDateStr} (${daysBack} days back)`,
114+
`Found results from ${checkDateStr} (${daysBack} days back)`
116115
);
117-
return {results, date: checkDateStr};
116+
return { results, date: checkDateStr };
118117
}
119118
} catch (error) {
120119
console.log(`No results found for ${checkDateStr}: ${error.message}`);
@@ -123,9 +122,9 @@ class FirebaseClient {
123122
}
124123

125124
console.log(
126-
`No previous results found within the last ${maxDaysBack} days`,
125+
`No previous results found within the last ${maxDaysBack} days`
127126
);
128-
return {results: null, date: null};
127+
return { results: null, date: null };
129128
}
130129

131130
async makeRequest(hostname, path, method, data = null) {
@@ -231,7 +230,7 @@ function compareResults(currentResults, previousResults) {
231230
}
232231
}
233232

234-
return {broken, recovered};
233+
return { broken, recovered };
235234
}
236235

237236
/**

.github/workflow-scripts/notifyDiscord.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async function sendMessageToDiscord(webHook, message) {
3434
} else {
3535
const errorText = await response.text();
3636
console.error(
37-
`Failed to send message to Discord: ${response.status} ${errorText}`,
37+
`Failed to send message to Discord: ${response.status} ${errorText}`
3838
);
3939
throw new Error(`HTTP status code: ${response.status}`);
4040
}
@@ -125,7 +125,7 @@ function prepareComparisonPayload(broken, recovered) {
125125
}
126126
}
127127

128-
return {content};
128+
return { content };
129129
}
130130

131131
// Export the functions using CommonJS syntax

.github/workflows/test-js.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
node-version: 24
2020
cache: 'yarn'
2121
- name: Install deps
22-
run: npm install
22+
run: yarn install --frozen-lockfile
23+
- name: Lint code
24+
run: yarn lint
2325
- name: Run tests
24-
run: npm test
26+
run: yarn test

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Automated GitHub Actions workflows for testing React Native ecosystem libraries
1010
This repository contains GitHub Actions workflows that automatically test **popular React Native OSS libraries** against React Native **nightly builds**. The system runs daily to catch breaking changes early and ensure ecosystem compatibility with upcoming React Native releases.
1111

1212
Specifically this repo will:
13+
1314
- Run a daily job **every night at 4:15 AM UTC** via GitHub Actions scheduled workflows
1415
- Testing the latest `react-native@nightly` build against a list of popular libraries, such as:
1516
- `react-native-async-storage`
@@ -20,15 +21,17 @@ Specifically this repo will:
2021
- Send a Discord message for failure alerts and status updates
2122
- Store results in Firebase for historical tracking and run comparison to identify newly broken or recovered libraries
2223

23-
#### How to apply?
24+
### How to apply?
2425

2526
If you're a library maintainer, you can now sign up to be part of our nightly testing to make sure your library will keep on working. Read more in the Discussions and Proposals discussion:
26-
* https://github.com/react-native-community/discussions-and-proposals/discussions/931
27+
28+
- https://github.com/react-native-community/discussions-and-proposals/discussions/931
2729

2830
### Website
2931

3032
The test results are also published on the website, which is available on the following address:
31-
* https://react-native-community.github.io/nightly-tests/
33+
34+
- https://react-native-community.github.io/nightly-tests/
3235

3336
To learn more about website app, see [the README file](./website/README.md) in the `website` directory.
3437

eslint.config.mjs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import jsPlugin from '@eslint/js';
2+
import jsonPlugin from '@eslint/json';
3+
import markdownPlugin from '@eslint/markdown';
4+
import { defineConfig, globalIgnores } from 'eslint/config';
5+
import importPlugin from 'eslint-plugin-import';
6+
import jestPlugin from 'eslint-plugin-jest';
7+
import prettierPlugin from 'eslint-plugin-prettier/recommended';
8+
import globals from 'globals';
9+
10+
export default defineConfig([
11+
globalIgnores(['website']),
12+
13+
prettierPlugin,
14+
importPlugin.flatConfigs.recommended,
15+
16+
{
17+
rules: {
18+
'prettier/prettier': [
19+
'error',
20+
{
21+
arrowParens: 'avoid',
22+
bracketSameLine: true,
23+
printWidth: 80,
24+
singleQuote: true,
25+
trailingComma: 'es5',
26+
endOfLine: 'auto',
27+
},
28+
],
29+
},
30+
},
31+
32+
{
33+
files: ['**/*.{js,mjs}'],
34+
plugins: {
35+
js: jsPlugin,
36+
},
37+
languageOptions: {
38+
globals: globals.node,
39+
ecmaVersion: 'latest',
40+
sourceType: 'module',
41+
},
42+
extends: ['js/recommended'],
43+
rules: {
44+
'import/no-unresolved': 'off',
45+
'import/enforce-node-protocol-usage': ['error', 'always'],
46+
'import/order': [
47+
'error',
48+
{
49+
groups: [['external', 'builtin'], 'internal', ['parent', 'sibling']],
50+
'newlines-between': 'always',
51+
alphabetize: {
52+
order: 'asc',
53+
},
54+
},
55+
],
56+
},
57+
},
58+
59+
{
60+
files: ['**/*-test.js'],
61+
plugins: { jest: jestPlugin },
62+
languageOptions: {
63+
globals: jestPlugin.environments.globals.globals,
64+
},
65+
...jestPlugin.configs['flat/recommended'],
66+
},
67+
68+
{
69+
files: ['**/*.json'],
70+
language: 'json/json',
71+
plugins: {
72+
json: jsonPlugin,
73+
},
74+
extends: ['json/recommended'],
75+
},
76+
77+
{
78+
files: ['**/*.md'],
79+
plugins: {
80+
markdown: markdownPlugin,
81+
},
82+
extends: ['markdown/recommended'],
83+
},
84+
]);

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module.exports = {
2-
testEnvironment: 'node'
2+
testEnvironment: 'node',
33
};

libraries.schema.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@
3939
"type": "string"
4040
},
4141
"description": "Array of GitHub usernames. We will use it in the future to communicate with library maintainers (e.g. ping for build failures).",
42-
"examples": [
43-
["frodo", "bilbo", "pippin", "merry", "sam"]
44-
]
42+
"examples": [["frodo", "bilbo", "pippin", "merry", "sam"]]
4543
},
4644
"notes": {
4745
"type": "string",

0 commit comments

Comments
 (0)