{"version":3,"file":"githubActions.cjs","names":["existsSync","join","readFileSync","resolve","getGitRootDir","listProjects","detectPackageManager","relative","sep"],"sources":["../../../../src/init/utils/githubActions.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join, relative, resolve, sep } from 'node:path';\nimport { getGitRootDir, listProjects } from '../../listProjects';\nimport { detectPackageManager, type PackageManager } from './packageManager';\n\n/** A GitHub Actions workflow file to be written to the project. */\nexport type GithubWorkflowFile = {\n  /** Path relative to the project root, e.g. `.github/workflows/intlayer-fill.yml`. */\n  filePath: string;\n  /** Full YAML content of the workflow file. */\n  content: string;\n};\n\n/** Options controlling how the CI workflows are generated. */\nexport type GithubWorkflowsOptions = {\n  /**\n   * The repository hosts several Intlayer projects. Every scaffolded command\n   * always carries `--ci` (the CLI discovers each Intlayer project of the\n   * repository and runs the command inside it), so this only adds the\n   * `INTLAYER_PROJECT_CREDENTIALS` hint to the workflow env: a JSON map of\n   * project path to CMS access keys injected per project by the CLI.\n   */\n  isMonorepo?: boolean;\n  /**\n   * Repository-relative directory (posix separators) the workflow commands run\n   * in. Set when the Intlayer project lives in a subdirectory of the\n   * repository and the repository root has no workspace manifest to install\n   * dependencies from. Undefined = repository root.\n   */\n  workingDirectory?: string;\n};\n\n/**\n * Resolved placement and generation parameters for the CI workflows.\n * See {@link resolveGithubWorkflowsContext}.\n */\nexport type GithubWorkflowsContext = {\n  /**\n   * Directory the workflow files must be written to. GitHub only triggers\n   * workflows stored in `.github/workflows` at the repository root, so this is\n   * the git root whenever the project lives inside a git repository.\n   */\n  workflowsRootDir: string;\n  /** Package manager whose commands are baked into the workflows. */\n  packageManager: PackageManager;\n  /** Generation options forwarded to {@link getGithubWorkflows}. */\n  options: GithubWorkflowsOptions;\n};\n\n/**\n * Returns true when the repository root hosts a workspace manifest\n * (a `package.json` `workspaces` field or a `pnpm-workspace.yaml`), meaning a\n * single install at the root covers every package of the monorepo.\n */\nconst hasWorkspaceManifest = (repositoryRootDir: string): boolean => {\n  if (existsSync(join(repositoryRootDir, 'pnpm-workspace.yaml'))) {\n    return true;\n  }\n\n  try {\n    const packageJsonPath = join(repositoryRootDir, 'package.json');\n    if (!existsSync(packageJsonPath)) return false;\n    const { workspaces } = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n    return Boolean(workspaces);\n  } catch {\n    return false;\n  }\n};\n\n/**\n * Resolves where the CI workflows must be written and how their commands must\n * be generated, based on the repository layout around `rootDir` (the Intlayer\n * project being initialized):\n *\n * - Project at the repository root: workflows are written in place. When the\n *   repository is a monorepo (workspace manifest, or several Intlayer projects\n *   discovered by `listProjects`), the per-project credentials hint is added.\n * - Project nested in a repository whose root has a `package.json`\n *   (workspace-managed monorepo): workflows are written at the git root —\n *   GitHub ignores `.github/workflows` in subdirectories — dependencies are\n *   installed at the root with the root's package manager, and the per-project\n *   credentials hint is added.\n * - Project nested in a repository without a root `package.json`: workflows\n *   are written at the git root but run inside the project directory\n *   (`working-directory`), keeping the project's own package manager.\n */\nexport const resolveGithubWorkflowsContext = async (\n  rootDir: string,\n  projectPackageManager: PackageManager\n): Promise<GithubWorkflowsContext> => {\n  const projectRootDir = resolve(rootDir);\n\n  let repositoryRootDir = projectRootDir;\n  try {\n    const gitRootDir = await getGitRootDir(projectRootDir);\n    if (gitRootDir) repositoryRootDir = resolve(gitRootDir);\n  } catch {\n    // Not a git repository — keep the project root as the workflow root.\n  }\n\n  // Discover every Intlayer project of the repository. `listProjects` scans\n  // for configuration files, so a fresh project whose config is created later\n  // in the init flow may not be counted yet — the workspace manifest check\n  // below still catches that monorepo case.\n  let repositoryProjectCount = 0;\n  try {\n    const { projectsPath } = await listProjects({\n      baseDir: repositoryRootDir,\n    });\n    repositoryProjectCount = projectsPath.length;\n  } catch {\n    repositoryProjectCount = 0;\n  }\n\n  const isNestedProject = repositoryRootDir !== projectRootDir;\n  const isWorkspaceRepository = hasWorkspaceManifest(repositoryRootDir);\n  const hasRootPackageJson = existsSync(\n    join(repositoryRootDir, 'package.json')\n  );\n\n  if (!isNestedProject) {\n    return {\n      workflowsRootDir: projectRootDir,\n      packageManager: projectPackageManager,\n      options: {\n        isMonorepo: isWorkspaceRepository || repositoryProjectCount > 1,\n      },\n    };\n  }\n\n  if (hasRootPackageJson) {\n    return {\n      workflowsRootDir: repositoryRootDir,\n      packageManager: detectPackageManager(repositoryRootDir),\n      options: { isMonorepo: true },\n    };\n  }\n\n  // Nested project without a root manifest: install and run everything inside\n  // the project directory itself.\n  return {\n    workflowsRootDir: repositoryRootDir,\n    packageManager: projectPackageManager,\n    options: {\n      workingDirectory: relative(repositoryRootDir, projectRootDir)\n        .split(sep)\n        .join('/'),\n    },\n  };\n};\n\n/**\n * Package-manager-specific snippets used to assemble the CI workflows.\n * - `setupSteps`: YAML steps (already indented for the `steps:` list) that\n *   install the package manager and Node.js, including dependency caching.\n * - `installCommand`: command that installs the project dependencies.\n * - `execCommand`: prefix used to run the locally installed `intlayer` binary.\n */\ntype PackageManagerCIConfig = {\n  setupSteps: string;\n  installCommand: string;\n  execCommand: string;\n};\n\n/** Dependency lock file of each package manager, used for cache invalidation. */\nconst LOCK_FILE_BY_PACKAGE_MANAGER: Record<PackageManager, string> = {\n  bun: 'bun.lock',\n  pnpm: 'pnpm-lock.yaml',\n  yarn: 'yarn.lock',\n  npm: 'package-lock.json',\n};\n\n/**\n * Returns the package-manager-specific steps and commands used to build the\n * GitHub Actions workflows. Each package manager needs a slightly different\n * setup action and a different way to run the local `intlayer` binary.\n *\n * `workingDirectory` is the repository-relative directory holding the lock\n * file when the project is nested: `actions/setup-node` looks for the lock\n * file at the repository root by default and fails the cache setup otherwise.\n */\nconst getPackageManagerCIConfig = (\n  packageManager: PackageManager,\n  workingDirectory?: string\n): PackageManagerCIConfig => {\n  const setupNodeStep = (cache: PackageManager | undefined): string => {\n    const cacheDependencyPath =\n      cache && workingDirectory\n        ? `\\n          cache-dependency-path: ${workingDirectory}/${LOCK_FILE_BY_PACKAGE_MANAGER[cache]}`\n        : '';\n\n    return `      - name: 🟢 Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20${cache ? `\\n          cache: ${cache}` : ''}${cacheDependencyPath}`;\n  };\n\n  switch (packageManager) {\n    case 'bun':\n      return {\n        setupSteps: `      - name: 🥟 Setup Bun\n        uses: oven-sh/setup-bun@v2`,\n        installCommand: 'bun install --frozen-lockfile',\n        execCommand: 'bunx intlayer',\n      };\n    case 'pnpm':\n      return {\n        setupSteps: `      - name: 📦 Setup pnpm\n        uses: pnpm/action-setup@v4\n${setupNodeStep('pnpm')}`,\n        installCommand: 'pnpm install --frozen-lockfile',\n        execCommand: 'pnpm exec intlayer',\n      };\n    case 'yarn':\n      return {\n        setupSteps: setupNodeStep('yarn'),\n        installCommand: 'yarn install --frozen-lockfile',\n        execCommand: 'yarn intlayer',\n      };\n    case 'npm':\n      return {\n        setupSteps: setupNodeStep('npm'),\n        installCommand: 'npm ci',\n        execCommand: 'npx intlayer',\n      };\n  }\n};\n\n/**\n * Renders the `defaults.run.working-directory` block for jobs that must run\n * inside a nested project directory. Empty when the commands run at the\n * repository root.\n */\nconst getWorkingDirectoryBlock = (workingDirectory?: string): string =>\n  workingDirectory\n    ? `\n    defaults:\n      run:\n        working-directory: ${workingDirectory}`\n    : '';\n\n/**\n * Renders the env comment + optional wiring for per-project credentials in a\n * monorepo. With `--ci`, the CLI matches each entry of the\n * `INTLAYER_PROJECT_CREDENTIALS` JSON map to a discovered project path and\n * injects its access keys before running the command in that project.\n */\nconst getMonorepoCredentialsBlock = (isMonorepo?: boolean): string =>\n  isMonorepo\n    ? `\n      #\n      # Monorepo — per-project CMS credentials, as a JSON map of project path\n      # (relative to the repository root) to access keys, e.g.\n      # {\"apps/web\":{\"clientId\":\"...\",\"clientSecret\":\"...\"}}:\n      # INTLAYER_PROJECT_CREDENTIALS: \\${{ secrets.INTLAYER_PROJECT_CREDENTIALS }}`\n    : '';\n\n/**\n * Builds the `intlayer fill` workflow. Runs on pull requests, regenerates the\n * missing translations for the changed dictionaries (`--git-diff`) using AI,\n * then commits the result back to the PR branch.\n *\n * Every intlayer command carries `--ci`: the CLI discovers the Intlayer\n * project(s) of the repository and runs the command inside each of them, so\n * the same workflow works for a single project and for a monorepo.\n *\n * AI access is required for `fill`. The workflow exposes both options:\n * - a provider API key (`AI_API_KEY` secret), forwarded via CLI flags, or\n * - Intlayer CMS access keys (`INTLAYER_CLIENT_ID` / `INTLAYER_CLIENT_SECRET`).\n */\nconst generateFillWorkflow = (\n  packageManager: PackageManager,\n  options: GithubWorkflowsOptions\n): string => {\n  const { setupSteps, installCommand, execCommand } = getPackageManagerCIConfig(\n    packageManager,\n    options.workingDirectory\n  );\n\n  return `name: Intlayer Fill\n# Auto-fill missing translations on every pull request.\non:\n  pull_request:\n    branches:\n      - main\n\npermissions:\n  contents: write\n  pull-requests: write\n\nconcurrency:\n  group: intlayer-fill-\\${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  fill:\n    runs-on: ubuntu-latest${getWorkingDirectoryBlock(options.workingDirectory)}\n    env:\n      # AI access is required to generate translations.\n      # Add the secret in: Settings → Secrets and variables → Actions.\n      #\n      # Option 1 — Use your own AI provider key (forwarded below via CLI flags):\n      AI_PROVIDER: openai\n      AI_MODEL: gpt-5-mini\n      AI_API_KEY: \\${{ secrets.AI_API_KEY }}\n      #\n      # Option 2 — Use Intlayer CMS access keys instead of your own AI key.\n      # Wire them in your intlayer.config and uncomment the lines below:\n      # INTLAYER_CLIENT_ID: \\${{ secrets.INTLAYER_CLIENT_ID }}\n      # INTLAYER_CLIENT_SECRET: \\${{ secrets.INTLAYER_CLIENT_SECRET }}${getMonorepoCredentialsBlock(options.isMonorepo)}\n    steps:\n      - name: ⬇️ Checkout repository\n        uses: actions/checkout@v4\n        with:\n          persist-credentials: true # Keep credentials to push back to the PR\n          fetch-depth: 0 # Full history so --git-diff can compare refs\n${setupSteps}\n      - name: 📦 Install dependencies\n        run: ${installCommand}\n      - name: ⚙️ Build dictionaries\n        run: ${execCommand} build --ci\n      - name: 🤖 Fill missing translations\n        # Skip when no AI credentials are configured, so the workflow stays green\n        # until an \\`AI_API_KEY\\` (or Intlayer CMS access keys) secret is added.\n        if: \\${{ env.AI_API_KEY != '' || env.INTLAYER_CLIENT_ID != '' }}\n        run: ${execCommand} fill --ci --git-diff --mode complete --provider $AI_PROVIDER --model $AI_MODEL --api-key $AI_API_KEY\n      - name: 📤 Commit and push changes\n        run: |\n          git config --local user.email \"github-actions[bot]@users.noreply.github.com\"\n          git config --local user.name \"github-actions[bot]\"\n          if [ -n \"$(git status --porcelain)\" ]; then\n            git add .\n            git commit -m \"chore(intlayer): fill missing translations [skip ci]\"\n            git push origin HEAD:\\${{ github.head_ref }}\n          else\n            echo \"No missing translations to fill.\"\n          fi\n`;\n};\n\n/**\n * Builds the `intlayer test` workflow. Runs on pull requests and fails the\n * check when required locales are missing translations. No AI access needed.\n */\nconst generateTestWorkflow = (\n  packageManager: PackageManager,\n  options: GithubWorkflowsOptions\n): string => {\n  const { setupSteps, installCommand, execCommand } = getPackageManagerCIConfig(\n    packageManager,\n    options.workingDirectory\n  );\n\n  return `name: Intlayer Test\n# Fail the pull request when required locales are missing translations.\non:\n  pull_request:\n    branches:\n      - main\n\nconcurrency:\n  group: intlayer-test-\\${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    runs-on: ubuntu-latest${getWorkingDirectoryBlock(options.workingDirectory)}\n    steps:\n      - name: ⬇️ Checkout repository\n        uses: actions/checkout@v4\n${setupSteps}\n      - name: 📦 Install dependencies\n        run: ${installCommand}\n      - name: ⚙️ Build dictionaries\n        run: ${execCommand} build --ci\n      - name: 🧪 Test for missing translations\n        run: ${execCommand} test --ci\n`;\n};\n\n/** Workflow file path constants, relative to the project root. */\nexport const GITHUB_FILL_WORKFLOW_PATH = '.github/workflows/intlayer-fill.yml';\nexport const GITHUB_TEST_WORKFLOW_PATH = '.github/workflows/intlayer-test.yml';\n\n/**\n * Returns the two Intlayer GitHub Actions workflows (`fill` and `test`),\n * generated with commands matching the detected package manager and the\n * repository layout (see {@link GithubWorkflowsOptions} for the monorepo and\n * nested-project variants).\n */\nexport const getGithubWorkflows = (\n  packageManager: PackageManager,\n  options: GithubWorkflowsOptions = {}\n): GithubWorkflowFile[] => [\n  {\n    filePath: GITHUB_FILL_WORKFLOW_PATH,\n    content: generateFillWorkflow(packageManager, options),\n  },\n  {\n    filePath: GITHUB_TEST_WORKFLOW_PATH,\n    content: generateTestWorkflow(packageManager, options),\n  },\n];\n"],"mappings":"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"}