Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/JAVA/Openclaw/scripts/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 9 kB image not shown  

Quelle  changed-lanes.mjs   Sprache: unbekannt

 
Spracherkennung für: .mjs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { execFileSync } from "node:child_process";
import { appendFileSync } from "node:fs";
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mjs";

const DOCS_PATH_RE = /^(?:docs\/|README\.md$|AGENTS\.md$|.*\.mdx?$)/u;
const APP_PATH_RE = /^(?:apps\/|Swabble\/|appcast\.xml$)/u;
const EXTENSION_PATH_RE = /^extensions\/[^/]+(?:\/|$)/u;
const CORE_PATH_RE = /^(?:src\/|ui\/|packages\/)/u;
const TOOLING_PATH_RE =
  /^(?:scripts\/|test\/vitest\/|\.github\/|git-hooks\/|vitest(?:\..+)?\.config\.ts$|tsconfig.*\.json$|\.gitignore$|\.oxlint.*|\.oxfmt.*)/u;
const ROOT_GLOBAL_PATH_RE =
  /^(?:package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|tsdown\.config\.ts$|vitest\.config\.ts$)/u;
const TEST_PATH_RE =
  /(?:^|\/)(?:test|__tests__)\/|(?:\.|\/)(?:test|spec|e2e|browser\.test)\.[cm]?[jt]sx?$/u;
const PUBLIC_EXTENSION_CONTRACT_RE =
  /^(?:src\/plugin-sdk\/|src\/plugins\/contracts\/|src\/channels\/plugins\/|scripts\/lib\/plugin-sdk-entrypoints\.json$|scripts\/sync-plugin-sdk-exports\.mjs$|scripts\/generate-plugin-sdk-api-baseline\.ts$)/u;
export const RELEASE_METADATA_PATHS = new Set([
  "CHANGELOG.md",
  "apps/android/app/build.gradle.kts",
  "apps/ios/CHANGELOG.md",
  "apps/ios/Config/Version.xcconfig",
  "apps/ios/fastlane/metadata/en-US/release_notes.txt",
  "apps/ios/version.json",
  "apps/macos/Sources/OpenClaw/Resources/Info.plist",
  "docs/.generated/config-baseline.sha256",
  "docs/install/updating.md",
  "package.json",
  "src/config/schema.base.generated.ts",
]);

/** @typedef {"core" | "coreTests" | "extensions" | "extensionTests" | "apps" | "docs" | "tooling" | "releaseMetadata" | "all"} ChangedLane */

/**
 * @typedef {{
 *   paths: string[];
 *   lanes: Record<ChangedLane, boolean>;
 *   extensionImpactFromCore: boolean;
 *   docsOnly: boolean;
 *   reasons: string[];
 * }} ChangedLaneResult
 */

export function normalizeChangedPath(inputPath) {
  return String(inputPath ?? "")
    .trim()
    .replaceAll("\\", "/")
    .replace(/^\.\/+/u, "");
}

export function createEmptyChangedLanes() {
  return {
    core: false,
    coreTests: false,
    extensions: false,
    extensionTests: false,
    apps: false,
    docs: false,
    tooling: false,
    releaseMetadata: false,
    all: false,
  };
}

/**
 * @param {string[]} changedPaths
 * @returns {ChangedLaneResult}
 */
export function detectChangedLanes(changedPaths) {
  const paths = [...new Set(changedPaths.map(normalizeChangedPath).filter(Boolean))].toSorted(
    (left, right) => left.localeCompare(right),
  );
  const lanes = createEmptyChangedLanes();
  const reasons = [];
  let extensionImpactFromCore = false;
  let hasNonDocs = false;

  if (paths.length === 0) {
    reasons.push("no changed paths");
    return { paths, lanes, extensionImpactFromCore: false, docsOnly: false, reasons };
  }

  if (
    paths.some((changedPath) => RELEASE_METADATA_PATHS.has(changedPath)) &&
    paths.every(
      (changedPath) => RELEASE_METADATA_PATHS.has(changedPath) || DOCS_PATH_RE.test(changedPath),
    )
  ) {
    lanes.releaseMetadata = true;
    lanes.docs = paths.some((changedPath) => DOCS_PATH_RE.test(changedPath));
    for (const changedPath of paths) {
      reasons.push(`${changedPath}: release metadata`);
    }
    return { paths, lanes, extensionImpactFromCore: false, docsOnly: false, reasons };
  }

  for (const changedPath of paths) {
    if (DOCS_PATH_RE.test(changedPath)) {
      lanes.docs = true;
      continue;
    }

    hasNonDocs = true;

    if (ROOT_GLOBAL_PATH_RE.test(changedPath)) {
      lanes.all = true;
      extensionImpactFromCore = true;
      reasons.push(`${changedPath}: root config/package surface`);
      continue;
    }

    if (PUBLIC_EXTENSION_CONTRACT_RE.test(changedPath)) {
      lanes.core = true;
      lanes.coreTests = true;
      lanes.extensions = true;
      lanes.extensionTests = true;
      extensionImpactFromCore = true;
      reasons.push(`${changedPath}: public core/plugin contract affects extensions`);
      continue;
    }

    if (EXTENSION_PATH_RE.test(changedPath)) {
      if (TEST_PATH_RE.test(changedPath)) {
        lanes.extensionTests = true;
        reasons.push(`${changedPath}: extension test`);
      } else {
        lanes.extensions = true;
        lanes.extensionTests = true;
        reasons.push(`${changedPath}: extension production`);
      }
      continue;
    }

    if (CORE_PATH_RE.test(changedPath)) {
      if (TEST_PATH_RE.test(changedPath)) {
        lanes.coreTests = true;
        reasons.push(`${changedPath}: core test`);
      } else {
        lanes.core = true;
        lanes.coreTests = true;
        reasons.push(`${changedPath}: core production`);
      }
      continue;
    }

    if (APP_PATH_RE.test(changedPath)) {
      lanes.apps = true;
      reasons.push(`${changedPath}: app surface`);
      continue;
    }

    if (changedPath.startsWith("test/")) {
      lanes.tooling = true;
      reasons.push(`${changedPath}: root test/support surface`);
      continue;
    }

    if (TOOLING_PATH_RE.test(changedPath)) {
      lanes.tooling = true;
      reasons.push(`${changedPath}: tooling surface`);
      continue;
    }

    lanes.all = true;
    extensionImpactFromCore = true;
    reasons.push(`${changedPath}: unknown surface; fail-safe all lanes`);
  }

  return {
    paths,
    lanes,
    extensionImpactFromCore,
    docsOnly: lanes.docs && !hasNonDocs,
    reasons,
  };
}

