This repository is a runnable example of the bit ci sync integration. It
holds one small Bit component, the sync configuration, and the GitHub Actions
workflows that keep bit.cloud and git equal.
The example carries no organization of its own. You clone it, you point it at your own bit.cloud scope, and you watch the four flows run in your own repository.
| Flow | You do this | The action does this | You see this |
|---|---|---|---|
| 1. Lane to branch | You export a lane on bit.cloud. | It creates a branch for the lane and opens a pull request. | A new branch and a new pull request. |
| 2. Branch to lane | You push a commit to the lane branch. | It snaps the branch content and exports it to the lane. | The lane on bit.cloud carries your git edit. |
| 3. Merge to release | You merge the pull request into main. |
It merges the lane into the main scope, then it tags and exports new versions. | New component versions on bit.cloud, and an archived lane. |
| 4. Main drift to git | You export to the main scope. | It opens a pull request from bit-sync/main. |
A pull request that carries the exported state. |
The four flows use two workflows. bit-sync.yml runs flows 1, 2 and 4.
bit-release.yml runs flow 3.
The bit ci sync command arrived in bit 2.0.65. Every earlier release has bit ci pr and
bit ci merge, but not bit ci sync. The workspace.jsonc of this repository pins the version
for the workflows. bit-tasks/init@v2 reads the engine value in teambit.harmony/bit and
installs that version. Without the pin, the runner gets the latest stable release. That release
does not have bit ci sync.
If a workflow runs a sync on a bit version without the command, the action stops and names the requirement.
| Item | Reason |
|---|---|
| A bit.cloud organization and one scope | The example exports components to your scope. |
| The Bit CLI | setup.sh calls bit install, bit add and bit status. |
| A GitHub repository that you own | The workflows need write permission on it. |
| A bit.cloud token of a service account | The workflows write to your scope with it. |
A GitHub token with the repo scope |
bit.cloud sends it in the webhook header. |
Install the Bit CLI with npx @teambit/bvm install.
- Fork this repository, or copy its files into your own repository.
- Clone your repository.
- Run the setup script with your scope id:
./setup.sh acme.shopThe script does five things. It writes your scope into workspace.jsonc. It
initializes the workspace with bit init. It runs bit install. It tracks
the utils/schema-node-label component. It prints the workspace status.
The script rejects a value that is not a scope id. A scope id has two parts
and one dot, for example acme.shop.
The repository ships "defaultScope": "CHANGE-ME.CHANGE-ME". The script
replaces that placeholder. Commit the changed workspace.jsonc and the new
.bitmap.
The script cannot do these three steps for you.
- Get a token:
bit login --machine-name ci. - Go to Settings > Secrets and variables > Actions.
- Select New repository secret.
- Name the secret
BIT_CONFIG_ACCESS_TOKEN. - Paste the token value.
The token belongs to a service account with write permission on your scope. Never commit the token. Never print it in a workflow step.
- Go to Settings > Actions > General.
- Find Workflow permissions.
- Select Allow GitHub Actions to create and approve pull requests.
If this setting is off, the run fails when it opens the pull request.
- Go to your bit.cloud organization: Settings > Webhooks > Create webhook.
- Select the event Components > Export succeeded.
- Set the URL to
https://api.github.com/repos/<owner>/<repo>/dispatches. - Add the header
Authorization: Bearer <GitHub token with repo scope>. - Add the header
Accept: application/vnd.github+json. - Select the template type Custom.
- Paste this payload template:
{"event_type":"bit-export","client_payload":{"owner":"{{owner}}","componentIds":"{{componentIds}}","username":"{{username}}","userId":"{{userId}}","laneId":"{{laneId}}"}}bit.cloud replaces each {{...}} token before it sends the request. A correct
delivery returns 204.
The Authorization header holds a GitHub token, not a bit.cloud token.
bit.cloud cannot read your repository secrets, so ${{ secrets.* }} has no
meaning in this field.
laneId is the discriminator. A lane export sends <scope>/<lane>. A main
export sends an empty value.
After you save the webhook, export a lane. Then read the delivery log. A correct delivery returns 204.
- Create a lane:
bit lane create hello. - Change the label format in
components/utils/schema-node-label/schema-node-label.ts. - Snap the change:
bit snap -m "change the label format". - Export the lane:
bit export.
Expected result: the webhook starts the bit-sync workflow. The run
creates the branch hello. The run opens a pull request from hello into
main. The pull request body names the lane, the components and the lane head.
- Fetch the new branch:
git fetch origin. - Check it out:
git checkout hello. - Change the label format in
components/utils/schema-node-label/schema-node-label.tsagain. - Commit the change and push it.
Expected result: the push starts the bit-sync workflow. The run snaps the
branch content and exports it to the lane. The lane head on bit.cloud moves
forward and carries your git edit.
The workflow ignores a push to main and a push to bit-sync/**, because
those pushes are the action's own output.
- Open the pull request from flow 1.
- Read the diff.
- Merge the pull request into
main.
Expected result: the merge starts the bit-release workflow. The run
merges the lane into the main scope. The run tags and exports new component
versions. The remote lane becomes archived. The version numbers therefore
describe merged state only.
- Switch to main:
bit switch main. - Snap a change:
bit snap -m "change the label format on main". - Export the change:
bit export.
Expected result: the webhook sends an empty laneId, so the run reconciles
the main scope. The run opens a pull request from bit-sync/main into main.
The diff shows the exported state of your scope.
Read this diff before you merge it. The main-sync pull request checks the workspace out to the exported versions. If a file in git holds a change that nobody exported, the diff reverts that change. Close the pull request instead of merging it if you want to keep the change.
A merged bit-sync/main pull request starts no release. The main scope is
already ahead of that merge.
The schedule runs every hour, and it repairs a lost webhook delivery. It is also the only way to find a deleted lane, because bit.cloud has no lane-removed event.
To run the reconcile now, go to Actions > bit-sync > Run workflow. Leave
the lane input empty to reconcile every lane. Type a lane name to reconcile
one lane.
The teambit.git/ci block in workspace.jsonc holds the sync configuration.
This example ships three keys:
| Key | What it decides | Value here |
|---|---|---|
lanes |
Which lanes get a branch. | ["*"] — every lane. |
mainSync |
How main-scope drift reaches the default branch. | "pr" — a pull request, never a direct push. |
onConflict |
What happens to one contested line. | "halt" — the run stops and labels the pull request. |
Four more keys are valid. This example uses none of them.
| Key | What it decides |
|---|---|
branches |
The branch name of one named lane. |
branchPrefix |
The text before each branch name. |
mainSyncBranch |
The name of the main-sync branch. |
autoMergeMainSyncPr |
Auto-merge on the main-sync pull request. |
No key decides who approves a change, because people merge pull requests.
Each workflow uses teambit/bit-git-sync@66c0fdf9e34e45f70b4f397b38b2b01f79dd1f41.
The pin is a commit SHA, not a tag. The job holds contents: write and
pull-requests: write, so a moved tag would give new code that write
permission. Update the SHA when you choose to, and read the change first.
bit-adopt-pr.yml turns an ordinary git pull request into a lane. Use it if
your developers start work in git instead of on a lane.
The workflow runs bit ci pr --keep-lane, then it pushes the new lane pointer
in .bitmap back to the pull request branch. Flow 2 then keeps the pair equal.
The workflow skips three cases. It skips a branch that starts with
bit-sync/, because the action owns that branch. It skips a pull request from
github-actions[bot]. It skips a pull request from a fork, because the push
back is not permitted.
The workflow adopts each pull request one time. A lane pointer in .bitmap is
the evidence of an earlier adoption, and the job stops when it finds one.
Delete this file if you do not want the behavior.
| Symptom | Cause | Repair |
|---|---|---|
| No run starts after an export. | The webhook delivery failed. | Read the delivery log on bit.cloud. A 401 means the Authorization header is wrong or absent. Check the header, then send a test delivery. A 404 means the URL names the wrong repository. |
| A run starts, but no pull request appears. | The repository forbids the write. | Turn on Allow GitHub Actions to create and approve pull requests. Confirm that the workflow declares pull-requests: write. |
The run halts, and the pull request gets the label bit-sync-conflict. |
Git and the lane changed the same line. | Read the comment on the pull request. Resolve the conflict on the branch, push the result, then remove the label. The sync stays paused for that lane while the label is present. |
| The run halts with a shallow-clone message. | actions/checkout fetched one commit. |
Keep fetch-depth: 0 in the checkout step. The reconciler reads the full history, so a shallow clone stops it before any write. |
| The run reports that the lane is not in this scope. | The lane holds components from another scope. | One lane must hold components of one scope in this version. Move the foreign components to their own lane. |
| Path | Purpose |
|---|---|
workspace.jsonc |
The workspace, the env and the sync configuration. |
components/utils/schema-node-label/ |
The example component. |
setup.sh |
Writes your scope, installs, tracks the component. |
.github/workflows/bit-sync.yml |
Flows 1, 2 and 4. |
.github/workflows/bit-release.yml |
Flow 3. |
.github/workflows/bit-adopt-pr.yml |
The optional adopt workflow. |
Apache-2.0. See LICENSE.