@cellajs/cli 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.js.map +1 -1
- package/dist/index.js +166 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/types.ts"],"sourcesContent":["/**\n * Sync CLI v2 - Configuration Types\n *\n * Simplified configuration for worktree-based merge approach.\n */\n\nimport { z } from 'zod';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PUBLIC API - for cella.config.ts\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Valid package.json keys that can be synced */\nexport type PackageJsonSyncKey =\n | 'dependencies'\n | 'devDependencies'\n | 'peerDependencies'\n | 'optionalDependencies'\n | 'scripts'\n | 'engines'\n | 'packageManager'\n | 'overrides'\n | 'pnpm';\n\n/**\n * Sync settings - all configurable options for the sync CLI.\n * Hover over each property for documentation.\n */\nexport interface SyncSettings {\n /** Upstream repository URL (e.g., 'git@github.com:cellajs/cella.git') */\n upstreamUrl: string;\n\n /**\n * Upstream branch, used for `upstreamTrack: 'branch'` and for GitHub file links.\n * Defaults to 'main'.\n */\n upstreamBranch?: string;\n\n /**\n * How to track upstream cella. Defaults to 'release'.\n * - 'release' (default): sync to a published cella release tag (`v*`). Stable and\n * reviewable — each bump maps to a changelog. Uses the latest release.\n * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella\n * maintainers and forks doing active development on top of unreleased changes.\n */\n upstreamTrack?: 'release' | 'branch';\n\n /**\n * Trunk branch that `pnpm cella sync` cuts the temporary branch from and that you open the\n * squash-merge PR into. Defaults to 'main'. Cutting fresh from trunk each cycle keeps every\n * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.\n */\n releaseBase?: string;\n\n /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']) */\n packageJsonSync?: PackageJsonSyncKey[];\n\n /**\n * Automatically run packages sync after the sync service completes.\n * When true (default), the packages service is hidden from the menu\n * and runs automatically as part of sync.\n * Set to false to keep packages as a separate manual service.\n */\n syncWithPackages?: boolean;\n\n /**\n * How to link files in CLI output.\n * - 'commit' (default): Link to the commit that changed the file on GitHub.\n * - 'file': Link to the file in the repo at the upstream branch on GitHub.\n * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.\n */\n fileLinkMode?: 'commit' | 'file' | 'local';\n}\n\n/**\n * Configuration for a downstream fork repository that cella interacts with.\n *\n * Cella is the upstream/source-of-truth. For each fork it can:\n * - pull contributions FROM the fork's `pullBranch` (contributions service)\n * - run the normal temporary-branch sync flow inside the fork (forks service)\n */\nexport interface ForkConfig {\n /** Display name for the fork (also used as the contrib/<name> branch slug) */\n name: string;\n /** Local path to the fork repository (relative to this config or absolute) */\n localPath: string;\n /**\n * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').\n * When set, the contributions service fetches the fork's `pullBranch` from this\n * remote (the authoritative committed ref) instead of the local clone, so the\n * comparison no longer depends on the local checkout being up to date. The local\n * clone is still used to read the fork's owned-folder territory when available.\n */\n remoteUrl?: string;\n /** Fork branch that cella pulls contributions from (contributions service) */\n pullBranch: string;\n}\n\n/**\n * User-configurable sync options for cella.config.ts.\n * Simplified for v2 - no long-lived sync branch or squash options.\n */\nexport interface CellaCliConfig {\n /** Core sync settings */\n settings: SyncSettings;\n\n /**\n * File overrides for controlling sync behavior per folder/file.\n */\n overrides?: {\n /**\n * Paths the fork fully owns — never synced (existing or new).\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Local territory: upstream cannot add, modify, or delete anything under these.\n */\n ignored?: string[];\n\n /**\n * Paths pinned to fork — fork wins on conflicts.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Non-conflicting upstream changes merge normally.\n * If fork deleted a pinned file, deletion is respected.\n */\n pinned?: string[];\n };\n\n /**\n * Downstream fork repositories that cella interacts with (for upstream template repos).\n * When configured, the 'forks' and 'contributions' services appear in the CLI menu.\n */\n forks?: ForkConfig[];\n}\n\n/**\n * Helper function for defining cella.config.ts with type checking.\n */\nexport function defineConfig(config: CellaCliConfig): CellaCliConfig {\n return config;\n}\n\n/**\n * Strict runtime schema mirroring {@link CellaCliConfig}.\n *\n * The TS interfaces above stay the source of truth (and keep their hover docs for\n * config authors). This schema is what's enforced at load time: `.strict()` rejects\n * unknown keys, so a typo like `ignoredFolders` instead of `ignored` fails closed\n * instead of silently dropping sync protection (tsx does not typecheck the config).\n *\n * The `satisfies z.ZodType<CellaCliConfig>` assertion keeps schema and interface in\n * lockstep: if either drifts (a renamed/removed/mis-typed field), TypeScript errors here.\n */\nexport const cellaConfigSchema = z\n .object({\n settings: z\n .object({\n upstreamUrl: z.string().min(1),\n upstreamBranch: z.string().min(1).optional(),\n upstreamTrack: z.enum(['release', 'branch']).optional(),\n releaseBase: z.string().min(1).optional(),\n packageJsonSync: z\n .array(\n z.enum([\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n 'scripts',\n 'engines',\n 'packageManager',\n 'overrides',\n 'pnpm',\n ]),\n )\n .optional(),\n syncWithPackages: z.boolean().optional(),\n fileLinkMode: z.enum(['commit', 'file', 'local']).optional(),\n })\n .strict(),\n overrides: z\n .object({\n ignored: z.array(z.string()).optional(),\n pinned: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n forks: z\n .array(\n z\n .object({\n name: z.string().min(1),\n localPath: z.string().min(1),\n remoteUrl: z.string().optional(),\n pullBranch: z.string().min(1),\n })\n .strict(),\n )\n .optional(),\n })\n .strict() satisfies z.ZodType<CellaCliConfig>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// INTERNAL TYPES - for sync modules\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Sync services available in the CLI */\nexport type SyncService = 'analyze' | 'sync' | 'audit' | 'forks' | 'contributions' | 'stats';\n\n/** Analyze output scope for interactive and machine-readable flows */\nexport type AnalyzeScope = 'all' | 'risk' | 'protected';\n\n/** Runtime configuration with all resolved values */\nexport interface RuntimeConfig extends CellaCliConfig {\n /** Resolved fork repository path */\n forkPath: string;\n\n /** Full upstream remote ref (e.g., 'cella-upstream/development') */\n upstreamRef: string;\n\n /** Selected sync service */\n service: SyncService;\n\n /** Write full file list to log file */\n logFile: boolean;\n\n /** Non-interactive list output (for LLM/agent usage) */\n list: boolean;\n\n /** Machine-readable JSON output (for tooling/agent usage) */\n json: boolean;\n\n /** Print the unified diff for a single contributed file, then exit (contributions; for tooling/agents) */\n diff?: string;\n\n /** Open a browser diff for one file, then exit (analyze) */\n openDiff?: string;\n\n /** Scope of files returned in analyze list/json output */\n scope?: AnalyzeScope;\n\n /** Show verbose output */\n verbose: boolean;\n\n /** Pre-selected fork name (skips fork selection prompt) */\n fork?: string;\n\n /** Overwrite drifted files with upstream version (aggressive realignment) */\n hard?: boolean;\n\n /**\n * Disable pinned entries (cella.config.ts overrides.pinned) for this sync so upstream\n * versions surface as behind/diverged. Managed files stay pinned because cella\n * handles them separately. Like --hard, uses the natural merge-base to resurface\n * full upstream history.\n */\n unpinned?: boolean;\n\n /**\n * Override upstream tracking for this run (analyze/sync), ignoring `settings.upstreamTrack`.\n * 'branch' follows the upstream branch tip (bleeding edge); 'release' uses a release tag.\n */\n track?: 'release' | 'branch';\n\n /** Bypass pnpm metadata cache for fresh registry data (audit service) */\n force?: boolean;\n\n /** Check which pnpm.overrides are still needed (audit service) */\n checkOverrides?: boolean;\n\n /** Regenerate test coverage before showing the stats summary (stats service) */\n coverage?: boolean;\n}\n\n/** File status after analysis */\nexport type FileStatus =\n | 'identical' // No changes needed\n | 'ahead' // Fork is ahead, protected (pinned/ignored)\n | 'local' // Local file (never existed upstream)\n | 'drifted' // Fork is ahead, NOT protected (at risk)\n | 'behind' // Upstream has changes to sync\n | 'diverged' // Both changed, will merge\n | 'pinned' // Fork wins on conflict\n | 'ignored' // Excluded from sync entirely\n | 'deleted' // Fork deleted, will stay deleted\n | 'renamed'; // Upstream renamed file\n\n/** Analyzed file with status and metadata */\nexport interface AnalyzedFile {\n path: string;\n status: FileStatus;\n /** True if file is in ignored list */\n isIgnored: boolean;\n /** True if file is in pinned list */\n isPinned: boolean;\n /** True if file exists in fork */\n existsInFork: boolean;\n /** True if file exists in upstream */\n existsInUpstream: boolean;\n /** True if file has merge conflict */\n hasConflict?: boolean;\n /** Relative date when file was last changed (since merge-base) */\n changedAt?: string;\n /** Unix epoch seconds of the last change, for sorting */\n changedTs?: number;\n /** Short commit hash of the last change */\n changedCommit?: string;\n /** For diverged files: relative date of upstream change */\n upstreamChangedAt?: string;\n /** For diverged files: Unix epoch seconds of the upstream change, for sorting */\n upstreamChangedTs?: number;\n /** For diverged files: short commit hash of the upstream change */\n upstreamCommit?: string;\n /** For renamed files: the original path before rename */\n renamedFrom?: string;\n}\n\n/** Summary counts by status */\nexport interface AnalysisSummary {\n /** Changed package/config files that are handled separately from normal sync categories */\n managed: number;\n identical: number;\n ahead: number;\n local: number;\n drifted: number;\n behind: number;\n diverged: number;\n pinned: number;\n ignored: number;\n deleted: number;\n renamed: number;\n total: number;\n}\n\n/** Merge result from merge-engine */\nexport interface MergeResult {\n success: boolean;\n /** Upstream branch name for file links */\n upstreamBranch?: string;\n /** Resolved concrete upstream ref that was merged (branch ref or release tag ref) */\n upstreamRef?: string;\n /** Upstream release tag synced (when tracking releases) */\n upstreamTag?: string;\n files: AnalyzedFile[];\n summary: AnalysisSummary;\n conflicts: string[];\n /** Upstream GitHub URL base for commit links */\n upstreamGitHubUrl?: string;\n /** Upstream commit info */\n upstreamCommit?: {\n hash: string;\n message: string;\n date: string;\n };\n /** Commits included in this sync range (oldest-first when rendered) */\n upstreamCommits?: Array<{\n hash: string;\n message: string;\n date: string;\n }>;\n /** Files that were auto-merged by git (diverged without remaining conflicts) */\n autoMergedFiles?: string[];\n}\n"],"mappings":";AAMA,SAAS,SAAS;AAoIX,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;AAaO,IAAM,oBAAoB,EAC9B,OAAO;AAAA,EACN,UAAU,EACP,OAAO;AAAA,IACN,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC7B,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IAC3C,eAAe,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC,EAAE,SAAS;AAAA,IACtD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACxC,iBAAiB,EACd;AAAA,MACC,EAAE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EACC,SAAS;AAAA,IACZ,kBAAkB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACvC,cAAc,EAAE,KAAK,CAAC,UAAU,QAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,EAC7D,CAAC,EACA,OAAO;AAAA,EACV,WAAW,EACR,OAAO;AAAA,IACN,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACtC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACvC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,OAAO,EACJ;AAAA,IACC,EACG,OAAO;AAAA,MACN,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MACtB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MAC3B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,MAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC9B,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AACd,CAAC,EACA,OAAO;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/config/types.ts"],"sourcesContent":["/**\n * Sync CLI v2 - Configuration Types\n *\n * Simplified configuration for worktree-based merge approach.\n */\n\nimport { z } from 'zod';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PUBLIC API - for cella.config.ts\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Valid package.json keys that can be synced */\nexport type PackageJsonSyncKey =\n | 'dependencies'\n | 'devDependencies'\n | 'peerDependencies'\n | 'optionalDependencies'\n | 'scripts'\n | 'engines'\n | 'packageManager'\n | 'overrides'\n | 'pnpm';\n\n/**\n * Sync settings - all configurable options for the sync CLI.\n * Hover over each property for documentation.\n */\nexport interface SyncSettings {\n /** Upstream repository URL (e.g., 'git@github.com:cellajs/cella.git') */\n upstreamUrl: string;\n\n /**\n * Upstream branch, used for `upstreamTrack: 'branch'` and for GitHub file links.\n * Defaults to 'main'.\n */\n upstreamBranch?: string;\n\n /**\n * How to track upstream cella. Defaults to 'release'.\n * - 'release' (default): sync to a published cella release tag (`v*`). Stable and\n * reviewable — each bump maps to a changelog. Uses the latest release.\n * - 'branch': follow the bleeding-edge tip of `upstreamBranch`. For cella\n * maintainers and forks doing active development on top of unreleased changes.\n */\n upstreamTrack?: 'release' | 'branch';\n\n /**\n * Trunk branch that `pnpm cella sync` cuts the temporary branch from and that you open the\n * squash-merge PR into. Defaults to 'main'. Cutting fresh from trunk each cycle keeps every\n * PR limited to that cycle's upstream delta (no accumulation) and `main` linear.\n */\n releaseBase?: string;\n\n /** Which package.json keys to sync (default: ['dependencies', 'devDependencies']) */\n packageJsonSync?: PackageJsonSyncKey[];\n\n /**\n * Automatically run packages sync after the sync service completes.\n * When true (default), the packages service is hidden from the menu\n * and runs automatically as part of sync.\n * Set to false to keep packages as a separate manual service.\n */\n syncWithPackages?: boolean;\n\n /**\n * How to link files in CLI output.\n * - 'commit' (default): Link to the commit that changed the file on GitHub.\n * - 'file': Link to the file in the repo at the upstream branch on GitHub.\n * - 'local': Deprecated (the upstream view worktree was replaced by browser diffs); treated as 'file'.\n */\n fileLinkMode?: 'commit' | 'file' | 'local';\n}\n\n/**\n * Configuration for a downstream fork repository that cella interacts with.\n *\n * Cella is the upstream/source-of-truth. For each fork it can:\n * - pull contributions FROM the fork's `pullBranch` (contributions service)\n * - run the normal temporary-branch sync flow inside the fork (forks service)\n */\nexport interface ForkConfig {\n /** Display name for the fork (also used as the contrib/<name> branch slug) */\n name: string;\n /** Local path to the fork repository (relative to this config or absolute) */\n localPath: string;\n /**\n * Git remote URL of the fork (e.g. 'git@github.com:org/fork.git').\n * When set, the contributions service fetches the fork's `pullBranch` from this\n * remote (the authoritative committed ref) instead of the local clone, so the\n * comparison no longer depends on the local checkout being up to date. The local\n * clone is still used to read the fork's owned-folder territory when available.\n */\n remoteUrl?: string;\n /** Fork branch that cella pulls contributions from (contributions service) */\n pullBranch: string;\n}\n\n/**\n * User-configurable sync options for cella.config.ts.\n * Simplified for v2 - no long-lived sync branch or squash options.\n */\nexport interface CellaCliConfig {\n /** Core sync settings */\n settings: SyncSettings;\n\n /**\n * File overrides for controlling sync behavior per folder/file.\n */\n overrides?: {\n /**\n * Paths the fork fully owns — never synced (existing or new).\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Local territory: upstream cannot add, modify, or delete anything under these.\n */\n ignored?: string[];\n\n /**\n * Paths pinned to fork — fork wins on conflicts.\n * Exact paths or directory prefixes, not globs: 'bench' matches 'bench/' and\n * everything under it, 'README.md' matches that exact file.\n * Non-conflicting upstream changes merge normally.\n * If fork deleted a pinned file, deletion is respected.\n */\n pinned?: string[];\n };\n\n /**\n * Downstream fork repositories that cella interacts with (for upstream template repos).\n * When configured, the 'forks' and 'contributions' services appear in the CLI menu.\n */\n forks?: ForkConfig[];\n}\n\n/**\n * Helper function for defining cella.config.ts with type checking.\n */\nexport function defineConfig(config: CellaCliConfig): CellaCliConfig {\n return config;\n}\n\n/**\n * Strict runtime schema mirroring {@link CellaCliConfig}.\n *\n * The TS interfaces above stay the source of truth (and keep their hover docs for\n * config authors). This schema is what's enforced at load time: `.strict()` rejects\n * unknown keys, so a typo like `ignoredFolders` instead of `ignored` fails closed\n * instead of silently dropping sync protection (tsx does not typecheck the config).\n *\n * The `satisfies z.ZodType<CellaCliConfig>` assertion keeps schema and interface in\n * lockstep: if either drifts (a renamed/removed/mis-typed field), TypeScript errors here.\n */\nexport const cellaConfigSchema = z\n .object({\n settings: z\n .object({\n upstreamUrl: z.string().min(1),\n upstreamBranch: z.string().min(1).optional(),\n upstreamTrack: z.enum(['release', 'branch']).optional(),\n releaseBase: z.string().min(1).optional(),\n packageJsonSync: z\n .array(\n z.enum([\n 'dependencies',\n 'devDependencies',\n 'peerDependencies',\n 'optionalDependencies',\n 'scripts',\n 'engines',\n 'packageManager',\n 'overrides',\n 'pnpm',\n ]),\n )\n .optional(),\n syncWithPackages: z.boolean().optional(),\n fileLinkMode: z.enum(['commit', 'file', 'local']).optional(),\n })\n .strict(),\n overrides: z\n .object({\n ignored: z.array(z.string()).optional(),\n pinned: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n forks: z\n .array(\n z\n .object({\n name: z.string().min(1),\n localPath: z.string().min(1),\n remoteUrl: z.string().optional(),\n pullBranch: z.string().min(1),\n })\n .strict(),\n )\n .optional(),\n })\n .strict() satisfies z.ZodType<CellaCliConfig>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// INTERNAL TYPES - for sync modules\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Sync services available in the CLI */\nexport type SyncService = 'analyze' | 'sync' | 'audit' | 'forks' | 'contributions' | 'stats';\n\n/** Analyze output scope for interactive and machine-readable flows */\nexport type AnalyzeScope = 'all' | 'risk' | 'protected';\n\n/** Runtime configuration with all resolved values */\nexport interface RuntimeConfig extends CellaCliConfig {\n /** Resolved fork repository path */\n forkPath: string;\n\n /** Full upstream remote ref (e.g., 'cella-upstream/development') */\n upstreamRef: string;\n\n /** Selected sync service */\n service: SyncService;\n\n /** Write full file list to log file */\n logFile: boolean;\n\n /** Non-interactive list output (for LLM/agent usage) */\n list: boolean;\n\n /** Machine-readable JSON output (for tooling/agent usage) */\n json: boolean;\n\n /** Print the unified diff for a single contributed file, then exit (contributions; for tooling/agents) */\n diff?: string;\n\n /** Open a browser diff for one file, then exit (analyze) */\n openDiff?: string;\n\n /** Scope of files returned in analyze list/json output */\n scope?: AnalyzeScope;\n\n /** Show verbose output */\n verbose: boolean;\n\n /** Pre-selected fork name (skips fork selection prompt) */\n fork?: string;\n\n /** Overwrite drifted files with upstream version (aggressive realignment) */\n hard?: boolean;\n\n /**\n * Disable pinned entries (cella.config.ts overrides.pinned) for this sync so upstream\n * versions surface as behind/diverged. Managed files stay pinned because cella\n * handles them separately. Like --hard, uses the natural merge-base to resurface\n * full upstream history.\n */\n unpinned?: boolean;\n\n /**\n * Override upstream tracking for this run (analyze/sync), ignoring `settings.upstreamTrack`.\n * 'branch' follows the upstream branch tip (bleeding edge); 'release' uses a release tag.\n */\n track?: 'release' | 'branch';\n\n /** Bypass pnpm metadata cache for fresh registry data (audit service) */\n force?: boolean;\n\n /** Check which pnpm.overrides are still needed (audit service) */\n checkOverrides?: boolean;\n\n /** Regenerate test coverage before showing the stats summary (stats service) */\n coverage?: boolean;\n\n /**\n * After opening the sync PR, enable GitHub auto-merge (squash) so it merges itself once\n * required checks pass (sync service). Opt-in per run via `--direct-merge`; needs auto-merge\n * enabled on the repo. Not the advised default — a sync PR is usually worth a human look.\n */\n directMerge?: boolean;\n}\n\n/** File status after analysis */\nexport type FileStatus =\n | 'identical' // No changes needed\n | 'ahead' // Fork is ahead, protected (pinned/ignored)\n | 'local' // Local file (never existed upstream)\n | 'drifted' // Fork is ahead, NOT protected (at risk)\n | 'behind' // Upstream has changes to sync\n | 'diverged' // Both changed, will merge\n | 'pinned' // Fork wins on conflict\n | 'ignored' // Excluded from sync entirely\n | 'deleted' // Fork deleted, will stay deleted\n | 'renamed'; // Upstream renamed file\n\n/** Analyzed file with status and metadata */\nexport interface AnalyzedFile {\n path: string;\n status: FileStatus;\n /** True if file is in ignored list */\n isIgnored: boolean;\n /** True if file is in pinned list */\n isPinned: boolean;\n /** True if file exists in fork */\n existsInFork: boolean;\n /** True if file exists in upstream */\n existsInUpstream: boolean;\n /** True if file has merge conflict */\n hasConflict?: boolean;\n /** Relative date when file was last changed (since merge-base) */\n changedAt?: string;\n /** Unix epoch seconds of the last change, for sorting */\n changedTs?: number;\n /** Short commit hash of the last change */\n changedCommit?: string;\n /** For diverged files: relative date of upstream change */\n upstreamChangedAt?: string;\n /** For diverged files: Unix epoch seconds of the upstream change, for sorting */\n upstreamChangedTs?: number;\n /** For diverged files: short commit hash of the upstream change */\n upstreamCommit?: string;\n /** For renamed files: the original path before rename */\n renamedFrom?: string;\n}\n\n/** Summary counts by status */\nexport interface AnalysisSummary {\n /** Changed package/config files that are handled separately from normal sync categories */\n managed: number;\n identical: number;\n ahead: number;\n local: number;\n drifted: number;\n behind: number;\n diverged: number;\n pinned: number;\n ignored: number;\n deleted: number;\n renamed: number;\n total: number;\n}\n\n/** Merge result from merge-engine */\nexport interface MergeResult {\n success: boolean;\n /** Upstream branch name for file links */\n upstreamBranch?: string;\n /** Resolved concrete upstream ref that was merged (branch ref or release tag ref) */\n upstreamRef?: string;\n /** Upstream release tag synced (when tracking releases) */\n upstreamTag?: string;\n files: AnalyzedFile[];\n summary: AnalysisSummary;\n conflicts: string[];\n /** Upstream GitHub URL base for commit links */\n upstreamGitHubUrl?: string;\n /** Upstream commit info */\n upstreamCommit?: {\n hash: string;\n message: string;\n date: string;\n };\n /** Commits included in this sync range (oldest-first when rendered) */\n upstreamCommits?: Array<{\n hash: string;\n message: string;\n date: string;\n }>;\n /** Files that were auto-merged by git (diverged without remaining conflicts) */\n autoMergedFiles?: string[];\n}\n"],"mappings":";AAMA,SAAS,SAAS;AAoIX,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;AAaO,IAAM,oBAAoB,EAC9B,OAAO;AAAA,EACN,UAAU,EACP,OAAO;AAAA,IACN,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC7B,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IAC3C,eAAe,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC,EAAE,SAAS;AAAA,IACtD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACxC,iBAAiB,EACd;AAAA,MACC,EAAE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EACC,SAAS;AAAA,IACZ,kBAAkB,EAAE,QAAQ,EAAE,SAAS;AAAA,IACvC,cAAc,EAAE,KAAK,CAAC,UAAU,QAAQ,OAAO,CAAC,EAAE,SAAS;AAAA,EAC7D,CAAC,EACA,OAAO;AAAA,EACV,WAAW,EACR,OAAO;AAAA,IACN,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACtC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACvC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,OAAO,EACJ;AAAA,IACC,EACG,OAAO;AAAA,MACN,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MACtB,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,MAC3B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,MAC/B,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,IAC9B,CAAC,EACA,OAAO;AAAA,EACZ,EACC,SAAS;AACd,CAAC,EACA,OAAO;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -196,7 +196,7 @@ import process4 from "process";
|
|
|
196
196
|
// package.json
|
|
197
197
|
var package_default = {
|
|
198
198
|
name: "@cellajs/cli",
|
|
199
|
-
version: "0.0.
|
|
199
|
+
version: "0.0.6",
|
|
200
200
|
private: false,
|
|
201
201
|
license: "MIT",
|
|
202
202
|
description: "CLI for managing your monorepo and syncing with upstream Cella.",
|
|
@@ -747,7 +747,8 @@ function readOptions(opts) {
|
|
|
747
747
|
track: opts.track === "release" || opts.track === "branch" ? opts.track : void 0,
|
|
748
748
|
force: opts.force === true,
|
|
749
749
|
checkOverrides: opts.checkOverrides === true,
|
|
750
|
-
coverage: opts.coverage === true
|
|
750
|
+
coverage: opts.coverage === true,
|
|
751
|
+
directMerge: opts.directMerge === true
|
|
751
752
|
};
|
|
752
753
|
}
|
|
753
754
|
var serviceDefinitions = [
|
|
@@ -772,7 +773,11 @@ var serviceDefinitions = [
|
|
|
772
773
|
{ flags: "--log", description: "write complete file list to cella-sync.log" },
|
|
773
774
|
{ flags: "--hard", description: "overwrite drifted files with upstream version (aggressive realignment)" },
|
|
774
775
|
{ flags: "--unpinned", description: "ignore pinned files (except package.json) to resurface upstream changes" },
|
|
775
|
-
{ flags: "--track <mode>", description: "override upstream tracking for this run: release|branch" }
|
|
776
|
+
{ flags: "--track <mode>", description: "override upstream tracking for this run: release|branch" },
|
|
777
|
+
{
|
|
778
|
+
flags: "--direct-merge",
|
|
779
|
+
description: "auto-merge (squash) the sync PR once checks pass \u2014 needs auto-merge enabled on the repo"
|
|
780
|
+
}
|
|
776
781
|
],
|
|
777
782
|
includeInMenu: (context) => !context.isUpstreamRepo,
|
|
778
783
|
menuDescription: () => "merge upstream changes + sync package.json"
|
|
@@ -859,6 +864,7 @@ function buildProgram(setSelection) {
|
|
|
859
864
|
" $ cella sync --hard",
|
|
860
865
|
" $ cella sync --unpinned",
|
|
861
866
|
" $ cella sync --track branch",
|
|
867
|
+
" $ cella sync --direct-merge",
|
|
862
868
|
" $ cella audit --check-overrides",
|
|
863
869
|
" $ cella contributions --fork raak --json"
|
|
864
870
|
].join("\n")
|
|
@@ -1570,6 +1576,21 @@ async function getEffectiveMergeBase(cwd, headRef, upstreamRef) {
|
|
|
1570
1576
|
}
|
|
1571
1577
|
return { base: gitBase, isStale: false, storedRef: recorded };
|
|
1572
1578
|
}
|
|
1579
|
+
async function withTemporarySyncBaseGraft(cwd, headRef, baseSha, fn) {
|
|
1580
|
+
const headSha = await git(["rev-parse", headRef], cwd, { ignoreErrors: true });
|
|
1581
|
+
if (!headSha || !await commitObjectExists(cwd, baseSha)) return fn();
|
|
1582
|
+
if (await isAncestor(cwd, baseSha, headSha)) return fn();
|
|
1583
|
+
const existingReplacement = await git(["replace", "-l", headSha], cwd, { ignoreErrors: true });
|
|
1584
|
+
if (existingReplacement) return fn();
|
|
1585
|
+
const parentsRaw = await git(["log", "-1", "--format=%P", headSha], cwd, { ignoreErrors: true });
|
|
1586
|
+
const parents = parentsRaw ? parentsRaw.split(/\s+/) : [];
|
|
1587
|
+
await git(["replace", "-f", "--graft", headSha, ...parents, baseSha], cwd);
|
|
1588
|
+
try {
|
|
1589
|
+
return await fn();
|
|
1590
|
+
} finally {
|
|
1591
|
+
await git(["replace", "-d", headSha], cwd, { ignoreErrors: true });
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1573
1594
|
|
|
1574
1595
|
// src/utils/cleanup.ts
|
|
1575
1596
|
var WORKTREE_PREFIXES = {
|
|
@@ -1847,7 +1868,12 @@ async function applyDirectMerge(forkPath, upstreamRef, mergeBaseRef, config, onP
|
|
|
1847
1868
|
}
|
|
1848
1869
|
}
|
|
1849
1870
|
onProgress?.("starting merge in fork...");
|
|
1850
|
-
await
|
|
1871
|
+
await withTemporarySyncBaseGraft(
|
|
1872
|
+
forkPath,
|
|
1873
|
+
"HEAD",
|
|
1874
|
+
mergeBaseRef,
|
|
1875
|
+
() => merge(forkPath, upstreamRef, { noCommit: true, noEdit: true })
|
|
1876
|
+
);
|
|
1851
1877
|
if (batchRestorePaths.length > 0) {
|
|
1852
1878
|
onProgress?.(`batch restoring ${batchRestorePaths.length} pinned/ignored files...`);
|
|
1853
1879
|
await batchRestoreToHead(forkPath, batchRestorePaths);
|
|
@@ -2100,7 +2126,12 @@ async function runAnalyzePreview(config, ctx, onProgress, onStep) {
|
|
|
2100
2126
|
await createWorktree(forkPath, worktreePath, "HEAD");
|
|
2101
2127
|
onStep?.("worktree created", worktreePath);
|
|
2102
2128
|
onProgress?.("performing merge in worktree...");
|
|
2103
|
-
await
|
|
2129
|
+
await withTemporarySyncBaseGraft(
|
|
2130
|
+
worktreePath,
|
|
2131
|
+
"HEAD",
|
|
2132
|
+
ctx.mergeBase,
|
|
2133
|
+
() => merge(worktreePath, ctx.upstreamRef, { noCommit: true, noEdit: true })
|
|
2134
|
+
);
|
|
2104
2135
|
onStep?.("merge complete", "upstream merged into worktree");
|
|
2105
2136
|
onProgress?.("analyzing files...");
|
|
2106
2137
|
const analyzedFiles = await analyzeRefs(
|
|
@@ -3516,10 +3547,57 @@ async function runContributions(config) {
|
|
|
3516
3547
|
// src/services/forks.ts
|
|
3517
3548
|
import { resolve as resolve4 } from "path";
|
|
3518
3549
|
import process8 from "process";
|
|
3519
|
-
import { Separator as Separator2, select as
|
|
3550
|
+
import { Separator as Separator2, select as select4 } from "@inquirer/prompts";
|
|
3520
3551
|
|
|
3521
3552
|
// src/services/sync.ts
|
|
3553
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
3554
|
+
import { select as select3 } from "@inquirer/prompts";
|
|
3555
|
+
|
|
3556
|
+
// src/utils/gh.ts
|
|
3522
3557
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3558
|
+
function ghAvailable() {
|
|
3559
|
+
return spawnSync2("gh", ["--version"], { stdio: "ignore" }).status === 0;
|
|
3560
|
+
}
|
|
3561
|
+
function runGh(cwd, args) {
|
|
3562
|
+
const result = spawnSync2("gh", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
3563
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
3564
|
+
return { ok: result.status === 0, output };
|
|
3565
|
+
}
|
|
3566
|
+
function filterSyncPrs(prs, prefix) {
|
|
3567
|
+
return prs.filter((pr) => pr.headRefName.startsWith(`${prefix}/`));
|
|
3568
|
+
}
|
|
3569
|
+
function listOpenSyncPrs(cwd, prefix) {
|
|
3570
|
+
const { ok, output } = runGh(cwd, [
|
|
3571
|
+
"pr",
|
|
3572
|
+
"list",
|
|
3573
|
+
"--state",
|
|
3574
|
+
"open",
|
|
3575
|
+
"--json",
|
|
3576
|
+
"number,title,headRefName,url",
|
|
3577
|
+
"--limit",
|
|
3578
|
+
"50"
|
|
3579
|
+
]);
|
|
3580
|
+
if (!ok || !output) return [];
|
|
3581
|
+
let parsed;
|
|
3582
|
+
try {
|
|
3583
|
+
parsed = JSON.parse(output);
|
|
3584
|
+
} catch {
|
|
3585
|
+
return [];
|
|
3586
|
+
}
|
|
3587
|
+
return filterSyncPrs(parsed, prefix).sort((a, b) => b.number - a.number);
|
|
3588
|
+
}
|
|
3589
|
+
function buildMergeArgs(ref, options = {}) {
|
|
3590
|
+
const args = ["pr", "merge", ref, "--squash"];
|
|
3591
|
+
if (options.auto) args.push("--auto");
|
|
3592
|
+
if (options.deleteBranch) args.push("--delete-branch");
|
|
3593
|
+
return args;
|
|
3594
|
+
}
|
|
3595
|
+
function mergePrSquash(cwd, ref, options = {}) {
|
|
3596
|
+
return runGh(cwd, buildMergeArgs(String(ref), options));
|
|
3597
|
+
}
|
|
3598
|
+
function closePr(cwd, ref) {
|
|
3599
|
+
return runGh(cwd, ["pr", "close", String(ref)]);
|
|
3600
|
+
}
|
|
3523
3601
|
|
|
3524
3602
|
// src/services/packages.ts
|
|
3525
3603
|
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
@@ -3859,9 +3937,6 @@ function printFinishSteps() {
|
|
|
3859
3937
|
function hasStagedSyncChanges(result) {
|
|
3860
3938
|
return result.files.some((file) => ["behind", "diverged", "renamed", "ignored", "pinned"].includes(file.status));
|
|
3861
3939
|
}
|
|
3862
|
-
function ghAvailable() {
|
|
3863
|
-
return spawnSync2("gh", ["--version"], { stdio: "ignore" }).status === 0;
|
|
3864
|
-
}
|
|
3865
3940
|
function extractFirstUrl(output) {
|
|
3866
3941
|
return output.match(/https?:\/\/\S+/)?.[0];
|
|
3867
3942
|
}
|
|
@@ -3878,12 +3953,25 @@ async function flattenSyncBranch(forkPath, branch, base) {
|
|
|
3878
3953
|
await flattenBranch(forkPath, base, message);
|
|
3879
3954
|
return true;
|
|
3880
3955
|
}
|
|
3956
|
+
function indentLines(text) {
|
|
3957
|
+
return text.split("\n").map((line) => ` ${line}`).join("\n");
|
|
3958
|
+
}
|
|
3959
|
+
function enableAutoMerge(forkPath, branch) {
|
|
3960
|
+
console.info(colors_default.dim("enabling auto-merge (squash) once checks pass..."));
|
|
3961
|
+
const merge2 = mergePrSquash(forkPath, branch, { auto: true, deleteBranch: true });
|
|
3962
|
+
if (merge2.ok) return true;
|
|
3963
|
+
console.info(colors_default.yellow("could not enable auto-merge (is it enabled for the repo?). merge it manually:"));
|
|
3964
|
+
if (merge2.output) console.info(colors_default.dim(indentLines(merge2.output)));
|
|
3965
|
+
console.info(colors_default.dim(` gh pr merge ${branch} --squash --delete-branch`));
|
|
3966
|
+
return false;
|
|
3967
|
+
}
|
|
3881
3968
|
async function shipSyncBranch(config, branch) {
|
|
3882
3969
|
const { forkPath, settings } = config;
|
|
3883
3970
|
const base = resolveReleaseBase(settings);
|
|
3884
3971
|
const flattened = await flattenSyncBranch(forkPath, branch, base);
|
|
3885
3972
|
let prUrl;
|
|
3886
3973
|
let prOpened = false;
|
|
3974
|
+
let autoMergeEnabled = false;
|
|
3887
3975
|
const headSubject = (await getCommitInfo(forkPath, "HEAD").catch(() => null))?.message;
|
|
3888
3976
|
const prTitle = headSubject?.startsWith(SYNC_PR_TITLE) ? headSubject : SYNC_PR_TITLE;
|
|
3889
3977
|
console.info(colors_default.dim(`pushing '${branch}' to origin...`));
|
|
@@ -3899,7 +3987,7 @@ async function shipSyncBranch(config, branch) {
|
|
|
3899
3987
|
console.info(colors_default.dim("opening a pull request..."));
|
|
3900
3988
|
const prBody = await buildSyncPrBodyForBranch(forkPath, base);
|
|
3901
3989
|
const bodyArgs = prBody ? ["--body", prBody] : ["--fill"];
|
|
3902
|
-
const pr =
|
|
3990
|
+
const pr = spawnSync3("gh", ["pr", "create", "--base", base, "--head", branch, "--title", prTitle, ...bodyArgs], {
|
|
3903
3991
|
cwd: forkPath,
|
|
3904
3992
|
encoding: "utf-8",
|
|
3905
3993
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3907,7 +3995,9 @@ async function shipSyncBranch(config, branch) {
|
|
|
3907
3995
|
prUrl = extractFirstUrl(`${pr.stdout ?? ""}
|
|
3908
3996
|
${pr.stderr ?? ""}`);
|
|
3909
3997
|
prOpened = pr.status === 0;
|
|
3910
|
-
if (
|
|
3998
|
+
if (prOpened) {
|
|
3999
|
+
if (config.directMerge) autoMergeEnabled = enableAutoMerge(forkPath, branch);
|
|
4000
|
+
} else {
|
|
3911
4001
|
console.info(colors_default.yellow("could not open the PR automatically (it may already exist). open it with:"));
|
|
3912
4002
|
if (prUrl) console.info(colors_default.dim(` ${prUrl}`));
|
|
3913
4003
|
printPrCreateStep(branch, base, prTitle);
|
|
@@ -3923,7 +4013,8 @@ ${pr.stderr ?? ""}`);
|
|
|
3923
4013
|
console.info();
|
|
3924
4014
|
if (prUrl) {
|
|
3925
4015
|
console.info(`${colors_default.green("\u2713")} Sync pull request ${prOpened ? "opened" : "ready"}`);
|
|
3926
|
-
|
|
4016
|
+
const status = autoMergeEnabled ? `auto-merge on \u2014 squashes into '${base}' when checks pass` : `branch pushed, back on '${base}'`;
|
|
4017
|
+
console.info(colors_default.dim(` ${prUrl} \xB7 ${status}`));
|
|
3927
4018
|
} else {
|
|
3928
4019
|
console.info(`${colors_default.green("\u2713")} Sync branch pushed`);
|
|
3929
4020
|
console.info(colors_default.dim(` '${branch}' is on origin, back on '${base}'`));
|
|
@@ -3932,7 +4023,7 @@ ${pr.stderr ?? ""}`);
|
|
|
3932
4023
|
function finalizeWorkspace(forkPath) {
|
|
3933
4024
|
for (const args of [["install"], ["check"]]) {
|
|
3934
4025
|
console.info(colors_default.dim(`running pnpm ${args.join(" ")}...`));
|
|
3935
|
-
const result =
|
|
4026
|
+
const result = spawnSync3("pnpm", args, { cwd: forkPath, stdio: "inherit" });
|
|
3936
4027
|
if (result.status !== 0) return false;
|
|
3937
4028
|
}
|
|
3938
4029
|
return true;
|
|
@@ -3977,6 +4068,64 @@ async function resumeSyncMerge(config, branch) {
|
|
|
3977
4068
|
console.info(colors_default.green(`committed the sync on '${branch}' as '${message}'.`));
|
|
3978
4069
|
await shipSyncBranch(config, branch);
|
|
3979
4070
|
}
|
|
4071
|
+
async function mergeOpenSyncPrs(config, open) {
|
|
4072
|
+
const { forkPath, settings } = config;
|
|
4073
|
+
const base = resolveReleaseBase(settings);
|
|
4074
|
+
const [newest, ...superseded] = open;
|
|
4075
|
+
console.info();
|
|
4076
|
+
console.info(colors_default.dim(`squash-merging #${newest.number} into '${base}'...`));
|
|
4077
|
+
const merged = mergePrSquash(forkPath, newest.number, { deleteBranch: true });
|
|
4078
|
+
if (!merged.ok) {
|
|
4079
|
+
console.info();
|
|
4080
|
+
console.info(colors_default.yellow(`could not merge #${newest.number} \u2014 resolve it first, then re-run \`pnpm cella sync\`:`));
|
|
4081
|
+
if (merged.output) console.info(colors_default.dim(indentLines(merged.output)));
|
|
4082
|
+
console.info(colors_default.dim(` ${newest.url}`));
|
|
4083
|
+
return "cancel";
|
|
4084
|
+
}
|
|
4085
|
+
console.info(`${colors_default.green("\u2713")} merged #${newest.number}`);
|
|
4086
|
+
await deleteBranch(forkPath, newest.headRefName);
|
|
4087
|
+
for (const pr of superseded) {
|
|
4088
|
+
console.info(colors_default.dim(`closing #${pr.number} \u2014 its changes are included in #${newest.number}...`));
|
|
4089
|
+
closePr(forkPath, pr.number);
|
|
4090
|
+
await deleteBranch(forkPath, pr.headRefName);
|
|
4091
|
+
}
|
|
4092
|
+
console.info(colors_default.dim(`switching to '${base}' and fast-forwarding...`));
|
|
4093
|
+
await switchBranch(forkPath, base);
|
|
4094
|
+
await pullFastForward(forkPath);
|
|
4095
|
+
return "continue";
|
|
4096
|
+
}
|
|
4097
|
+
async function guardAgainstOpenSyncPr(config) {
|
|
4098
|
+
const { forkPath } = config;
|
|
4099
|
+
if (!ghAvailable() || !process.stdout.isTTY) return "continue";
|
|
4100
|
+
const open = listOpenSyncPrs(forkPath, DEFAULT_SYNC_PREFIX);
|
|
4101
|
+
if (open.length === 0) return "continue";
|
|
4102
|
+
const many = open.length > 1;
|
|
4103
|
+
console.info();
|
|
4104
|
+
console.info(
|
|
4105
|
+
`${warningMark} ${colors_default.yellow(`${many ? `${open.length} earlier sync PRs are` : "an earlier sync PR is"} still open and unmerged:`)}`
|
|
4106
|
+
);
|
|
4107
|
+
for (const pr of open) {
|
|
4108
|
+
console.info(colors_default.dim(` #${pr.number} ${pr.title}`));
|
|
4109
|
+
console.info(colors_default.dim(` ${pr.url}`));
|
|
4110
|
+
}
|
|
4111
|
+
console.info(colors_default.dim(` starting a new sync now produces a PR that re-includes ${many ? "these" : "those"} changes.`));
|
|
4112
|
+
console.info();
|
|
4113
|
+
const choice = await select3({
|
|
4114
|
+
message: "how do you want to proceed?",
|
|
4115
|
+
choices: [
|
|
4116
|
+
{
|
|
4117
|
+
value: "merge",
|
|
4118
|
+
name: `merge ${many ? "them" : `#${open[0].number}`} first, then sync ${colors_default.dim("(recommended)")}`
|
|
4119
|
+
},
|
|
4120
|
+
{ value: "continue", name: `continue anyway ${colors_default.dim("(new PR will re-include the unmerged changes)")}` },
|
|
4121
|
+
{ value: "cancel", name: colors_default.red("cancel") }
|
|
4122
|
+
],
|
|
4123
|
+
loop: false
|
|
4124
|
+
});
|
|
4125
|
+
if (choice === "cancel") return "cancel";
|
|
4126
|
+
if (choice === "continue") return "continue";
|
|
4127
|
+
return mergeOpenSyncPrs(config, open);
|
|
4128
|
+
}
|
|
3980
4129
|
async function runSyncCommand(config) {
|
|
3981
4130
|
const { forkPath } = config;
|
|
3982
4131
|
const currentBranch = await getCurrentBranch(forkPath);
|
|
@@ -4000,6 +4149,7 @@ commit them first (\`git commit --amend --no-edit\` or a new commit).`
|
|
|
4000
4149
|
return;
|
|
4001
4150
|
}
|
|
4002
4151
|
await assertClean(forkPath);
|
|
4152
|
+
if (await guardAgainstOpenSyncPr(config) === "cancel") return;
|
|
4003
4153
|
const outcome = await runSyncCycle(config);
|
|
4004
4154
|
console.info();
|
|
4005
4155
|
if (outcome.status === "noop") {
|
|
@@ -4108,7 +4258,7 @@ async function runForks(config) {
|
|
|
4108
4258
|
while (true) {
|
|
4109
4259
|
const choices = await buildForkChoices(forks, config.forkPath);
|
|
4110
4260
|
const forkChoices = [...choices, new Separator2("\u2500".repeat(40)), { value: "_exit", name: colors_default.dim("exit") }];
|
|
4111
|
-
const selectedPath = await
|
|
4261
|
+
const selectedPath = await select4({
|
|
4112
4262
|
message: "select fork to sync:",
|
|
4113
4263
|
choices: forkChoices,
|
|
4114
4264
|
loop: false
|
|
@@ -4132,7 +4282,7 @@ import { readFile as readFile2 } from "fs/promises";
|
|
|
4132
4282
|
import { join as join16 } from "path";
|
|
4133
4283
|
|
|
4134
4284
|
// src/utils/coverage-utils.ts
|
|
4135
|
-
import { spawnSync as
|
|
4285
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
4136
4286
|
import * as fs3 from "fs";
|
|
4137
4287
|
import * as path3 from "path";
|
|
4138
4288
|
var STALE_THRESHOLD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -4155,7 +4305,7 @@ function formatAge(ms) {
|
|
|
4155
4305
|
}
|
|
4156
4306
|
function refreshCoverage(forkPath) {
|
|
4157
4307
|
console.info(colors_default.dim("running `pnpm test` (this spins up Docker and runs the full suite)..."));
|
|
4158
|
-
const result =
|
|
4308
|
+
const result = spawnSync4("pnpm", ["test"], { cwd: forkPath, stdio: "inherit" });
|
|
4159
4309
|
return result.status === 0;
|
|
4160
4310
|
}
|
|
4161
4311
|
function printCoverageSummary(forkPath, options = {}) {
|