diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 62c317eb8a..89c7f80b50 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,27 +20,28 @@ jobs: run: | git fetch origin master:master ANY_CHANGED=false - JS_ANY_CHANGED=false - JS_ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- tests/**/*.js tests/**/*.ts scripts/**/*.js scripts/**/*.mts scripts/**/*.ts sites/**/*.js sites/**/*.ts | tr '\n' ' ') - if [ -n "${JS_ALL_CHANGED_FILES}" ]; then - JS_ANY_CHANGED=true - ANY_CHANGED=true - fi - echo "js_all_changed_files=$JS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" - echo "js_any_changed=$JS_ANY_CHANGED" >> "$GITHUB_OUTPUT" - CHANNELS_ANY_CHANGED=false - CHANNELS_ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- sites/**/*.channels.xml | tr '\n' ' ') - if [ -n "${CHANNELS_ALL_CHANGED_FILES}" ]; then - CHANNELS_ANY_CHANGED=true - ANY_CHANGED=true - fi - echo "channels_all_changed_files=$CHANNELS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" - echo "channels_any_changed=$CHANNELS_ANY_CHANGED" >> "$GITHUB_OUTPUT" - echo "any_changed=$ANY_CHANGED" >> "$GITHUB_OUTPUT" + JS_FILES="tests/**/*.js tests/**/*.ts scripts/**/*.js scripts/**/*.mts scripts/**/*.ts sites/**/*.js sites/**/*.ts" + CHANNELS_FILES="sites/**/*.channels.xml" + WORKERS_FILES="workers.txt" + for PREFIX in JS CHANNELS WORKERS; do + CHANGED_FILES_VAR="${PREFIX}_FILES" + CHANGED_FILES_PATTERN=${!CHANGED_FILES_VAR} + if [ -n "${CHANGED_FILES_PATTERN}" ]; then + HAS_CHANGES=false + CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- ${CHANGED_FILES_PATTERN} | tr '\n' ' ') + if [ -n "${CHANGED_FILES}" ]; then + ANY_CHANGED=true + HAS_CHANGES=true + echo "${PREFIX,,}_all_changed_files=${CHANGED_FILES}" >> "${GITHUB_OUTPUT}" + fi + echo "${PREFIX,,}_any_changed=${HAS_CHANGES}" >> "${GITHUB_OUTPUT}" + fi + done + echo "any_changed=${ANY_CHANGED}" >> "${GITHUB_OUTPUT}" - uses: actions/setup-node@v4 if: steps.files.outputs.any_changed == 'true' with: - node-version: 22 + node-version: 24 cache: 'npm' - name: Install dependencies if: steps.files.outputs.any_changed == 'true' @@ -53,4 +54,7 @@ jobs: run: | npm run postinstall npm run channels:lint -- ${{ steps.files.outputs.channels_all_changed_files }} - npm run channels:validate -- ${{ steps.files.outputs.channels_all_changed_files }} \ No newline at end of file + npm run channels:validate -- ${{ steps.files.outputs.channels_all_changed_files }} + - name: Check changed workers.txt file + if: steps.files.outputs.workers_any_changed == 'true' + run: npm run workers:validate diff --git a/package.json b/package.json index 08be0dcd58..d94d9d6170 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "guides:update": "tsx scripts/commands/guides/update.ts", "guides:export": "tsx scripts/commands/guides/export.ts", "workers:load": "tsx scripts/commands/workers/load.ts", + "workers:validate": "tsx scripts/commands/workers/validate.ts", "grab": "tsx scripts/commands/epg/grab.ts", "lint": "npx eslint \"{scripts,tests,sites}/**/*.{ts,mts,js}\"", "test": "cross-env TZ=Pacific/Nauru npx jest --runInBand", diff --git a/scripts/commands/workers/validate.ts b/scripts/commands/workers/validate.ts new file mode 100644 index 0000000000..8834c749b9 --- /dev/null +++ b/scripts/commands/workers/validate.ts @@ -0,0 +1,67 @@ +import { Storage } from '@freearhey/storage-js' +import { ROOT_DIR } from '../../constants' +import { program } from 'commander' +import chalk from 'chalk' + +program.parse(process.argv) + +interface ValidationError { + line: number + type: 'missing_crlf' | 'contains_spaces' + content: string +} + +async function main() { + const rootStorage = new Storage(ROOT_DIR) + + if (!await rootStorage.exists('workers.txt')) { + console.log(chalk.red('workers.txt file not found!')) + process.exit(1) + } + + const workersTxt = await rootStorage.load('workers.txt') + const lines = workersTxt.split('\n') + + let totalFiles = 0 + let totalErrors = 0 + let totalWarnings = 0 + + const errors: ValidationError[] = [] + + lines.forEach((line, index) => { + const lineNum = index + 1 + + if (lineNum === lines.length && line.trim() === '') return + + if (!line.endsWith('\r')) { + errors.push({ line: lineNum, type: 'missing_crlf', content: line.replace(/\r/g, '') }) + totalErrors++ + } + + if (line.includes(' ')) { + errors.push({ line: lineNum, type: 'contains_spaces', content: line.replace(/\r/g, '') }) + totalErrors++ + } + }) + + if (errors.length) { + console.log(chalk.underline('workers.txt')) + console.table(errors, ['line', 'type', 'content']) + console.log() + totalFiles++ + } + + const totalProblems = totalWarnings + totalErrors + if (totalProblems > 0) { + console.log( + chalk.red( + `${totalProblems} problems (${totalErrors} errors, ${totalWarnings} warnings) in ${totalFiles} file(s)` + ) + ) + if (totalErrors > 0) { + process.exit(1) + } + } +} + +main() diff --git a/tests/commands/workers/validate.test.ts b/tests/commands/workers/validate.test.ts new file mode 100644 index 0000000000..32f95e261d --- /dev/null +++ b/tests/commands/workers/validate.test.ts @@ -0,0 +1,62 @@ +import { execSync } from 'child_process' +import fs from 'fs-extra' + +interface ExecError { + status: number + stdout: string +} + +const ENV_VAR = 'cross-env ROOT_DIR=tests/__data__/output' + +beforeEach(() => { + fs.emptyDirSync('tests/__data__/output') +}) + +describe('workers:validate', () => { + it('will show a message if workers.txt does not exist', () => { + try { + const cmd = `${ENV_VAR} npm run workers:validate` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + process.exit(1) + } catch (error) { + expect((error as ExecError).status).toBe(1) + expect((error as ExecError).stdout).toContain('workers.txt file not found!') + } + }) + + it('will show a message if workers.txt contains validation error', () => { + try { + fs.writeFileSync('tests/__data__/output/workers.txt', 'worker1.example.com\nworker 2.example.com\r\nworker3.example.com') + const cmd = `${ENV_VAR} npm run workers:validate` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + process.exit(1) + } catch (error) { + expect((error as ExecError).status).toBe(1) + expect((error as ExecError).stdout).toContain(` +┌─────────┬──────┬───────────────────┬────────────────────────┐ +│ (index) │ line │ type │ content │ +├─────────┼──────┼───────────────────┼────────────────────────┤ +│ 0 │ 1 │ 'missing_crlf' │ 'worker1.example.com' │ +│ 1 │ 2 │ 'contains_spaces' │ 'worker 2.example.com' │ +│ 2 │ 3 │ 'missing_crlf' │ 'worker3.example.com' │ +└─────────┴──────┴───────────────────┴────────────────────────┘ + +3 problems (3 errors, 0 warnings) in 1 file(s) +`) + } + }) + + it('does not display errors if there are none', () => { + try { + fs.writeFileSync('tests/__data__/output/workers.txt', 'worker1.example.com\r\nworker2.example.com\r\nworker3.example.com\r\n') + const cmd = `${ENV_VAR} npm run workers:validate` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + } catch (error) { + if (process.env.DEBUG === 'true') console.log((error as ExecError).stdout) + process.exit(1) + } + }) +}) diff --git a/workers.txt b/workers.txt index 8c3abe8a95..91c1949df1 100644 --- a/workers.txt +++ b/workers.txt @@ -1,2 +1,2 @@ worker-9dd4.onrender.com -raw.githubusercontent.com/StrangeDrVN/epg/public/output \ No newline at end of file +raw.githubusercontent.com/StrangeDrVN/epg/public/output