@teambit/ci 1.0.637 → 1.0.638
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/ci.docs.mdx +13 -7
- package/commands/sync.cmd.ts +5 -2
- package/dist/ci.docs.mdx +13 -7
- package/dist/ci.main.runtime.d.ts +6 -4
- package/dist/ci.main.runtime.js +19 -13
- package/dist/ci.main.runtime.js.map +1 -1
- package/dist/commands/sync.cmd.js +5 -2
- package/dist/commands/sync.cmd.js.map +1 -1
- package/dist/{preview-1792532220087.js → preview-1792591009402.js} +1 -1
- package/dist/sync/init-scaffold.d.ts +89 -12
- package/dist/sync/init-scaffold.js +208 -21
- package/dist/sync/init-scaffold.js.map +1 -1
- package/dist/sync/init-scaffold.spec.js +295 -45
- package/dist/sync/init-scaffold.spec.js.map +1 -1
- package/dist/sync/sync-orchestrator.d.ts +3 -1
- package/dist/sync/sync-orchestrator.js +84 -14
- package/dist/sync/sync-orchestrator.js.map +1 -1
- package/package.json +19 -19
- package/sync/init-scaffold.spec.ts +314 -52
- package/sync/init-scaffold.ts +341 -55
- package/sync/sync-orchestrator.ts +89 -22
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_fs","data","_interopRequireDefault","require","_path","_githubClient","e","__esModule","default","WORKFLOW_RELATIVE_PATHS","exports","sync","path","join","release","BIT_SYNC_WORKFLOW_RAW","BIT_RELEASE_WORKFLOW_RAW","DEFAULT_BRANCH_MARKER","yamlSingleQuoted","value","replace","substituteDefaultBranch","raw","marker","defaultBranch","includes","Error","substitutedMarker","renderBitSyncWorkflow","renderBitReleaseWorkflow","writeIfAbsent","workspaceDir","relativePath","content","absPath","fs","existsSync","status","mkdirSync","dirname","recursive","writeFileSync","scaffoldWorkflowFiles","deriveOwnerRepo","remoteUrl","isGitHubRemote","undefined","ownerSlashRepo","parseGitHubRepo","host","remoteHost","owner","repo","split","WEBHOOK_PAYLOAD_TEMPLATE","renderInitChecklist","ownerRepo","repoSegment","api","gitHubApiUrl","dispatchUrl","url","error"],"sources":["init-scaffold.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport { parseGitHubRepo, isGitHubRemote, gitHubApiUrl, remoteHost } from './github-client';\n\n/**\n * `bit ci sync --init` — pure scaffolding logic, unit-testable without a live workspace/git checkout.\n *\n * TEMPLATE SOURCE OF TRUTH: `BIT_SYNC_WORKFLOW_RAW` / `BIT_RELEASE_WORKFLOW_RAW` are byte-for-byte\n * copies of the canonical templates in the `bit-git-sync` action workspace\n * (git-sync/git-sync/workflows/templates/) and MUST be kept in sync with them. The ONLY departure is\n * the two `DEFAULT_BRANCH_MARKER` substitution points; everything else — CHANGE-ME comments, the\n * placeholder action ref, `bit-sync/main` config defaults — stays untouched.\n */\n\n/** Where `scaffoldWorkflowFiles` writes each file, relative to the workspace root. */\nexport const WORKFLOW_RELATIVE_PATHS = {\n sync: path.join('.github', 'workflows', 'bit-sync.yml'),\n release: path.join('.github', 'workflows', 'bit-release.yml'),\n} as const;\n\nconst BIT_SYNC_WORKFLOW_RAW = `# Copy this file into your repository at \\`.github/workflows/bit-sync.yml\\`,\n# then review every line marked \\`CHANGE-ME\\` below -- the defaults assume a\n# repository whose default branch is \\`main\\` and a workspace at the repo root.\n#\n# See the \\`workflows.docs.mdx\\` customer setup guide (component\n# bitdev.git-sync/git-sync/workflows) for the full walk-through: the\n# bit.cloud webhook wiring that drives \\`repository_dispatch\\`, the required\n# secrets, and the \\`teambit.git/ci\\`.\\`sync\\` workspace config this workflow\n# acts on.\nname: bit-sync\non:\n repository_dispatch:\n # 'bit-export' is the real, empirically-verified bit.cloud webhook\n # contract: a single dispatch type discriminated by\n # client_payload.laneId (non-empty 'scope/lane' -> lane export, empty ->\n # main export). The other three types are forward-compat aliases the\n # event router also accepts, in case a webhook is configured to send the\n # originally-assumed, more granular dispatch types instead.\n types: [bit-export, bit-lane-export, bit-main-export, bit-lane-removed]\n schedule:\n # Scheduled reconcile. This is also the ONLY mechanism that picks up\n # lane deletions -- bit.cloud has no \"lane removed\" webhook event.\n - cron: '*/30 * * * *'\n push:\n # CHANGE-ME: \\`main\\` must be your repository's DEFAULT branch, and\n # \\`bit-sync/**\\` must cover your configured \\`sync.mainSyncBranch\\`. Pushes\n # to either are bit-sync's own output and must not re-trigger a sync.\n branches-ignore: [main, 'bit-sync/**']\n workflow_dispatch:\n inputs:\n lane: { description: Lane to sync (empty = all), required: false }\n\n# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations\n# created after Feb 2023, and anywhere the org/repo default is set to\n# \"Read repository contents permission\". Sync pushes branches and\n# opens/updates PRs, so both scopes must be declared explicitly -- without\n# this block the sync fails with a 403 on its first write.\npermissions:\n contents: write\n pull-requests: write\n\nconcurrency:\n # Serializes one lane's syncs so two runs never race on the same branch/PR.\n #\n # Each trigger needs a different expression, because the lane identity\n # arrives in a different place every time:\n # - repository_dispatch -> client_payload.laneId ('scope/lane'), or\n # client_payload.lane for the alias types\n # - push -> github.ref_name (the branch)\n # - workflow_dispatch -> the \\`lane\\` input\n # - schedule -> nothing lane-shaped; it reconciles everything\n #\n # github.ref_name is populated on schedule runs too (it is the default\n # branch there), so it may only be consulted AFTER the event name has been\n # checked. Reading it first -- as a bare \\`... || github.ref_name || 'all'\\`\n # chain does -- makes the final fallback unreachable and lumps every cron\n # run into the same group as pushes to the default branch.\n #\n # RESIDUAL RACE (accepted, by design): a webhook run groups as\n # 'scope/my-lane' while a push to that lane's branch groups as 'my-lane', so\n # two runs for the same lane can still overlap across those two triggers.\n # GitHub expressions have no substring/split function, and bit.cloud's\n # webhook payload template exposes only the full laneId, so the two keys\n # cannot be normalized into one here. This is safe rather than merely\n # tolerated: the reconciler is idempotent and the sync executor diffs\n # against converged state, so a second overlapping run finds nothing left to\n # do and no-ops. The concurrency group is an efficiency measure; it is not\n # what makes the sync correct.\n group: >-\n bit-sync-\\${{\n github.event_name == 'repository_dispatch' && (github.event.client_payload.laneId || github.event.client_payload.lane || 'main-export')\n || github.event_name == 'push' && github.ref_name\n || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane || 'reconcile-all')\n || 'reconcile-all' }}\n # Never cancel a sync mid-flight: it may already have pushed a branch and be\n # partway through opening or updating its PR.\n cancel-in-progress: false\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n # A PAT/App token so that pushes this workflow makes (e.g. a sync\n # PR branch) can themselves trigger CI. Pushes authenticated with\n # the default GITHUB_TOKEN do NOT trigger other workflow runs.\n token: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n - uses: bit-tasks/init@v2\n # Pinned to a commit SHA on purpose: this job holds write permission, and\n # a tag can move. Bump the SHA to take a new version of the action.\n - uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41\n with:\n # CHANGE-ME: keep this in sync with\n # \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\` in your workspace.jsonc\n # (the action's own default is shown here).\n main-sync-branch: bit-sync/main\n # Uncomment if your workspace.jsonc is NOT at the repository root --\n # the action chdirs here before running anything.\n # ws-dir: packages/my-workspace\n env:\n # Service account token. This account must be EXEMPT from the\n # org's change-request/merge block, or bit.cloud rejects the\n # sync-driven writes.\n BIT_CONFIG_ACCESS_TOKEN: \\${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}\n GITHUB_TOKEN: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n # Optional: git identity for the commits the sync creates. Defaults\n # to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.\n # GIT_USER_NAME: bit-sync[bot]\n # GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com\n`;\n\nconst BIT_RELEASE_WORKFLOW_RAW = `# Copy this file into your repository at \\`.github/workflows/bit-release.yml\\`,\n# then review every line marked \\`CHANGE-ME\\` below.\n#\n# See the \\`workflows.docs.mdx\\` customer setup guide (component\n# bitdev.git-sync/git-sync/workflows) for the full walk-through.\nname: bit-release\non:\n pull_request:\n types: [closed]\n # CHANGE-ME: your repository's DEFAULT branch. \\`bit ci merge\\` releases\n # from the default branch, so only PRs merged INTO it should trigger a\n # release -- a stacked PR landing on another feature branch has not\n # reached the default branch yet. The action's event router enforces the\n # same rule from \\`pull_request.base.ref\\`; this filter means a run is never\n # even queued for a non-default base.\n branches: [main]\n\n# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations\n# created after Feb 2023, and anywhere the org/repo default is set to\n# \"Read repository contents permission\". The release run pushes commits/tags\n# and updates PRs, so both scopes must be declared explicitly.\npermissions:\n contents: write\n pull-requests: write\n\nconcurrency:\n group: bit-release-\\${{ github.event.pull_request.number }}\n # Never cancel a release mid-flight.\n cancel-in-progress: false\n\njobs:\n release:\n runs-on: ubuntu-latest\n # Merged main-sync PRs must NOT trigger a release: the scope is already\n # ahead of this merge, there is nothing new to release. The event\n # router also skips this case (routePullRequest checks\n # prHeadRef === mainSyncBranch) -- this \\`if:\\` is defense in depth so the\n # job doesn't even start.\n # CHANGE-ME: the branch literal below MUST match your workspace's\n # configured \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\`.\n if: github.event.pull_request.merged == true && github.event.pull_request.head.ref != 'bit-sync/main'\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n token: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n - uses: bit-tasks/init@v2\n # Pinned to a commit SHA on purpose: this job holds write permission, and\n # a tag can move. Bump the SHA to take a new version of the action.\n - uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41\n with:\n # CHANGE-ME: keep this in sync with\n # \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\` AND with the \\`if:\\`\n # condition above.\n main-sync-branch: bit-sync/main\n # Uncomment if your workspace.jsonc is NOT at the repository root --\n # the action chdirs here before running anything.\n # ws-dir: packages/my-workspace\n env:\n BIT_CONFIG_ACCESS_TOKEN: \\${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}\n GITHUB_TOKEN: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n # Optional: git identity for the commits the release creates.\n # Defaults to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.\n # GIT_USER_NAME: bit-sync[bot]\n # GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com\n`;\n\n/**\n * The exact substring in each template that encodes the repository's default branch. Exact substring,\n * never a blanket `main` regex — that would also corrupt `bit-main-export` and `bit-sync/main`.\n */\nconst DEFAULT_BRANCH_MARKER = {\n sync: \"branches-ignore: [main, 'bit-sync/**']\",\n release: 'branches: [main]',\n} as const;\n\n/**\n * A YAML single-quoted scalar of `value` (the only escape inside single quotes is a doubled `''`).\n * Both substitution points land inside a YAML flow sequence, where `,` and `]` are structural — and a\n * branch name may legally contain both, so quoting is unconditional.\n */\nfunction yamlSingleQuoted(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Replace the default-branch literal inside `marker` with `defaultBranch`, inside `raw`. Throws when\n * the marker has drifted out of the template — a reworded canonical workflow must fail loudly, not\n * ship a scaffold that still says `main`.\n */\nfunction substituteDefaultBranch(raw: string, marker: string, defaultBranch: string): string {\n if (!raw.includes(marker)) {\n throw new Error(\n `init-scaffold: expected marker \"${marker}\" not found in its workflow template — the canonical ` +\n `template (bitdev.git-sync/git-sync/workflows) has drifted from BIT_SYNC_WORKFLOW_RAW / ` +\n `BIT_RELEASE_WORKFLOW_RAW; update the copy and this substitution point together`\n );\n }\n // Function replacements, not strings: `$&`/`$'`/`$$` are special in string replacements and `$` is\n // git-legal in a branch name. A function replacement is inserted verbatim.\n const substitutedMarker = marker.replace('main', () => yamlSingleQuoted(defaultBranch));\n return raw.replace(marker, () => substitutedMarker);\n}\n\n/** Render `bit-sync.yml` with the repository's actual default branch substituted. */\nexport function renderBitSyncWorkflow(defaultBranch: string): string {\n return substituteDefaultBranch(BIT_SYNC_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.sync, defaultBranch);\n}\n\n/** Render `bit-release.yml` with the repository's actual default branch substituted. */\nexport function renderBitReleaseWorkflow(defaultBranch: string): string {\n return substituteDefaultBranch(BIT_RELEASE_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.release, defaultBranch);\n}\n\nexport interface ScaffoldFileOutcome {\n /** relative to the workspace root, e.g. `.github/workflows/bit-sync.yml` */\n relativePath: string;\n status: 'written' | 'skipped';\n}\n\n// Never overwrites an existing file — `--init` must be safe to re-run after hand-edits.\nfunction writeIfAbsent(workspaceDir: string, relativePath: string, content: string): ScaffoldFileOutcome {\n const absPath = path.join(workspaceDir, relativePath);\n if (fs.existsSync(absPath)) {\n return { relativePath, status: 'skipped' };\n }\n fs.mkdirSync(path.dirname(absPath), { recursive: true });\n fs.writeFileSync(absPath, content);\n return { relativePath, status: 'written' };\n}\n\n/** Scaffold both workflow files under `workspaceDir`. Never overwrites; creates `.github/workflows/` as needed. */\nexport function scaffoldWorkflowFiles(workspaceDir: string, defaultBranch: string): ScaffoldFileOutcome[] {\n return [\n writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.sync, renderBitSyncWorkflow(defaultBranch)),\n writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.release, renderBitReleaseWorkflow(defaultBranch)),\n ];\n}\n\nexport interface OwnerRepo {\n owner: string;\n repo: string;\n /** The remote's host — github.com or the GITHUB_SERVER_URL one — which picks the API the checklist names. */\n host: string;\n}\n\n/**\n * Derive `{owner, repo}` from the `origin` remote when it is a parseable GitHub remote, else\n * undefined (the checklist falls back to a placeholder). Reuses `github-client.ts`'s parsing so the\n * two never disagree.\n */\nexport function deriveOwnerRepo(remoteUrl: string | undefined): OwnerRepo | undefined {\n if (!remoteUrl || !isGitHubRemote(remoteUrl)) return undefined;\n const ownerSlashRepo = parseGitHubRepo(remoteUrl);\n const host = remoteHost(remoteUrl);\n if (!ownerSlashRepo || !host) return undefined;\n const [owner, repo] = ownerSlashRepo.split('/');\n if (!owner || !repo) return undefined;\n return { owner, repo, host };\n}\n\n/**\n * The bit.cloud custom webhook payload template, verified byte-perfect against a live delivery.\n * Lane vs. main export is discriminated by whether `laneId` is empty.\n */\nconst WEBHOOK_PAYLOAD_TEMPLATE =\n '{\"event_type\":\"bit-export\",\"client_payload\":{\"laneId\":\"{{laneId}}\",\"componentIds\":\"{{componentIds}}\",\"owner\":\"{{owner}}\",\"actor\":\"{{username}}\"}}';\n\n/**\n * The manual-steps checklist `bit ci sync --init` prints: everything the command itself cannot do\n * (secrets live in GitHub settings; the webhook lives on bit.cloud).\n */\nexport function renderInitChecklist(ownerRepo: OwnerRepo | undefined): string {\n const repoSegment = ownerRepo ? `${ownerRepo.owner}/${ownerRepo.repo}` : '<owner>/<repo>';\n const api = gitHubApiUrl(ownerRepo?.host);\n const dispatchUrl = `${'error' in api ? '<GitHub API URL>' : api.url}/repos/${repoSegment}/dispatches`;\n return [\n '',\n 'Remaining manual steps (bit ci sync --init only scaffolds files -- these need a human):',\n '',\n '1. Secrets -- add in the repository (or org) Settings > Secrets and variables > Actions:',\n ' - BIT_CONFIG_ACCESS_TOKEN (required): a bit.cloud service-account token with export rights on',\n \" this scope. This account must also be exempt from the org's\",\n ' change-request/merge block -- it is the only identity that merges lanes.',\n ' - BIT_SYNC_GH_TOKEN (optional): a GitHub PAT or App token. Without it, sync pushes/PRs use the',\n ' default GITHUB_TOKEN, which does NOT trigger downstream workflow runs (loop-safe, but the',\n ' sync PR then gets no CI checks).',\n '',\n '2. Permissions -- both scaffolded workflows already declare `contents: write` and',\n ' `pull-requests: write`. Nothing to change unless an org-wide policy overrides workflow-level',\n ' permissions (e.g. a default of \"Read repository contents permission\" for the GITHUB_TOKEN).',\n '',\n '3. bit.cloud webhook -- create ONE webhook (org Settings > Webhooks) on the \"Components > Export',\n ' succeeded\" event:',\n ` URL: ${dispatchUrl}`,\n ...('error' in api ? [` (${api.error})`] : []),\n ' Headers: Authorization: Bearer <PAT>',\n ' Accept: application/vnd.github+json',\n ' Custom payload template:',\n ` ${WEBHOOK_PAYLOAD_TEMPLATE}`,\n ' WARNING: create this webhook fresh -- editing an existing webhook drops its custom headers',\n ' (known bit.cloud platform bug, verified 2026-07-29). If the URL, headers or template need to',\n ' change later, delete the webhook and create a new one rather than editing it.',\n '',\n '4. Checkout depth -- both scaffolded workflows already check out with `fetch-depth: 0`. This is',\n ' required: the reconciler reads full git history, and a shallow (--depth=1) clone fails sync',\n ' safely but uselessly (HALTED, no writes). Keep `fetch-depth: 0` if you customize the workflow.',\n ' Narrowing the REFSPEC is fine, though -- a single-branch clone is supported, because every',\n ' fetch sync performs names +refs/heads/*:refs/remotes/origin/* explicitly.',\n '',\n ].join('\\n');\n}\n"],"mappings":";;;;;;;;;;;AAAA,SAAAA,IAAA;EAAA,MAAAC,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAH,GAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAG,MAAA;EAAA,MAAAH,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAC,KAAA,YAAAA,CAAA;IAAA,OAAAH,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAI,cAAA;EAAA,MAAAJ,IAAA,GAAAE,OAAA;EAAAE,aAAA,YAAAA,CAAA;IAAA,OAAAJ,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAA4F,SAAAC,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAE5F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACO,MAAMG,uBAAuB,GAAAC,OAAA,CAAAD,uBAAA,GAAG;EACrCE,IAAI,EAAEC,eAAI,CAACC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,CAAC;EACvDC,OAAO,EAAEF,eAAI,CAACC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB;AAC9D,CAAU;AAEV,MAAME,qBAAqB,GAAG;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAED,MAAMC,wBAAwB,GAAG;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA;AACA;AACA,MAAMC,qBAAqB,GAAG;EAC5BN,IAAI,EAAE,wCAAwC;EAC9CG,OAAO,EAAE;AACX,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA,SAASI,gBAAgBA,CAACC,KAAa,EAAU;EAC/C,OAAO,IAAIA,KAAK,CAACC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;AACzC;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASC,uBAAuBA,CAACC,GAAW,EAAEC,MAAc,EAAEC,aAAqB,EAAU;EAC3F,IAAI,CAACF,GAAG,CAACG,QAAQ,CAACF,MAAM,CAAC,EAAE;IACzB,MAAM,IAAIG,KAAK,CACb,mCAAmCH,MAAM,uDAAuD,GAC9F,yFAAyF,GACzF,gFACJ,CAAC;EACH;EACA;EACA;EACA,MAAMI,iBAAiB,GAAGJ,MAAM,CAACH,OAAO,CAAC,MAAM,EAAE,MAAMF,gBAAgB,CAACM,aAAa,CAAC,CAAC;EACvF,OAAOF,GAAG,CAACF,OAAO,CAACG,MAAM,EAAE,MAAMI,iBAAiB,CAAC;AACrD;;AAEA;AACO,SAASC,qBAAqBA,CAACJ,aAAqB,EAAU;EACnE,OAAOH,uBAAuB,CAACN,qBAAqB,EAAEE,qBAAqB,CAACN,IAAI,EAAEa,aAAa,CAAC;AAClG;;AAEA;AACO,SAASK,wBAAwBA,CAACL,aAAqB,EAA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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_fs","data","_interopRequireDefault","require","_chalk","_cli","_path","_githubClient","e","__esModule","default","WORKFLOW_RELATIVE_PATHS","exports","sync","path","join","release","BIT_SYNC_WORKFLOW_RAW","BIT_RELEASE_WORKFLOW_RAW","DEFAULT_BRANCH_MARKER","yamlSingleQuoted","value","replace","substituteDefaultBranch","raw","marker","defaultBranch","includes","Error","substitutedMarker","WS_DIR_MARKER","substituteWsDir","wsDir","renderBitSyncWorkflow","renderBitReleaseWorkflow","writeIfAbsent","workspaceDir","relativePath","content","absPath","fs","existsSync","status","mkdirSync","dirname","recursive","writeFileSync","scaffoldWorkflowFiles","deriveOwnerRepo","remoteUrl","isGitHubRemote","undefined","ownerSlashRepo","parseGitHubRepo","host","remoteHost","owner","repo","split","WEBHOOK_PAYLOAD_TEMPLATE","BIT_GIT_SYNC_APP_URL","WEBHOOK_RELAY_SETUP_URL","SETUP_GUIDE_URL","FILE_PURPOSE","isLocalPathRemote","url","trim","test","redactRemoteUrl","escapeControlChars","c","charCodeAt","toString","padStart","shellQuote","chooseInitDefaultBranch","detected","current","suggestedServerUrl","URL","origin","renderInitReport","input","ownerRepo","scope","scopeId","redactedOrigin","originUrl","unrecognizedOrigin","serverHost","repoRootFromCwd","configDisplayPath","target","chalk","cyan","header","successSymbol","formatTitle","formatHint","defaultBranchGuessed","why","push","formatWarningSummary","width","Math","max","files","map","f","displayPath","length","fileItem","note","formatItem","padEnd","file","configAdded","step","n","title","who","body","line","cmd","text","firstCommit","repoHasCommits","toStage","ignored","quoted","paths","stage","gitInit","toCommit","nothingToCommit","commitMessage","commit","inGitRepo","steps","onGitHubCom","ghRepo","ghApi","api","gitHubApiUrl","dispatchUrl","yellow","error","footer","joinSections"],"sources":["init-scaffold.ts"],"sourcesContent":["import fs from 'fs';\nimport chalk from 'chalk';\nimport { formatHint, formatItem, formatTitle, formatWarningSummary, joinSections, successSymbol } from '@teambit/cli';\nimport path from 'path';\nimport { parseGitHubRepo, isGitHubRemote, gitHubApiUrl, remoteHost } from './github-client';\n\n/**\n * `bit ci sync --init` — pure scaffolding logic, unit-testable without a live workspace/git checkout.\n *\n * TEMPLATE SOURCE OF TRUTH: `BIT_SYNC_WORKFLOW_RAW` / `BIT_RELEASE_WORKFLOW_RAW` are byte-for-byte\n * copies of the canonical templates in the `bit-git-sync` action workspace\n * (git-sync/git-sync/workflows/templates/) and MUST be kept in sync with them. The ONLY departures\n * are the two `DEFAULT_BRANCH_MARKER` substitution points and, for a workspace below the repository\n * root, the `WS_DIR_MARKER` line; everything else — CHANGE-ME comments, the placeholder action ref,\n * `bit-sync/main` config defaults — stays untouched.\n */\n\n/** Where `scaffoldWorkflowFiles` writes each file, relative to the workspace root. */\nexport const WORKFLOW_RELATIVE_PATHS = {\n sync: path.join('.github', 'workflows', 'bit-sync.yml'),\n release: path.join('.github', 'workflows', 'bit-release.yml'),\n} as const;\n\nconst BIT_SYNC_WORKFLOW_RAW = `# Copy this file into your repository at \\`.github/workflows/bit-sync.yml\\`,\n# then review every line marked \\`CHANGE-ME\\` below -- the defaults assume a\n# repository whose default branch is \\`main\\` and a workspace at the repo root.\n#\n# See the \\`workflows.docs.mdx\\` customer setup guide (component\n# bitdev.git-sync/git-sync/workflows) for the full walk-through: the\n# bit.cloud webhook wiring that drives \\`repository_dispatch\\`, the required\n# secrets, and the \\`teambit.git/ci\\`.\\`sync\\` workspace config this workflow\n# acts on.\nname: bit-sync\non:\n repository_dispatch:\n # 'bit-export' is the real, empirically-verified bit.cloud webhook\n # contract: a single dispatch type discriminated by\n # client_payload.laneId (non-empty 'scope/lane' -> lane export, empty ->\n # main export). The other three types are forward-compat aliases the\n # event router also accepts, in case a webhook is configured to send the\n # originally-assumed, more granular dispatch types instead.\n types: [bit-export, bit-lane-export, bit-main-export, bit-lane-removed]\n schedule:\n # Scheduled reconcile. This is also the ONLY mechanism that picks up\n # lane deletions -- bit.cloud has no \"lane removed\" webhook event.\n - cron: '*/30 * * * *'\n push:\n # CHANGE-ME: \\`main\\` must be your repository's DEFAULT branch, and\n # \\`bit-sync/**\\` must cover your configured \\`sync.mainSyncBranch\\`. Pushes\n # to either are bit-sync's own output and must not re-trigger a sync.\n branches-ignore: [main, 'bit-sync/**']\n workflow_dispatch:\n inputs:\n lane: { description: Lane to sync (empty = all), required: false }\n\n# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations\n# created after Feb 2023, and anywhere the org/repo default is set to\n# \"Read repository contents permission\". Sync pushes branches and\n# opens/updates PRs, so both scopes must be declared explicitly -- without\n# this block the sync fails with a 403 on its first write.\npermissions:\n contents: write\n pull-requests: write\n\nconcurrency:\n # Serializes one lane's syncs so two runs never race on the same branch/PR.\n #\n # Each trigger needs a different expression, because the lane identity\n # arrives in a different place every time:\n # - repository_dispatch -> client_payload.laneId ('scope/lane'), or\n # client_payload.lane for the alias types\n # - push -> github.ref_name (the branch)\n # - workflow_dispatch -> the \\`lane\\` input\n # - schedule -> nothing lane-shaped; it reconciles everything\n #\n # github.ref_name is populated on schedule runs too (it is the default\n # branch there), so it may only be consulted AFTER the event name has been\n # checked. Reading it first -- as a bare \\`... || github.ref_name || 'all'\\`\n # chain does -- makes the final fallback unreachable and lumps every cron\n # run into the same group as pushes to the default branch.\n #\n # RESIDUAL RACE (accepted, by design): a webhook run groups as\n # 'scope/my-lane' while a push to that lane's branch groups as 'my-lane', so\n # two runs for the same lane can still overlap across those two triggers.\n # GitHub expressions have no substring/split function, and bit.cloud's\n # webhook payload template exposes only the full laneId, so the two keys\n # cannot be normalized into one here. This is safe rather than merely\n # tolerated: the reconciler is idempotent and the sync executor diffs\n # against converged state, so a second overlapping run finds nothing left to\n # do and no-ops. The concurrency group is an efficiency measure; it is not\n # what makes the sync correct.\n group: >-\n bit-sync-\\${{\n github.event_name == 'repository_dispatch' && (github.event.client_payload.laneId || github.event.client_payload.lane || 'main-export')\n || github.event_name == 'push' && github.ref_name\n || github.event_name == 'workflow_dispatch' && (github.event.inputs.lane || 'reconcile-all')\n || 'reconcile-all' }}\n # Never cancel a sync mid-flight: it may already have pushed a branch and be\n # partway through opening or updating its PR.\n cancel-in-progress: false\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n # A PAT/App token so that pushes this workflow makes (e.g. a sync\n # PR branch) can themselves trigger CI. Pushes authenticated with\n # the default GITHUB_TOKEN do NOT trigger other workflow runs.\n token: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n - uses: bit-tasks/init@v2\n # Pinned to a commit SHA on purpose: this job holds write permission, and\n # a tag can move. Bump the SHA to take a new version of the action.\n - uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41\n with:\n # CHANGE-ME: keep this in sync with\n # \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\` in your workspace.jsonc\n # (the action's own default is shown here).\n main-sync-branch: bit-sync/main\n # Uncomment if your workspace.jsonc is NOT at the repository root --\n # the action chdirs here before running anything.\n # ws-dir: packages/my-workspace\n env:\n # Service account token. This account must be EXEMPT from the\n # org's change-request/merge block, or bit.cloud rejects the\n # sync-driven writes.\n BIT_CONFIG_ACCESS_TOKEN: \\${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}\n GITHUB_TOKEN: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n # Optional: git identity for the commits the sync creates. Defaults\n # to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.\n # GIT_USER_NAME: bit-sync[bot]\n # GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com\n`;\n\nconst BIT_RELEASE_WORKFLOW_RAW = `# Copy this file into your repository at \\`.github/workflows/bit-release.yml\\`,\n# then review every line marked \\`CHANGE-ME\\` below.\n#\n# See the \\`workflows.docs.mdx\\` customer setup guide (component\n# bitdev.git-sync/git-sync/workflows) for the full walk-through.\nname: bit-release\non:\n pull_request:\n types: [closed]\n # CHANGE-ME: your repository's DEFAULT branch. \\`bit ci merge\\` releases\n # from the default branch, so only PRs merged INTO it should trigger a\n # release -- a stacked PR landing on another feature branch has not\n # reached the default branch yet. The action's event router enforces the\n # same rule from \\`pull_request.base.ref\\`; this filter means a run is never\n # even queued for a non-default base.\n branches: [main]\n\n# The default GITHUB_TOKEN is READ-ONLY on repositories and organizations\n# created after Feb 2023, and anywhere the org/repo default is set to\n# \"Read repository contents permission\". The release run pushes commits/tags\n# and updates PRs, so both scopes must be declared explicitly.\npermissions:\n contents: write\n pull-requests: write\n\nconcurrency:\n group: bit-release-\\${{ github.event.pull_request.number }}\n # Never cancel a release mid-flight.\n cancel-in-progress: false\n\njobs:\n release:\n runs-on: ubuntu-latest\n # Merged main-sync PRs must NOT trigger a release: the scope is already\n # ahead of this merge, there is nothing new to release. The event\n # router also skips this case (routePullRequest checks\n # prHeadRef === mainSyncBranch) -- this \\`if:\\` is defense in depth so the\n # job doesn't even start.\n # CHANGE-ME: the branch literal below MUST match your workspace's\n # configured \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\`.\n if: github.event.pull_request.merged == true && github.event.pull_request.head.ref != 'bit-sync/main'\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n token: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n - uses: bit-tasks/init@v2\n # Pinned to a commit SHA on purpose: this job holds write permission, and\n # a tag can move. Bump the SHA to take a new version of the action.\n - uses: teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41\n with:\n # CHANGE-ME: keep this in sync with\n # \\`teambit.git/ci\\`.\\`sync.mainSyncBranch\\` AND with the \\`if:\\`\n # condition above.\n main-sync-branch: bit-sync/main\n # Uncomment if your workspace.jsonc is NOT at the repository root --\n # the action chdirs here before running anything.\n # ws-dir: packages/my-workspace\n env:\n BIT_CONFIG_ACCESS_TOKEN: \\${{ secrets.BIT_CONFIG_ACCESS_TOKEN }}\n GITHUB_TOKEN: \\${{ secrets.BIT_SYNC_GH_TOKEN || secrets.GITHUB_TOKEN }}\n # Optional: git identity for the commits the release creates.\n # Defaults to bit-sync[bot] / bit-sync[bot]@users.noreply.github.com.\n # GIT_USER_NAME: bit-sync[bot]\n # GIT_USER_EMAIL: bit-sync[bot]@users.noreply.github.com\n`;\n\n/**\n * The exact substring in each template that encodes the repository's default branch. Exact substring,\n * never a blanket `main` regex — that would also corrupt `bit-main-export` and `bit-sync/main`.\n */\nconst DEFAULT_BRANCH_MARKER = {\n sync: \"branches-ignore: [main, 'bit-sync/**']\",\n release: 'branches: [main]',\n} as const;\n\n/**\n * A YAML single-quoted scalar of `value` (the only escape inside single quotes is a doubled `''`).\n * Both substitution points land inside a YAML flow sequence, where `,` and `]` are structural — and a\n * branch name may legally contain both, so quoting is unconditional.\n */\nfunction yamlSingleQuoted(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Replace the default-branch literal inside `marker` with `defaultBranch`, inside `raw`. Throws when\n * the marker has drifted out of the template — a reworded canonical workflow must fail loudly, not\n * ship a scaffold that still says `main`.\n */\nfunction substituteDefaultBranch(raw: string, marker: string, defaultBranch: string): string {\n if (!raw.includes(marker)) {\n throw new Error(\n `init-scaffold: expected marker \"${marker}\" not found in its workflow template — the canonical ` +\n `template (bitdev.git-sync/git-sync/workflows) has drifted from BIT_SYNC_WORKFLOW_RAW / ` +\n `BIT_RELEASE_WORKFLOW_RAW; update the copy and this substitution point together`\n );\n }\n // Function replacements, not strings: `$&`/`$'`/`$$` are special in string replacements and `$` is\n // git-legal in a branch name. A function replacement is inserted verbatim.\n const substitutedMarker = marker.replace('main', () => yamlSingleQuoted(defaultBranch));\n return raw.replace(marker, () => substitutedMarker);\n}\n\n/** The commented-out `ws-dir` input, once in each template. */\nconst WS_DIR_MARKER = '# ws-dir: packages/my-workspace';\n\n/**\n * Set the action's `ws-dir` input when the workspace is below the repository root (`wsDir` is the\n * workspace directory relative to the root, `/`-separated). At the root, the line stays commented out.\n */\nfunction substituteWsDir(raw: string, wsDir: string | undefined): string {\n if (!wsDir || wsDir === '.') return raw;\n if (!raw.includes(WS_DIR_MARKER)) {\n throw new Error(`init-scaffold: expected marker \"${WS_DIR_MARKER}\" not found in its workflow template`);\n }\n return raw.replace(WS_DIR_MARKER, () => `ws-dir: ${yamlSingleQuoted(wsDir)}`);\n}\n\n/** Render `bit-sync.yml` with the repository's actual default branch, and `ws-dir` for a nested workspace. */\nexport function renderBitSyncWorkflow(defaultBranch: string, wsDir?: string): string {\n return substituteWsDir(\n substituteDefaultBranch(BIT_SYNC_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.sync, defaultBranch),\n wsDir\n );\n}\n\n/** Render `bit-release.yml` with the repository's actual default branch, and `ws-dir` for a nested workspace. */\nexport function renderBitReleaseWorkflow(defaultBranch: string, wsDir?: string): string {\n return substituteWsDir(\n substituteDefaultBranch(BIT_RELEASE_WORKFLOW_RAW, DEFAULT_BRANCH_MARKER.release, defaultBranch),\n wsDir\n );\n}\n\nexport interface ScaffoldFileOutcome {\n /** relative to the workspace root, e.g. `.github/workflows/bit-sync.yml` */\n relativePath: string;\n status: 'written' | 'skipped';\n}\n\n// Never overwrites an existing file — `--init` must be safe to re-run after hand-edits.\nfunction writeIfAbsent(workspaceDir: string, relativePath: string, content: string): ScaffoldFileOutcome {\n const absPath = path.join(workspaceDir, relativePath);\n if (fs.existsSync(absPath)) {\n return { relativePath, status: 'skipped' };\n }\n fs.mkdirSync(path.dirname(absPath), { recursive: true });\n fs.writeFileSync(absPath, content);\n return { relativePath, status: 'written' };\n}\n\n/**\n * Scaffold both workflow files under `workspaceDir` (the repository root). `wsDir` is the bit workspace\n * relative to it, when the workspace is a subdirectory. Never overwrites; creates `.github/workflows/`.\n */\nexport function scaffoldWorkflowFiles(\n workspaceDir: string,\n defaultBranch: string,\n wsDir?: string\n): ScaffoldFileOutcome[] {\n return [\n writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.sync, renderBitSyncWorkflow(defaultBranch, wsDir)),\n writeIfAbsent(workspaceDir, WORKFLOW_RELATIVE_PATHS.release, renderBitReleaseWorkflow(defaultBranch, wsDir)),\n ];\n}\n\nexport interface OwnerRepo {\n owner: string;\n repo: string;\n /** The remote's host — github.com or the GITHUB_SERVER_URL one — which picks the API the checklist names. */\n host: string;\n}\n\n/**\n * Derive `{owner, repo}` from the `origin` remote when it is a parseable GitHub remote, else\n * undefined (the checklist falls back to a placeholder). Reuses `github-client.ts`'s parsing so the\n * two never disagree.\n */\nexport function deriveOwnerRepo(remoteUrl: string | undefined): OwnerRepo | undefined {\n if (!remoteUrl || !isGitHubRemote(remoteUrl)) return undefined;\n const ownerSlashRepo = parseGitHubRepo(remoteUrl);\n const host = remoteHost(remoteUrl);\n if (!ownerSlashRepo || !host) return undefined;\n const [owner, repo] = ownerSlashRepo.split('/');\n if (!owner || !repo) return undefined;\n return { owner, repo, host };\n}\n\n/**\n * The bit.cloud custom webhook payload template, verified byte-perfect against a live delivery.\n * Lane vs. main export is discriminated by whether `laneId` is empty. Used on GitHub Enterprise\n * Server, where the webhook posts straight to the repository's `dispatches` API with a token.\n */\nconst WEBHOOK_PAYLOAD_TEMPLATE =\n '{\"event_type\":\"bit-export\",\"client_payload\":{\"laneId\":\"{{laneId}}\",\"componentIds\":\"{{componentIds}}\",\"owner\":\"{{owner}}\",\"actor\":\"{{username}}\"}}';\n\n/**\n * On github.com, instant Bit -> Git goes through the hosted relay: the `bit-git-sync` GitHub App lets\n * it send `repository_dispatch` to the repositories it is installed on, and its setup page hands out\n * one webhook URL per repository. No personal token is involved.\n */\nconst BIT_GIT_SYNC_APP_URL = 'https://github.com/apps/bit-git-sync';\nconst WEBHOOK_RELAY_SETUP_URL = 'https://webhook-relay-gggmal.r2.composed.app/setup';\nconst SETUP_GUIDE_URL = 'https://github.com/teambit/acme-modular-store#set-it-up-for-your-own-scope';\n\n/** What each scaffolded file does, in the words the report shows next to it. */\nconst FILE_PURPOSE: Record<string, string> = {\n [WORKFLOW_RELATIVE_PATHS.sync]: 'syncs lanes and branches: on export, on push, and every 30 minutes',\n [WORKFLOW_RELATIVE_PATHS.release]: 'releases the scope when a pull request merges',\n};\n\nexport interface InitReportInput {\n /** the workspace's default scope, the one this repository mirrors */\n scopeId?: string;\n /** derived from the `origin` remote; undefined when there is none, or it isn't a recognized GitHub host */\n ownerRepo?: OwnerRepo;\n /** the raw `origin` URL, to name a remote that exists but isn't recognized as GitHub */\n originUrl?: string;\n defaultBranch: string;\n /** true when the remote didn't name its default branch and `defaultBranch` is a guess */\n defaultBranchGuessed?: boolean;\n /**\n * The repository root as seen from where the user stands (`.` when they stand in it). The workflows\n * live there, so the first commit and `gh repo create --source` must name it.\n */\n repoRootFromCwd?: string;\n /**\n * false when the repository has no commit yet. The first commit then takes the whole workspace;\n * after that, the commit takes only the files `--init` touched, never unrelated local changes.\n */\n repoHasCommits?: boolean;\n /** false when the workspace is not in a git repository yet: step 1 then starts with `git init` */\n inGitRepo?: boolean;\n /** scaffolded workflow files, with the path to show (relative to where the user stands) */\n files: Array<ScaffoldFileOutcome & { displayPath: string }>;\n /** workspace.jsonc, as seen from where the user stands */\n configDisplayPath?: string;\n /** whether `--init` added the sync block to workspace.jsonc, or found one already there */\n configAdded: boolean;\n /**\n * Of the workflow files and workspace.jsonc, the ones git shows as new or changed, as displayed.\n * Omitted: stage them all. Empty: nothing is left to commit (a re-run after the commit).\n */\n toStage?: string[];\n /**\n * Of those files, the untracked ones `.gitignore` matches, also before the first commit. `git add`\n * refuses them by name and `git add -A` skips them.\n */\n ignored?: string[];\n}\n\n/**\n * A remote that is a path on this machine (a bare repository, a `file://` URL), not a server. It has\n * no host to suggest as a GitHub Enterprise Server. Git's own rule: a URL has a scheme, or is scp-like\n * (`host:path`, with no slash before the first colon); anything else is a path, `remote.git` included.\n */\nexport function isLocalPathRemote(remoteUrl: string): boolean {\n const url = remoteUrl.trim();\n if (/^file:\\/\\//i.test(url)) return true;\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(url)) return false;\n // a Windows drive (`C:\\repos`) looks scp-like to the next check\n if (/^[a-z]:[/\\\\]/i.test(url)) return true;\n return !/^[^/\\\\]+:/.test(url);\n}\n\n/**\n * The remote URL without the credentials a URL can carry (`https://user:token@host/...`), so the\n * report never prints a token. An http(s) user name can itself be a token, so all of it goes; other\n * schemes keep the user name (`ssh://git@host`) and lose only a password.\n */\nexport function redactRemoteUrl(remoteUrl: string): string {\n const url = remoteUrl.trim();\n if (/^https?:\\/\\//i.test(url)) return url.replace(/^(https?:\\/\\/)[^@/]*@/i, '$1');\n return url.replace(/^([a-z][a-z0-9+.-]*:\\/\\/[^:@/]*):[^@/]*@/i, '$1@');\n}\n\n/** `value` with each control character written as `\\xNN`, so printing it cannot move or restyle the terminal. */\nexport function escapeControlChars(value: string): string {\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, (c) => `\\\\x${c.charCodeAt(0).toString(16).padStart(2, '0')}`);\n}\n\n/**\n * Quote a value for a POSIX shell, so a command the report prints runs as shown when pasted. Plain\n * names stay unquoted for readability; anything else is single-quoted. A git-valid branch such as\n * `$(id)` must not run as a command substitution.\n */\nexport function shellQuote(value: string): string {\n return /^[\\w./@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\\\''`)}'`;\n}\n\n/**\n * The default branch `--init` writes into the workflows: the one the remote names, else a guess. The\n * guess is the checked-out branch only when it is `main` or `master`: a feature branch in the branch\n * filters would exclude itself from sync and point releases at the wrong base.\n */\nexport function chooseInitDefaultBranch(detected: string | undefined, current: string | undefined): string {\n if (detected) return detected;\n return current === 'main' || current === 'master' ? current : 'main';\n}\n\n/**\n * The `GITHUB_SERVER_URL` to suggest for a remote bit doesn't recognize: an http(s) remote's own origin,\n * port included, else https on the remote's host (an ssh port is not the web server's).\n */\nexport function suggestedServerUrl(remoteUrl: string): string {\n if (/^https?:\\/\\//i.test(remoteUrl.trim())) {\n try {\n return new URL(remoteUrl.trim()).origin;\n } catch {\n // fall through to the host\n }\n }\n return `https://${remoteHost(remoteUrl) ?? '<your-server>'}`;\n}\n\n/**\n * The report `bit ci sync --init` prints: what it changed, then the steps that need a person, as\n * commands to paste. Short by design; the setup guide carries the details. Commands are printed only\n * for a resolved repository: a placeholder would not run when pasted.\n */\nexport function renderInitReport(input: InitReportInput): string {\n const { ownerRepo, defaultBranch } = input;\n const scope = input.scopeId ?? '<scope>';\n // An origin that isn't recognized is most often a GitHub Enterprise Server the shell doesn't know about.\n const redactedOrigin = !ownerRepo && input.originUrl ? redactRemoteUrl(input.originUrl) : undefined;\n // Shown, so made inert: a control character (a newline, a terminal escape) must not reach the terminal.\n const unrecognizedOrigin = redactedOrigin && escapeControlChars(redactedOrigin);\n // Suggest a server only for a remote that names a host: a local path has none, and a URL with a\n // control character in it has no trustworthy one.\n const serverHost =\n redactedOrigin && redactedOrigin === unrecognizedOrigin && !isLocalPathRemote(redactedOrigin)\n ? remoteHost(redactedOrigin)\n : undefined;\n const repoRootFromCwd = input.repoRootFromCwd || '.';\n const configDisplayPath = input.configDisplayPath || 'workspace.jsonc';\n\n const target = ownerRepo\n ? chalk.cyan(\n ownerRepo.host === 'github.com'\n ? `${ownerRepo.owner}/${ownerRepo.repo}`\n : `${ownerRepo.host}/${ownerRepo.owner}/${ownerRepo.repo}`\n )\n : unrecognizedOrigin\n ? `origin ${unrecognizedOrigin}`\n : 'no GitHub remote yet';\n const header = [\n `${successSymbol()} ${formatTitle('Scaffolded bit ci sync')} for ${chalk.cyan(scope)}, mirrored to ${target} ${formatHint(\n `(default branch: ${defaultBranch})`\n )}`,\n ];\n if (input.defaultBranchGuessed) {\n const why = input.originUrl ? \"the remote didn't name its default branch\" : 'no remote names the default branch';\n header.push(\n formatWarningSummary(\n `${why}, so the workflows use \"${defaultBranch}\". If that's wrong,\\n` +\n ` edit the branch filters in both workflow files.`\n )\n );\n }\n if (serverHost && unrecognizedOrigin) {\n header.push(\n formatWarningSummary(\"origin isn't a GitHub remote bit recognizes. For GitHub Enterprise Server, run:\")\n );\n header.push(\n ` ${chalk.cyan(`GITHUB_SERVER_URL=${shellQuote(suggestedServerUrl(unrecognizedOrigin))} bit ci sync --init`)}`\n );\n }\n\n const width = Math.max(...input.files.map((f) => f.displayPath.length), configDisplayPath.length) + 2;\n const fileItem = (displayPath: string, note: string) => formatItem(`${displayPath.padEnd(width)}${formatHint(note)}`);\n const files = [\n ...input.files.map((file) =>\n fileItem(\n file.displayPath,\n file.status === 'written' ? (FILE_PURPOSE[file.relativePath] ?? '') : 'already exists, left unchanged'\n )\n ),\n fileItem(\n configDisplayPath,\n input.configAdded ? 'added \"teambit.git/ci\": { \"sync\": {} }' : 'already configures sync, left unchanged'\n ),\n ];\n\n const step = (n: number, title: string, who: string, body: string[]) =>\n [`${formatTitle(`${n}. ${title}`)} ${formatHint(`(${who})`)}`, ...body.map((line) => ` ${line}`)].join('\\n');\n const cmd = (text: string) => chalk.cyan(text);\n // Stage and commit exactly what git shows as new or changed among the files `--init` handles, so\n // unrelated local changes stay out: a re-run still stages files that were never committed, and a\n // file already committed is not named. The first commit of a new repository is the exception: it\n // must carry the whole workspace. `:/` stages from the repository root, where the workflows are,\n // when the workspace is a subdirectory.\n const firstCommit = input.repoHasCommits === false;\n const toStage = firstCommit ? [] : (input.toStage ?? [...input.files.map((f) => f.displayPath), configDisplayPath]);\n const ignored = input.ignored ?? [];\n const quoted = (paths: string[]) => paths.map(shellQuote).join(' ');\n const stage: string[] = [\n ...(firstCommit ? [cmd(repoRootFromCwd === '.' ? 'git add -A' : 'git add -A :/')] : []),\n ...(toStage.length ? [cmd(`git add -- ${quoted(toStage)}`)] : []),\n // `git add` skips or refuses a path that .gitignore matches, and GitHub runs only committed workflows\n ...(ignored.length\n ? [cmd(`git add -f -- ${quoted(ignored)}`), formatHint('.gitignore matches these; -f adds them anyway')]\n : []),\n ];\n // Commit and push are separate: on a re-run nothing may be left to commit, and the push must still run.\n // Naming the paths keeps changes the user staged before out of the commit.\n // `-b`: without init.defaultBranch, a bare `git init` makes `master`, a branch the workflows ignore\n const gitInit = `git init -b ${shellQuote(defaultBranch)}${repoRootFromCwd === '.' ? '' : ` ${shellQuote(repoRootFromCwd)}`}`;\n const toCommit = [...toStage, ...ignored];\n const nothingToCommit = !firstCommit && !toCommit.length;\n const commitMessage = `git commit -m ${shellQuote(`Mirror ${scope}`)}`;\n const commit = [\n ...(input.inGitRepo === false ? [cmd(gitInit)] : []),\n ...stage,\n ...(nothingToCommit\n ? [formatHint('nothing to commit: these files are already committed')]\n : firstCommit\n ? [cmd(commitMessage)]\n : // the paths go on a continuation line, as in the permissions command, to keep lines short\n [cmd(`${commitMessage} -- \\\\`), cmd(` ${quoted(toCommit)}`)]),\n ];\n\n const steps: string[] = [];\n if (!ownerRepo) {\n steps.push(\n unrecognizedOrigin\n ? step(1, 'Push it', 'you', [...commit, cmd('git push -u origin HEAD')])\n : step(1, 'Create the repository and push it', 'you', [\n ...commit,\n cmd(`gh repo create --private --source ${shellQuote(repoRootFromCwd)} --remote origin --push`),\n formatHint('names the repository after its root folder; add OWNER/NAME after \"create\" to choose'),\n ]),\n step(2, 'Run bit ci sync --init again', 'you', [\n 'Once origin is a GitHub remote bit recognizes, it prints the secret, permission and webhook',\n 'steps for your repository as commands.',\n ])\n );\n } else {\n const onGitHubCom = ownerRepo.host === 'github.com';\n const repo = `${ownerRepo.owner}/${ownerRepo.repo}`;\n // gh takes HOST/OWNER/REPO, or --hostname, for an enterprise server\n const ghRepo = shellQuote(onGitHubCom ? repo : `${ownerRepo.host}/${repo}`);\n const ghApi = onGitHubCom ? 'gh api' : `gh api --hostname ${shellQuote(ownerRepo.host)}`;\n steps.push(\n step(1, 'Push it', 'you', [...commit, cmd('git push -u origin HEAD')]),\n step(2, 'Let the workflows publish to bit.cloud and open pull requests', 'repository admin', [\n cmd(`gh secret set BIT_CONFIG_ACCESS_TOKEN -R ${ghRepo}`),\n formatHint(`a bit.cloud access token with Write permission on ${scope}. If your organization`),\n formatHint(\"requires change requests, exempt the token's account: it is the one that merges lanes.\"),\n cmd(`${ghApi} -X PUT ${shellQuote(`repos/${repo}/actions/permissions/workflow`)} \\\\`),\n cmd(' -f default_workflow_permissions=write -F can_approve_pull_request_reviews=true'),\n formatHint(\n 'turns on \"Read and write permissions\" and \"Allow GitHub Actions to create and approve pull requests\".'\n ),\n formatHint('If they are greyed out in the settings, an organization owner must allow them first.'),\n cmd(`gh secret set BIT_SYNC_GH_TOKEN -R ${ghRepo}`),\n formatHint('optional: a GitHub token, so the pushes the sync makes start your other workflows (CI checks)'),\n ])\n );\n if (onGitHubCom) {\n steps.push(\n step(3, 'Make Bit -> Git instant', 'organization admin, optional', [\n `Install ${chalk.cyan(BIT_GIT_SYNC_APP_URL)} on ${repo}.`,\n `Copy the repository's webhook URL from ${chalk.cyan(WEBHOOK_RELAY_SETUP_URL)}`,\n `and add it to ${scope} on bit.cloud as an \"Export succeeded\" webhook.`,\n ])\n );\n } else {\n const api = gitHubApiUrl(ownerRepo.host);\n const dispatchUrl = `${'error' in api ? '<GitHub API URL>' : api.url}/repos/${repo}/dispatches`;\n steps.push(\n step(3, 'Make Bit -> Git instant', 'optional', [\n `On bit.cloud, add an \"Export succeeded\" webhook to ${scope}:`,\n ` URL ${chalk.cyan(dispatchUrl)}`,\n ...('error' in api ? [chalk.yellow(` (${api.error})`)] : []),\n ' Headers Authorization: Bearer <token with access to this repository>',\n ' Accept: application/vnd.github+json',\n ` Payload ${WEBHOOK_PAYLOAD_TEMPLATE}`,\n formatHint('Create it fresh: editing a bit.cloud webhook drops its custom headers.'),\n ])\n );\n }\n }\n\n const footer = ownerRepo\n ? `After step 2 the sync runs on pushes to feature branches and every 30 minutes; step 3 adds instant Bit -> Git.\\nGuide: ${SETUP_GUIDE_URL}`\n : `Guide: ${SETUP_GUIDE_URL}`;\n return joinSections([\n header.join('\\n'),\n files.join('\\n'),\n [formatTitle(`Next: ${steps.length} steps to go live`), '', steps.join('\\n\\n')].join('\\n'),\n formatHint(footer),\n ]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,SAAAA,IAAA;EAAA,MAAAC,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAH,GAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAG,OAAA;EAAA,MAAAH,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAC,MAAA,YAAAA,CAAA;IAAA,OAAAH,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAI,KAAA;EAAA,MAAAJ,IAAA,GAAAE,OAAA;EAAAE,IAAA,YAAAA,CAAA;IAAA,OAAAJ,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAK,MAAA;EAAA,MAAAL,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAG,KAAA,YAAAA,CAAA;IAAA,OAAAL,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAM,cAAA;EAAA,MAAAN,IAAA,GAAAE,OAAA;EAAAI,aAAA,YAAAA,CAAA;IAAA,OAAAN,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAA4F,SAAAC,uBAAAM,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAE5F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACO,MAAMG,uBAAuB,GAAAC,OAAA,CAAAD,uBAAA,GAAG;EACrCE,IAAI,EAAEC,eAAI,CAACC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,CAAC;EACvDC,OAAO,EAAEF,eAAI,CAACC,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB;AAC9D,CAAU;AAEV,MAAME,qBAAqB,GAAG;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AAED,MAAMC,wBAAwB,GAAG;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;;AAED;AACA;AACA;AACA;AACA,MAAMC,qBAAqB,GAAG;EAC5BN,IAAI,EAAE,wCAAwC;EAC9CG,OAAO,EAAE;AACX,CAAU;;AAEV;AACA;AACA;AACA;AACA;AACA,SAASI,gBAAgBA,CAACC,KAAa,EAAU;EAC/C,OAAO,IAAIA,KAAK,CAACC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;AACzC;;AAEA;AACA;AACA;AACA;AACA;AACA,SAASC,uBAAuBA,CAACC,GAAW,EAAEC,MAAc,EAAEC,aAAqB,EAAU;EAC3F,IAAI,CAACF,GAAG,CAACG,QAAQ,CAACF,MAAM,CAAC,EAAE;IACzB,MAAM,IAAIG,KAAK,CACb,mCAAmCH,MAAM,uDAAuD,GAC9F,yFAAyF,GACzF,gFACJ,CAAC;EACH;EACA;EACA;EACA,MAAMI,iBAAiB,GAAGJ,MAAM,CAACH,OAAO,CAAC,MAAM,EAAE,MAAMF,gBAAgB,CAACM,aAAa,C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","ignoreList":[]}
|
|
@@ -43,6 +43,11 @@ function _initScaffold() {
|
|
|
43
43
|
return data;
|
|
44
44
|
}
|
|
45
45
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
46
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
47
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
48
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
49
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
50
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
46
51
|
const yamlLoad = src => _yaml().default.parse(src);
|
|
47
52
|
|
|
48
53
|
/** YAML 1.1-schema parsers resolve the plain scalar key `on` to `true`, so accept either key. */
|
|
@@ -82,6 +87,16 @@ describe('init-scaffold', () => {
|
|
|
82
87
|
(0, _chai().expect)((0, _initScaffold().renderBitSyncWorkflow)("it's/a-branch")).to.contain("'it''s/a-branch'");
|
|
83
88
|
(0, _chai().expect)(yamlLoad((0, _initScaffold().renderBitSyncWorkflow)('release/main')).jobs).to.be.an('object');
|
|
84
89
|
});
|
|
90
|
+
it('set the action ws-dir for a workspace below the repository root, and leave it commented at the root', () => {
|
|
91
|
+
const withInput = doc => Object.values(yamlLoad(doc).jobs).flatMap(job => job.steps).find(s => String(s.uses).startsWith('teambit/bit-git-sync')).with;
|
|
92
|
+
[_initScaffold().renderBitSyncWorkflow, _initScaffold().renderBitReleaseWorkflow].forEach(render => {
|
|
93
|
+
(0, _chai().expect)(withInput(render('main', 'packages/app'))['ws-dir']).to.equal('packages/app');
|
|
94
|
+
(0, _chai().expect)(withInput(render('main', "it's dir"))['ws-dir']).to.equal("it's dir");
|
|
95
|
+
(0, _chai().expect)(withInput(render('main'))).to.not.have.property('ws-dir');
|
|
96
|
+
(0, _chai().expect)(render('main')).to.contain('# ws-dir: packages/my-workspace');
|
|
97
|
+
(0, _chai().expect)(render('main', '.')).to.equal(render('main'));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
85
100
|
});
|
|
86
101
|
describe('scaffoldWorkflowFiles', () => {
|
|
87
102
|
let workspaceDir;
|
|
@@ -145,38 +160,268 @@ describe('init-scaffold', () => {
|
|
|
145
160
|
REMOTES.forEach(([remote, expected]) => (0, _chai().expect)((0, _initScaffold().deriveOwnerRepo)(remote), String(remote)).to.deep.equal(expected));
|
|
146
161
|
});
|
|
147
162
|
});
|
|
148
|
-
describe('
|
|
149
|
-
it('
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
describe('chooseInitDefaultBranch', () => {
|
|
164
|
+
it('takes the detected branch, else main or master when checked out, else main', () => {
|
|
165
|
+
(0, _chai().expect)((0, _initScaffold().chooseInitDefaultBranch)('trunk', 'feature/x')).to.equal('trunk');
|
|
166
|
+
(0, _chai().expect)((0, _initScaffold().chooseInitDefaultBranch)(undefined, 'master')).to.equal('master');
|
|
167
|
+
(0, _chai().expect)((0, _initScaffold().chooseInitDefaultBranch)(undefined, 'main')).to.equal('main');
|
|
168
|
+
(0, _chai().expect)((0, _initScaffold().chooseInitDefaultBranch)(undefined, 'feature/x')).to.equal('main');
|
|
169
|
+
(0, _chai().expect)((0, _initScaffold().chooseInitDefaultBranch)(undefined, undefined)).to.equal('main');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe('renderInitReport', () => {
|
|
173
|
+
const files = [{
|
|
174
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.sync,
|
|
175
|
+
status: 'written',
|
|
176
|
+
displayPath: '.github/workflows/bit-sync.yml'
|
|
177
|
+
}, {
|
|
178
|
+
relativePath: _initScaffold().WORKFLOW_RELATIVE_PATHS.release,
|
|
179
|
+
status: 'written',
|
|
180
|
+
displayPath: '.github/workflows/bit-release.yml'
|
|
181
|
+
}];
|
|
182
|
+
// eslint-disable-next-line no-control-regex
|
|
183
|
+
const plain = text => text.replace(/\u001b\[[0-9;]*m/g, '');
|
|
184
|
+
const report = (remote, extra = {}) => plain((0, _initScaffold().renderInitReport)(_objectSpread({
|
|
185
|
+
scopeId: 'acme.shop',
|
|
186
|
+
ownerRepo: (0, _initScaffold().deriveOwnerRepo)(remote),
|
|
187
|
+
defaultBranch: 'main',
|
|
188
|
+
files,
|
|
189
|
+
configAdded: true
|
|
190
|
+
}, extra)));
|
|
191
|
+
it('prints each step once, as commands for the derived repository', () => {
|
|
192
|
+
const out = report('git@github.com:acme/shop.git');
|
|
193
|
+
(0, _chai().expect)(out).to.contain('for acme.shop, mirrored to acme/shop (default branch: main)');
|
|
194
|
+
(0, _chai().expect)(out).to.contain('git push -u origin HEAD');
|
|
195
|
+
(0, _chai().expect)(out).to.contain('gh secret set BIT_CONFIG_ACCESS_TOKEN -R acme/shop');
|
|
196
|
+
(0, _chai().expect)(out).to.contain('gh api -X PUT repos/acme/shop/actions/permissions/workflow');
|
|
197
|
+
(0, _chai().expect)(out.match(/Next: 3 steps to go live/g)).to.have.length(1);
|
|
198
|
+
});
|
|
199
|
+
it('points github.com repositories at the GitHub App and the relay, not at a personal token', () => {
|
|
200
|
+
const out = report('https://github.com/acme/shop.git');
|
|
201
|
+
(0, _chai().expect)(out).to.contain('https://github.com/apps/bit-git-sync');
|
|
202
|
+
(0, _chai().expect)(out).to.contain('/setup');
|
|
203
|
+
(0, _chai().expect)(out).to.not.contain('/dispatches');
|
|
204
|
+
(0, _chai().expect)(out).to.not.contain('Authorization: Bearer');
|
|
205
|
+
});
|
|
206
|
+
it('prints no placeholder commands when there is no GitHub remote yet', () => {
|
|
207
|
+
const out = report(undefined);
|
|
208
|
+
(0, _chai().expect)(out).to.contain('no GitHub remote yet');
|
|
209
|
+
(0, _chai().expect)(out).to.contain('gh repo create --private --source . --remote origin --push');
|
|
210
|
+
(0, _chai().expect)(out).to.not.contain('GITHUB_SERVER_URL');
|
|
211
|
+
(0, _chai().expect)(out).to.contain('Run bit ci sync --init again');
|
|
212
|
+
(0, _chai().expect)(out).to.contain('Next: 2 steps to go live');
|
|
213
|
+
(0, _chai().expect)(out).to.not.contain('<owner>');
|
|
214
|
+
(0, _chai().expect)(out).to.not.contain('gh secret set');
|
|
215
|
+
});
|
|
216
|
+
it('commits and pushes in separate commands, so a re-run with nothing to commit still pushes', () => {
|
|
217
|
+
const out = report('git@github.com:acme/shop.git');
|
|
218
|
+
(0, _chai().expect)(out).to.match(/git commit -m 'Mirror acme\.shop' -- \\\n\s+[^\n]+\n\s+git push -u origin HEAD/);
|
|
219
|
+
(0, _chai().expect)(out).to.not.contain('&& git');
|
|
220
|
+
});
|
|
221
|
+
it('stages only the files --init touched, so unrelated local changes stay out of the commit', () => {
|
|
222
|
+
const out = report('git@github.com:acme/shop.git');
|
|
223
|
+
(0, _chai().expect)(out).to.contain('git add -- .github/workflows/bit-sync.yml .github/workflows/bit-release.yml workspace.jsonc\n');
|
|
224
|
+
(0, _chai().expect)(out).to.not.contain('git add -A');
|
|
225
|
+
// the paths are the ones shown, as seen from where the user stands
|
|
226
|
+
(0, _chai().expect)(report('git@github.com:acme/shop.git', {
|
|
227
|
+
files: files.map(f => _objectSpread(_objectSpread({}, f), {}, {
|
|
228
|
+
displayPath: `/repo root/${f.displayPath}`
|
|
229
|
+
})),
|
|
230
|
+
configDisplayPath: 'workspace.jsonc'
|
|
231
|
+
})).to.contain("git add -- '/repo root/.github/workflows/bit-sync.yml'");
|
|
232
|
+
});
|
|
233
|
+
it('stages exactly the files git shows as new or changed', () => {
|
|
234
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
235
|
+
toStage: ['.github/workflows/bit-sync.yml'],
|
|
236
|
+
ignored: []
|
|
237
|
+
});
|
|
238
|
+
(0, _chai().expect)(out).to.contain('git add -- .github/workflows/bit-sync.yml\n');
|
|
239
|
+
(0, _chai().expect)(out).to.not.contain('bit-release.yml workspace.jsonc');
|
|
240
|
+
});
|
|
241
|
+
it('force-adds a workflow file that .gitignore matches, so the rest of the staging still runs', () => {
|
|
242
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
243
|
+
toStage: ['workspace.jsonc'],
|
|
244
|
+
ignored: ['.github/workflows/bit-release.yml']
|
|
245
|
+
});
|
|
246
|
+
(0, _chai().expect)(out).to.contain('git add -- workspace.jsonc\n');
|
|
247
|
+
(0, _chai().expect)(out).to.contain('git add -f -- .github/workflows/bit-release.yml\n');
|
|
248
|
+
(0, _chai().expect)(out).to.contain('.gitignore matches these');
|
|
249
|
+
(0, _chai().expect)(out).to.contain("git commit -m 'Mirror acme.shop'");
|
|
250
|
+
});
|
|
251
|
+
it('commits only the files it stages, so changes staged before stay out of the commit', () => {
|
|
252
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
253
|
+
toStage: ['workspace.jsonc'],
|
|
254
|
+
ignored: ['.github/workflows/bit-release.yml']
|
|
255
|
+
});
|
|
256
|
+
(0, _chai().expect)(out).to.match(/git commit -m 'Mirror acme\.shop' -- \\\n\s+workspace\.jsonc \.github\/workflows\/bit-release\.yml\n/);
|
|
257
|
+
});
|
|
258
|
+
it('force-adds an ignored workflow file also for the first commit, which git add -A would skip', () => {
|
|
259
|
+
const out = report(undefined, {
|
|
260
|
+
repoHasCommits: false,
|
|
261
|
+
ignored: ['.github/workflows/bit-sync.yml']
|
|
262
|
+
});
|
|
263
|
+
(0, _chai().expect)(out).to.match(/git add -A\n\s+git add -f -- \.github\/workflows\/bit-sync\.yml\n/);
|
|
264
|
+
// the first commit takes the whole workspace, so it names no paths
|
|
265
|
+
(0, _chai().expect)(out).to.match(/git commit -m 'Mirror acme\.shop'\n/);
|
|
266
|
+
});
|
|
267
|
+
it('prints no commit when nothing is left to commit, and still pushes', () => {
|
|
268
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
269
|
+
toStage: [],
|
|
270
|
+
ignored: []
|
|
271
|
+
});
|
|
272
|
+
(0, _chai().expect)(out).to.not.contain('git add');
|
|
273
|
+
(0, _chai().expect)(out).to.not.contain('git commit');
|
|
274
|
+
(0, _chai().expect)(out).to.contain('nothing to commit');
|
|
275
|
+
(0, _chai().expect)(out).to.contain('git push -u origin HEAD');
|
|
276
|
+
});
|
|
277
|
+
it('starts with git init when the workspace is not in a git repository', () => {
|
|
278
|
+
const out = report(undefined, {
|
|
279
|
+
inGitRepo: false,
|
|
280
|
+
repoHasCommits: false
|
|
281
|
+
});
|
|
282
|
+
(0, _chai().expect)(out).to.match(/git init -b main\n\s+git add -A\n\s+git commit -m/);
|
|
283
|
+
(0, _chai().expect)(report(undefined, {
|
|
284
|
+
inGitRepo: false,
|
|
285
|
+
repoRootFromCwd: '..'
|
|
286
|
+
})).to.contain('git init -b main ..\n');
|
|
287
|
+
// the branch the workflows use, not git's own default (master when init.defaultBranch is unset)
|
|
288
|
+
(0, _chai().expect)(report(undefined, {
|
|
289
|
+
inGitRepo: false,
|
|
290
|
+
defaultBranch: 'trunk'
|
|
291
|
+
})).to.contain('git init -b trunk\n');
|
|
292
|
+
(0, _chai().expect)(report(undefined)).to.not.contain('git init');
|
|
293
|
+
});
|
|
294
|
+
it('stages the whole workspace for the first commit of a new repository', () => {
|
|
295
|
+
(0, _chai().expect)(report(undefined, {
|
|
296
|
+
repoHasCommits: false
|
|
297
|
+
})).to.contain('git add -A\n');
|
|
298
|
+
(0, _chai().expect)(report(undefined, {
|
|
299
|
+
repoHasCommits: false,
|
|
300
|
+
repoRootFromCwd: '../..'
|
|
301
|
+
})).to.contain('git add -A :/\n');
|
|
302
|
+
});
|
|
303
|
+
it('shell-quotes values it puts in commands', () => {
|
|
304
|
+
(0, _chai().expect)((0, _initScaffold().shellQuote)('main')).to.equal('main');
|
|
305
|
+
(0, _chai().expect)((0, _initScaffold().shellQuote)('feature/x-1.2')).to.equal('feature/x-1.2');
|
|
306
|
+
(0, _chai().expect)((0, _initScaffold().shellQuote)('$(id)')).to.equal("'$(id)'");
|
|
307
|
+
(0, _chai().expect)((0, _initScaffold().shellQuote)("it's")).to.equal("'it'\\''s'");
|
|
308
|
+
// the push names HEAD, so a branch name never reaches a command
|
|
309
|
+
(0, _chai().expect)(report('git@github.com:acme/shop.git', {
|
|
310
|
+
defaultBranch: '$(id)'
|
|
311
|
+
})).to.contain('git push -u origin HEAD');
|
|
312
|
+
});
|
|
313
|
+
it('names the token-account exemption, the PR setting and the optional CI token', () => {
|
|
314
|
+
const out = report('git@github.com:acme/shop.git');
|
|
315
|
+
(0, _chai().expect)(out).to.contain('exempt the token');
|
|
316
|
+
(0, _chai().expect)(out).to.contain('Allow GitHub Actions to create and approve pull requests');
|
|
317
|
+
// one command sets both settings guide step 4 names
|
|
318
|
+
(0, _chai().expect)(out).to.contain('-f default_workflow_permissions=write -F can_approve_pull_request_reviews=true');
|
|
319
|
+
(0, _chai().expect)(out).to.contain('Read and write permissions');
|
|
320
|
+
// the command line ends at the command, so it runs when pasted
|
|
321
|
+
(0, _chai().expect)(out).to.match(/gh secret set BIT_SYNC_GH_TOKEN -R acme\/shop\n\s+optional: a GitHub token/);
|
|
322
|
+
});
|
|
323
|
+
it('states the schedule the scaffolded workflow installs', () => {
|
|
324
|
+
(0, _chai().expect)((0, _initScaffold().renderBitSyncWorkflow)('main')).to.contain("cron: '*/30 * * * *'");
|
|
325
|
+
(0, _chai().expect)(report('git@github.com:acme/shop.git')).to.contain('every 30 minutes');
|
|
326
|
+
});
|
|
327
|
+
it('names an origin it does not recognize, and says how to mark a GitHub Enterprise Server', () => {
|
|
328
|
+
const origin = 'git@github.corp.example:acme/store.git';
|
|
329
|
+
const out = report(origin, {
|
|
330
|
+
originUrl: origin
|
|
331
|
+
});
|
|
332
|
+
(0, _chai().expect)(out).to.contain(`origin ${origin}`);
|
|
333
|
+
(0, _chai().expect)(out).to.contain('GITHUB_SERVER_URL=https://github.corp.example bit ci sync --init');
|
|
334
|
+
(0, _chai().expect)(out).to.contain('git push -u origin HEAD');
|
|
335
|
+
(0, _chai().expect)(out).to.not.contain('no GitHub remote yet');
|
|
336
|
+
// the host is unknown, so no github.com steps
|
|
337
|
+
(0, _chai().expect)(out).to.not.contain('github.com/apps/bit-git-sync');
|
|
338
|
+
(0, _chai().expect)(out).to.not.contain('gh secret set');
|
|
339
|
+
});
|
|
340
|
+
it('marks files that already existed and a config block that was already there', () => {
|
|
341
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
342
|
+
files: [_objectSpread(_objectSpread({}, files[0]), {}, {
|
|
343
|
+
status: 'skipped'
|
|
344
|
+
}), files[1]],
|
|
345
|
+
configAdded: false
|
|
346
|
+
});
|
|
347
|
+
(0, _chai().expect)(out).to.match(/\.github\/workflows\/bit-sync\.yml\s+already exists, left unchanged/);
|
|
348
|
+
(0, _chai().expect)(out).to.match(/\.github\/workflows\/bit-release\.yml\s+releases the scope/);
|
|
349
|
+
(0, _chai().expect)(out).to.match(/workspace\.jsonc\s+already configures sync/);
|
|
350
|
+
});
|
|
351
|
+
it('creates the repository from the repository root when the workspace is a subdirectory', () => {
|
|
352
|
+
(0, _chai().expect)(report(undefined, {
|
|
353
|
+
repoRootFromCwd: '../..'
|
|
354
|
+
})).to.contain('gh repo create --private --source ../.. --remote origin --push');
|
|
355
|
+
(0, _chai().expect)(report(undefined, {
|
|
356
|
+
repoRootFromCwd: '/my repo'
|
|
357
|
+
})).to.contain("--source '/my repo' --remote");
|
|
358
|
+
});
|
|
359
|
+
it('flags a guessed default branch', () => {
|
|
360
|
+
const origin = 'git@github.com:acme/shop.git';
|
|
361
|
+
(0, _chai().expect)(report(origin, {
|
|
362
|
+
originUrl: origin,
|
|
363
|
+
defaultBranchGuessed: true
|
|
364
|
+
})).to.contain("the remote didn't name its default branch");
|
|
365
|
+
(0, _chai().expect)(report(undefined, {
|
|
366
|
+
defaultBranchGuessed: true
|
|
367
|
+
})).to.contain('no remote names the default branch');
|
|
368
|
+
(0, _chai().expect)(report(origin)).to.not.contain('default branch, so');
|
|
369
|
+
});
|
|
370
|
+
it('suggests no server for an origin that is a path on this machine', () => {
|
|
371
|
+
['/tmp/e2e/remote.git', '../remote.git', 'remote.git', 'repos/remote.git', 'file:///srv/remote.git', 'C:\\repos\\remote.git'].forEach(origin => {
|
|
372
|
+
(0, _chai().expect)((0, _initScaffold().isLocalPathRemote)(origin), origin).to.be.true;
|
|
373
|
+
const out = report(origin, {
|
|
374
|
+
originUrl: origin
|
|
172
375
|
});
|
|
173
|
-
(0, _chai().expect)(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
376
|
+
(0, _chai().expect)(out, origin).to.contain(`origin ${origin}`);
|
|
377
|
+
(0, _chai().expect)(out, origin).to.not.contain('GITHUB_SERVER_URL');
|
|
378
|
+
(0, _chai().expect)(out, origin).to.not.contain('<your-server>');
|
|
379
|
+
(0, _chai().expect)(out, origin).to.contain('git push -u origin HEAD');
|
|
380
|
+
});
|
|
381
|
+
(0, _chai().expect)((0, _initScaffold().isLocalPathRemote)('git@ghe.example:acme/store.git')).to.be.false;
|
|
382
|
+
(0, _chai().expect)((0, _initScaffold().isLocalPathRemote)('https://ghe.example/acme/store.git')).to.be.false;
|
|
383
|
+
(0, _chai().expect)((0, _initScaffold().isLocalPathRemote)('ssh://git@ghe.example:2222/acme/store.git')).to.be.false;
|
|
384
|
+
(0, _chai().expect)((0, _initScaffold().isLocalPathRemote)('ghe.example:acme/store.git')).to.be.false;
|
|
385
|
+
});
|
|
386
|
+
it('prints control characters in an origin as inert text, and suggests no server for it', () => {
|
|
387
|
+
(0, _chai().expect)((0, _initScaffold().escapeControlChars)('a\nb\u001b[2Jc')).to.equal('a\\x0ab\\x1b[2Jc');
|
|
388
|
+
const origin = 'https://ghe.example/acme/store.git\n\u001b[32m✔ all set';
|
|
389
|
+
const out = (0, _initScaffold().renderInitReport)({
|
|
390
|
+
scopeId: 'acme.shop',
|
|
391
|
+
originUrl: origin,
|
|
392
|
+
defaultBranch: 'main',
|
|
393
|
+
files,
|
|
394
|
+
configAdded: true
|
|
395
|
+
});
|
|
396
|
+
(0, _chai().expect)(out).to.not.contain('\u001b[32m');
|
|
397
|
+
(0, _chai().expect)(out).to.not.contain('store.git\n');
|
|
398
|
+
(0, _chai().expect)(out).to.contain('origin https://ghe.example/acme/store.git\\x0a\\x1b[32m');
|
|
399
|
+
(0, _chai().expect)(out).to.not.contain('GITHUB_SERVER_URL');
|
|
400
|
+
});
|
|
401
|
+
it('never prints the credentials an origin URL carries', () => {
|
|
402
|
+
(0, _chai().expect)((0, _initScaffold().redactRemoteUrl)('https://user:ghp_secret@git.corp.example/acme/store.git')).to.equal('https://git.corp.example/acme/store.git');
|
|
403
|
+
(0, _chai().expect)((0, _initScaffold().redactRemoteUrl)('https://ghp_secret@git.corp.example/acme/store.git')).to.equal('https://git.corp.example/acme/store.git');
|
|
404
|
+
(0, _chai().expect)((0, _initScaffold().redactRemoteUrl)('ssh://git:secret@git.corp.example/acme/store.git')).to.equal('ssh://git@git.corp.example/acme/store.git');
|
|
405
|
+
(0, _chai().expect)((0, _initScaffold().redactRemoteUrl)('ssh://git@git.corp.example/acme/store.git')).to.equal('ssh://git@git.corp.example/acme/store.git');
|
|
406
|
+
(0, _chai().expect)((0, _initScaffold().redactRemoteUrl)('git@git.corp.example:acme/store.git')).to.equal('git@git.corp.example:acme/store.git');
|
|
407
|
+
const origin = 'https://user:ghp_secret@git.corp.example:8443/acme/store.git';
|
|
408
|
+
const out = report(origin, {
|
|
409
|
+
originUrl: origin
|
|
410
|
+
});
|
|
411
|
+
(0, _chai().expect)(out).to.not.contain('ghp_secret');
|
|
412
|
+
(0, _chai().expect)(out).to.contain('origin https://git.corp.example:8443/acme/store.git');
|
|
413
|
+
(0, _chai().expect)(out).to.contain('GITHUB_SERVER_URL=https://git.corp.example:8443 bit ci sync --init');
|
|
414
|
+
});
|
|
415
|
+
it('suggests a GITHUB_SERVER_URL that keeps an https port, and drops an ssh one', () => {
|
|
416
|
+
(0, _chai().expect)((0, _initScaffold().suggestedServerUrl)('https://ghe.example:8443/acme/store.git')).to.equal('https://ghe.example:8443');
|
|
417
|
+
(0, _chai().expect)((0, _initScaffold().suggestedServerUrl)('git@ghe.example:acme/store.git')).to.equal('https://ghe.example');
|
|
418
|
+
(0, _chai().expect)((0, _initScaffold().suggestedServerUrl)('ssh://git@ghe.example:2222/acme/store.git')).to.equal('https://ghe.example');
|
|
419
|
+
const origin = 'https://ghe.example:8443/acme/store.git';
|
|
420
|
+
(0, _chai().expect)(report(origin, {
|
|
421
|
+
originUrl: origin
|
|
422
|
+
})).to.contain('GITHUB_SERVER_URL=https://ghe.example:8443 bit ci sync --init');
|
|
178
423
|
});
|
|
179
|
-
describe('on
|
|
424
|
+
describe('on GitHub Enterprise Server', () => {
|
|
180
425
|
const saved = {};
|
|
181
426
|
beforeEach(() => {
|
|
182
427
|
saved.api = process.env.GITHUB_API_URL;
|
|
@@ -187,27 +432,32 @@ describe('init-scaffold', () => {
|
|
|
187
432
|
if (saved.api === undefined) delete process.env.GITHUB_API_URL;else process.env.GITHUB_API_URL = saved.api;
|
|
188
433
|
if (saved.server === undefined) delete process.env.GITHUB_SERVER_URL;else process.env.GITHUB_SERVER_URL = saved.server;
|
|
189
434
|
});
|
|
190
|
-
it('
|
|
435
|
+
it('uses the dispatch webhook on the server API, and gh commands that name the host', () => {
|
|
191
436
|
process.env.GITHUB_API_URL = 'https://ghe.corp.example/api/v3';
|
|
192
|
-
const
|
|
193
|
-
(0, _chai().expect)(
|
|
194
|
-
(0, _chai().expect)(
|
|
437
|
+
const out = report('git@ghe.corp.example:acme/store.git');
|
|
438
|
+
(0, _chai().expect)(out).to.contain('https://ghe.corp.example/api/v3/repos/acme/store/dispatches');
|
|
439
|
+
(0, _chai().expect)(out).to.contain('"event_type":"bit-export"');
|
|
440
|
+
(0, _chai().expect)(out).to.contain('gh secret set BIT_CONFIG_ACCESS_TOKEN -R ghe.corp.example/acme/store');
|
|
441
|
+
(0, _chai().expect)(out).to.contain('gh api --hostname ghe.corp.example -X PUT repos/acme/store/');
|
|
442
|
+
(0, _chai().expect)(out).to.not.contain('github.com/apps/bit-git-sync');
|
|
195
443
|
});
|
|
196
|
-
it('derives <server>/api/v3
|
|
444
|
+
it('derives <server>/api/v3 when GITHUB_API_URL is unset', () => {
|
|
197
445
|
delete process.env.GITHUB_API_URL;
|
|
198
|
-
(0, _chai().expect)((
|
|
446
|
+
(0, _chai().expect)(report('https://ghe.corp.example/acme/store.git')).to.contain('https://ghe.corp.example/api/v3/repos/acme/store/dispatches');
|
|
199
447
|
});
|
|
200
|
-
it('names no API
|
|
448
|
+
it('names no API when GITHUB_API_URL is on another host', () => {
|
|
201
449
|
process.env.GITHUB_API_URL = 'https://api.github.com';
|
|
202
|
-
const
|
|
203
|
-
(0, _chai().expect)(
|
|
204
|
-
(0, _chai().expect)(
|
|
205
|
-
(0, _chai().expect)(
|
|
450
|
+
const out = report('https://ghe.corp.example/acme/store.git');
|
|
451
|
+
(0, _chai().expect)(out).to.contain('<GitHub API URL>/repos/acme/store/dispatches');
|
|
452
|
+
(0, _chai().expect)(out).to.contain('GITHUB_API_URL "https://api.github.com"');
|
|
453
|
+
(0, _chai().expect)(out).to.not.contain('https://api.github.com/repos');
|
|
454
|
+
});
|
|
455
|
+
it('keeps the App path for a github.com origin', () => {
|
|
456
|
+
process.env.GITHUB_API_URL = 'https://ghe.corp.example/api/v3';
|
|
457
|
+
const out = report('git@github.com:acme/shop.git');
|
|
458
|
+
(0, _chai().expect)(out).to.contain('https://github.com/apps/bit-git-sync');
|
|
459
|
+
(0, _chai().expect)(out).to.not.contain('ghe.corp.example');
|
|
206
460
|
});
|
|
207
|
-
});
|
|
208
|
-
it('always includes the secrets, permissions, webhook and fetch-depth checklist items', () => {
|
|
209
|
-
const checklist = (0, _initScaffold().renderInitChecklist)(undefined);
|
|
210
|
-
['BIT_CONFIG_ACCESS_TOKEN', 'BIT_SYNC_GH_TOKEN', 'contents: write', 'Components > Export', 'Authorization: Bearer <PAT>', 'Accept: application/vnd.github+json', '"event_type":"bit-export"', '{{laneId}}', 'drops its custom headers', 'fetch-depth: 0'].forEach(item => (0, _chai().expect)(checklist, item).to.contain(item));
|
|
211
461
|
});
|
|
212
462
|
});
|
|
213
463
|
});
|