refactor: move client specific scripts inside client (#51123)

This commit is contained in:
Oliver Eyton-Williams
2023-08-05 17:43:13 +02:00
committed by GitHub
parent 91977bbf2c
commit 8d0c3557dd
10 changed files with 82 additions and 66 deletions
-57
View File
@@ -1,57 +0,0 @@
import { writeFileSync } from 'fs';
import path from 'path';
import fetch from 'node-fetch';
import yaml from 'js-yaml';
import { config } from 'dotenv';
import { trendingSchemaValidator } from './schema/trending-schema';
config({ path: path.resolve(__dirname, '../../../.env') });
const createCdnUrl = (lang: string) =>
`https://cdn.freecodecamp.org/universal/trending/${lang}.yaml`;
const download = async (clientLocale: string) => {
const url = createCdnUrl(clientLocale);
const res = await fetch(url);
if (!res.ok) {
throw new Error(
`
----------------------------------------------------
Error: The CDN is missing the trending YAML file.
----------------------------------------------------
Unable to fetch the ${clientLocale} footer: ${res.statusText}
`
);
}
const data = await res.text();
const trendingJSON = JSON.stringify(yaml.load(data));
const trendingLocation = `./client/i18n/locales/${clientLocale}/trending.json`;
writeFileSync(trendingLocation, trendingJSON);
const trendingObject = JSON.parse(trendingJSON) as Record<string, string>;
const validationError =
(trendingSchemaValidator(trendingObject).error as Error) || null;
if (validationError) {
throw new Error(
`
----------------------------------------------------
Error: The trending JSON is invalid.
----------------------------------------------------
Unable to validate the ${clientLocale} trending JSON schema: ${validationError.message}
`
);
}
};
const locale = process.env.CLIENT_LOCALE;
if (!locale) throw Error('CLIENT_LOCALE must be set to a valid locale');
void download(locale);
// TODO: remove the need to fallback to english once we're confident it's
// unnecessary (client/i18n/config.js will need all references to 'en' removing)
if (locale !== 'english') void download('english');
-146
View File
@@ -1,146 +0,0 @@
import { spawn } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { availableLangs, Languages } from '../../../config/i18n';
import env from '../../../config/read-env';
const globalConfigPath = path.resolve(__dirname, '../../../config');
const { FREECODECAMP_NODE_ENV } = process.env;
function checkClientLocale() {
if (!process.env.CLIENT_LOCALE) throw Error('CLIENT_LOCALE is not set');
if (!availableLangs.client.includes(process.env.CLIENT_LOCALE as Languages)) {
throw Error(`
CLIENT_LOCALE, ${process.env.CLIENT_LOCALE}, is not an available language in config/i18n.ts
`);
}
}
function checkCurriculumLocale() {
if (!process.env.CURRICULUM_LOCALE)
throw Error('CURRICULUM_LOCALE is not set');
if (
!availableLangs.curriculum.includes(
process.env.CURRICULUM_LOCALE as Languages
)
) {
throw Error(`
CURRICULUM_LOCALE, ${process.env.CURRICULUM_LOCALE}, is not an available language in config/i18n.ts
`);
}
}
if (FREECODECAMP_NODE_ENV !== 'development') {
const locationKeys = [
'homeLocation',
'apiLocation',
'forumLocation',
'newsLocation',
'radioLocation'
];
const deploymentKeys = [
'clientLocale',
'curriculumLocale',
'showLocaleDropdownMenu',
'deploymentEnv',
'environment',
'showUpcomingChanges',
'showNewCurriculum'
];
const searchKeys = ['algoliaAppId', 'algoliaAPIKey'];
const donationKeys = ['stripePublicKey', 'paypalClientId', 'patreonClientId'];
const loggingKeys = ['sentryClientDSN'];
const abTestingKeys = ['growthbookUri'];
const expectedVariables = locationKeys.concat(
deploymentKeys,
searchKeys,
donationKeys,
loggingKeys,
abTestingKeys
);
const actualVariables = Object.keys(env as Record<string, unknown>);
if (expectedVariables.length !== actualVariables.length) {
const extraVariables = actualVariables
.filter(x => !expectedVariables.includes(x))
.toString();
const missingVariables = expectedVariables
.filter(x => !actualVariables.includes(x))
.toString();
throw Error(
`
Env. variable validation failed. Make sure only expected variables are used and configured.
` +
(extraVariables ? `Extra variables: ${extraVariables}\n` : '') +
(missingVariables ? `Missing variables: ${missingVariables}` : '')
);
}
for (const key of expectedVariables) {
// Since we may need to disable the sentry DSN (if we're getting too many
// errors), this is the one key we don't check is set.
if (key === 'sentryClientDSN') continue;
const envVal = env[key as keyof typeof env];
if (typeof envVal === 'undefined' || envVal === null) {
throw Error(`
Env. variable ${key} is missing, build cannot continue
`);
}
}
if (env['environment'] !== 'production')
throw Error(`
Production environment should be 'production'
`);
if (env['showUpcomingChanges'])
throw Error(`
SHOW_UPCOMING_CHANGES should never be 'true' in production
`);
checkClientLocale();
checkCurriculumLocale();
} else {
checkClientLocale();
checkCurriculumLocale();
if (fs.existsSync(`${globalConfigPath}/env.json`)) {
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-assignment */
const {
showNewCurriculum,
showUpcomingChanges
} = require(`${globalConfigPath}/env.json`);
/* eslint-enable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-assignment */
if (
env['showUpcomingChanges'] !== showUpcomingChanges ||
env['showNewCurriculum'] !== showNewCurriculum
) {
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
console.log('Feature flags have been changed, cleaning client cache.');
const child = spawn('pnpm', ['run', 'clean:client']);
child.stdout.setEncoding('utf8');
child.stdout.on('data', function (data) {
console.log(data);
});
child.on('error', err => {
console.error(err);
});
}
}
}
fs.writeFileSync(`${globalConfigPath}/env.json`, JSON.stringify(env));
-6
View File
@@ -19,14 +19,8 @@
"author": "freeCodeCamp <team@freecodecamp.org>",
"main": "none",
"devDependencies": {
"@types/js-yaml": "4.0.5",
"@types/node-fetch": "2.6.4",
"chai": "4.3.7",
"debug": "4.3.4",
"dotenv": "16.3.1",
"joi": "17.9.2",
"js-yaml": "4.1.0",
"node-fetch": "2.6.12",
"readdirp": "3.6.0"
}
}
@@ -1,70 +0,0 @@
import Joi from 'joi';
const schema = Joi.object().keys({
article0title: Joi.string().required(),
article0link: Joi.string().uri({ scheme: 'https' }).required(),
article1title: Joi.string().required(),
article1link: Joi.string().uri({ scheme: 'https' }).required(),
article2title: Joi.string().required(),
article2link: Joi.string().uri({ scheme: 'https' }).required(),
article3title: Joi.string().required(),
article3link: Joi.string().uri({ scheme: 'https' }).required(),
article4title: Joi.string().required(),
article4link: Joi.string().uri({ scheme: 'https' }).required(),
article5title: Joi.string().required(),
article5link: Joi.string().uri({ scheme: 'https' }).required(),
article6title: Joi.string().required(),
article6link: Joi.string().uri({ scheme: 'https' }).required(),
article7title: Joi.string().required(),
article7link: Joi.string().uri({ scheme: 'https' }).required(),
article8title: Joi.string().required(),
article8link: Joi.string().uri({ scheme: 'https' }).required(),
article9title: Joi.string().required(),
article9link: Joi.string().uri({ scheme: 'https' }).required(),
article10title: Joi.string().required(),
article10link: Joi.string().uri({ scheme: 'https' }).required(),
article11title: Joi.string().required(),
article11link: Joi.string().uri({ scheme: 'https' }).required(),
article12title: Joi.string().required(),
article12link: Joi.string().uri({ scheme: 'https' }).required(),
article13title: Joi.string().required(),
article13link: Joi.string().uri({ scheme: 'https' }).required(),
article14title: Joi.string().required(),
article14link: Joi.string().uri({ scheme: 'https' }).required(),
article15title: Joi.string().required(),
article15link: Joi.string().uri({ scheme: 'https' }).required(),
article16title: Joi.string().required(),
article16link: Joi.string().uri({ scheme: 'https' }).required(),
article17title: Joi.string().required(),
article17link: Joi.string().uri({ scheme: 'https' }).required(),
article18title: Joi.string().required(),
article18link: Joi.string().uri({ scheme: 'https' }).required(),
article19title: Joi.string().required(),
article19link: Joi.string().uri({ scheme: 'https' }).required(),
article20title: Joi.string().required(),
article20link: Joi.string().uri({ scheme: 'https' }).required(),
article21title: Joi.string().required(),
article21link: Joi.string().uri({ scheme: 'https' }).required(),
article22title: Joi.string().required(),
article22link: Joi.string().uri({ scheme: 'https' }).required(),
article23title: Joi.string().required(),
article23link: Joi.string().uri({ scheme: 'https' }).required(),
article24title: Joi.string().required(),
article24link: Joi.string().uri({ scheme: 'https' }).required(),
article25title: Joi.string().required(),
article25link: Joi.string().uri({ scheme: 'https' }).required(),
article26title: Joi.string().required(),
article26link: Joi.string().uri({ scheme: 'https' }).required(),
article27title: Joi.string().required(),
article27link: Joi.string().uri({ scheme: 'https' }).required(),
article28title: Joi.string().required(),
article28link: Joi.string().uri({ scheme: 'https' }).required(),
article29title: Joi.string().required(),
article29link: Joi.string().uri({ scheme: 'https' }).required()
});
export const trendingSchemaValidator = (
trendingObj: Record<string, string>
): Joi.ValidationResult => {
return schema.validate(trendingObj);
};