pankod/refine

View on GitHub
.github/workflows/build-example-chunks.js

Summary

Maintainability
A
0 mins
Test Coverage
#!/usr/bin/env node

const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");

const EXAMPLES_DIR = "./examples";
const ignoredRegexes = [/^monorepo-/, /^with-nx/];
const CHUNK_COUNT = Number(process.env.CHUNKS ? process.env.CHUNKS : 1);
const BASE_REF = process.env.BASE_REF ? process.env.BASE_REF : "master";
const BUILD_ALL_EXAMPLES = process.env.BUILD_ALL_EXAMPLES === "true";

const getChangedPackages = () => {
  const p = require.resolve("lerna/cli.js");

  const output = execSync(`node ${p} ls --since origin/${BASE_REF} --json`, {
    stdio: "pipe",
  });

  const changedPackages = JSON.parse(output.toString());

  return changedPackages.map((pkg) => pkg.name);
};

const isExampleAffected = (example, changedPackages) => {
  const examplePath = path.join(EXAMPLES_DIR, example);

  if (changedPackages.length === 0) {
    return false;
  }

  const pkgJson = JSON.parse(
    fs.readFileSync(path.join(examplePath, "package.json")),
  );

  const dependencies = Object.keys(pkgJson.dependencies || {});
  const devDependencies = Object.keys(pkgJson.devDependencies || {});

  const allDependencies = [...dependencies, ...devDependencies];

  return allDependencies.some((dependency) => {
    return changedPackages.includes(dependency);
  });
};

const isExampleModified = (example) => {
  const output = execSync(
    `git diff --quiet HEAD origin/${BASE_REF} -- ${EXAMPLES_DIR}/${example} || echo changed`,
    { stdio: "pipe" },
  );

  return output.toString().includes("changed");
};

const hasPackageJson = (example) => {
  return fs.existsSync(path.join(EXAMPLES_DIR, example, "package.json"));
};

const hasE2eTests = (example) => {
  return fs.existsSync(path.join(EXAMPLES_DIR, example, "cypress.config.ts"));
};

const isDirectory = (example) => {
  return fs.statSync(path.join(EXAMPLES_DIR, example)).isDirectory();
};

const isIgnored = (example) => {
  return ignoredRegexes.some((regex) => regex.test(example));
};

const getExamples = () => {
  return fs
    .readdirSync(EXAMPLES_DIR)
    .filter((dir) => isDirectory(dir))
    .filter((dir) => !isIgnored(dir))
    .filter((dir) => hasPackageJson(dir));
};

//
// Get changed packages and affected examples
//
const changedPackages = getChangedPackages();

const examples = getExamples().filter((dir) =>
  BUILD_ALL_EXAMPLES
    ? true
    : isExampleAffected(dir, changedPackages) || isExampleModified(dir),
);

console.log(
  `Changed packages (${changedPackages.length}):\n- ${changedPackages.join(
    "\n- ",
  )}`,
);

console.log("");

console.log(
  `Affected examples (${examples.length}):\n- ${examples.join("\n- ")}`,
);

//
// Get examples with and without e2e tests
//
const examplesWithE2eTests = examples.filter((dir) => hasE2eTests(dir));

const examplesWithoutE2eTests = examples.filter(
  (dir) => !examplesWithE2eTests.includes(dir),
);

console.log(
  `\nFound ${examplesWithE2eTests.length} examples with e2e tests and ${examplesWithoutE2eTests.length} without.\n`,
);

//
// Split examples into chunks
//
const chunkSize = Math.ceil(examples.length / CHUNK_COUNT);

const chunks = [];

for (let i = 0; i < examples.length; i += chunkSize) {
  const tempChunk = [];

  for (j = 0; j < chunkSize; j++) {
    if (j % (CHUNK_COUNT / 2) === 1) {
      if (examplesWithE2eTests.length > 0) {
        tempChunk.push(examplesWithE2eTests.shift());
      } else if (examplesWithoutE2eTests.length > 0) {
        tempChunk.push(examplesWithoutE2eTests.shift());
      }
    } else {
      if (examplesWithoutE2eTests.length > 0) {
        tempChunk.push(examplesWithoutE2eTests.shift());
      }
    }
  }

  if (i + chunkSize >= examples.length) {
    tempChunk.push(...examplesWithE2eTests);

    tempChunk.push(...examplesWithoutE2eTests);
  }

  chunks.push(tempChunk);
}

//
// Set outputs
//
chunks.forEach((chunk, i) => {
  console.log(`::set-output name=CHUNK_${i + 1}::${chunk.join(",")}`);
});