/**
 * @param {{ base: string; head?: string; includeWorktree?: boolean }} params
 * @returns {string[]}
 */
export function listChangedPathsFromGit(params) {
  const base = params.base;
  const head = params.head ?? "HEAD";
  if (!base) {
    return [];
  }
  const rangePaths = runGitNameOnlyDiff([`${base}...${head}`]);
  if (params.includeWorktree === false) {
    return rangePaths;
  }
  return [
    ...new Set([
      ...rangePaths,
      ...runGitNameOnlyDiff(["--cached", "--diff-filter=ACMR"]),
      ...runGitNameOnlyDiff(["--diff-filter=ACMR"]),
      ...runGitLsFiles(["--others", "--exclude-standard"]),
    ]),
  ].toSorted((left, right) => left.localeCompare(right));
}

function runGitNameOnlyDiff(extraArgs) {
  const output = execFileSync("git", ["diff", "--name-only", ...extraArgs], {
    stdio: ["ignore", "pipe", "pipe"],
    encoding: "utf8",
  });
  return output.split("\n").map(normalizeChangedPath).filter(Boolean);
}

function runGitLsFiles(extraArgs) {
  const output = execFileSync("git", ["ls-files", ...extraArgs], {
    stdio: ["ignore", "pipe", "pipe"],
    encoding: "utf8",
  });
  return output.split("\n").map(normalizeChangedPath).filter(Boolean);
}

export function listStagedChangedPaths() {
  const output = execFileSync("git", ["diff", "--cached", "--name-only", "--diff-filter=ACMR"], {
    stdio: ["ignore", "pipe", "pipe"],
    encoding: "utf8",
  });
  return output.split("\n").map(normalizeChangedPath).filter(Boolean);
}

export function writeChangedLaneGitHubOutput(result, outputPath = process.env.GITHUB_OUTPUT) {
  if (!outputPath) {
    throw new Error("GITHUB_OUTPUT is required");
  }
  for (const [lane, enabled] of Object.entries(result.lanes)) {
    appendFileSync(outputPath, `run_${toSnakeCase(lane)}=${String(enabled)}\n`, "utf8");
  }
  appendFileSync(outputPath, `docs_only=${result.docsOnly}\n`, "utf8");
  appendFileSync(
    outputPath,
    `extension_impact_from_core=${result.extensionImpactFromCore}\n`,
    "utf8",
  );
}

function toSnakeCase(value) {
  return value.replace(/[A-Z]/gu, (match) => `_${match.toLowerCase()}`);
}

function parseArgs(argv) {
  const args = {
    base: "origin/main",
    head: "HEAD",
    staged: false,
    json: false,
    githubOutput: false,
    paths: [],
  };
  return parseFlagArgs(
    argv,
    args,
    [
      stringFlag("--base", "base"),
      stringFlag("--head", "head"),
      booleanFlag("--staged", "staged"),
      booleanFlag("--json", "json"),
      booleanFlag("--github-output", "githubOutput"),
    ],
    {
      onUnhandledArg(arg, target) {
        target.paths.push(arg);
        return "handled";
      },
    },
  );
}

function isDirectRun() {
  const direct = process.argv[1];
  return Boolean(direct && import.meta.url.endsWith(direct));
}

function printHuman(result) {
  const enabled = Object.entries(result.lanes)
    .filter(([, value]) => value)
    .map(([lane]) => lane);
  console.log(`lanes: ${enabled.length > 0 ? enabled.join(", ") : "none"}`);
  if (result.docsOnly) {
    console.log("docs-only: true");
  }
  if (result.extensionImpactFromCore) {
    console.log("extension-impact-from-core: true");
  }
  if (result.paths.length > 0) {
    console.log("paths:");
    for (const changedPath of result.paths) {
      console.log(`- ${changedPath}`);
    }
  }
  if (result.reasons.length > 0) {
    console.log("reasons:");
    for (const reason of result.reasons) {
      console.log(`- ${reason}`);
    }
  }
}

if (isDirectRun()) {
  const args = parseArgs(process.argv.slice(2));
  const paths =
    args.paths.length > 0
      ? args.paths
      : args.staged
        ? listStagedChangedPaths()
        : listChangedPathsFromGit({ base: args.base, head: args.head });
  const result = detectChangedLanes(paths);
  if (args.githubOutput) {
    writeChangedLaneGitHubOutput(result);
  }
  if (args.json) {
    console.log(JSON.stringify(result, null, 2));
  } else if (!args.githubOutput) {
    printHuman(result);
  }
}

[Dauer der Verarbeitung: 0.23 Sekunden, vorverarbeitet 2026-04-27]