GitHub & source control
Factorial Code stores your code in the cloud, and the workspace console is a full editor — you never
have to use Git. But a team that wants pull requests, code review, branch history or CI can have
all of it: fcode team:clone lays every App and global
workspace the team owns out on disk in one predictable structure, which is exactly what a repository
needs.
Get the whole team on disk
Section titled “Get the whole team on disk”Install the CLI and clone your development team:
$ pnpm install -g @factorialco/fcode-cli$ fcode login$ fcode team:cloneYou get one folder per App, each holding a checkout of that App’s development workspace, and the
global workspaces the team owns under global-workspaces/:
📦 acme-payroll┣ 📂 .fcode┃ ┗ 📜 team.json # the team, the Apps and the global workspaces cloned into it┣ 📂 .claude┃ ┗ 📂 skills # agent skills, installed once for the whole team┣ 📂 global-workspaces┃ ┗ 📂 acme-base # a global workspace the team owns: processes, modules, variables…┣ 📂 payroll-sync┃ ┣ 📜 settings.json # the App — name, description, id┃ ┣ 📜 README.md # the App's README┃ ┣ 📜 MARKETPLACE.md # its marketplace listing┃ ┣ 📜 CHANGELOG.md # its releases, generated┃ ┣ 📂 marketplace┃ ┃ ┗ 🖼️ logo.png # its logo┃ ┗ 📂 app # the App's workspace: processes, modules, variables…┗ 📂 time-off-sync ┗ ...Each App folder is a small repository of its own around the workspace: a README.md for the people
who own and operate the App, the marketplace listing as MARKETPLACE.md, a CHANGELOG.md generated
from the App’s releases, and the logo. That is what an internal integration catalogue or a handover
needs, and it lives next to the code it describes.
Create the repository
Section titled “Create the repository”One repository for the whole team folder is the simplest thing that works: a single clone gives a new developer every App, one CI configuration covers them all, and a change touching two Apps is one pull request.
$ cd acme-payroll$ git init$ git add .$ git commit -m "Import Acme Payroll apps from Factorial Code"$ git push -u origin mainAdd a .gitignore at the team root first — the agent skills are a local install, not source:
.claude/skills.agentsskills-lock.jsonWhat each App’s workspace already excludes for you (its .gitignore is written by the CLI):
variables.local.env, datastore.json, .env, storage/*, and every inherited file, which the
parent workspace owns and fcode pull regenerates.
Keep .fcode/ in the repository. It records which cloud workspace each folder points at, so a
teammate who clones the repository gets folders fcode already recognises — fcode pull in an App’s
app folder just works, with no second setup step. The cost is that its per-resource sync hashes can
conflict when two people push around the same time; resolve by taking either side and running
fcode team:pull, which rewrites them from the cloud.
Staying in sync
Section titled “Staying in sync”The cloud is the source of truth for what actually runs. A process executes the version stored in its workspace, not the one on your branch — merging a pull request changes nothing in Factorial Code until someone pushes. Treat the repository as history, review and backup, and keep one habit: pull before you work, push when you are done.
$ fcode team:pull # from the team root: clone new Apps, pull every workspace$ fcode team:status # what differs from the cloud, App by App$ git diff # …and what that changed in the repositoryfcode team:pull also picks up Apps created since your last run and refreshes each App’s
settings.json, CHANGELOG.md and logo. An App removed from the team is reported but never
deleted — the folder stays until you remove it yourself.
Pushing is deliberately per App. There is no team:push: you run fcode push from inside the App’s
app folder, so a deploy is always one App at a time.
$ cd payroll-sync/app$ fcode pushThe App’s README.md and MARKETPLACE.md are the one thing you edit in the repository and push
up as documentation rather than code. They are also editable in the dashboard, so a pull never
overwrites a local edit you have not pushed — it keeps the file and tells you. From the App folder:
$ cd payroll-sync$ fcode app:push # uploads README.md and MARKETPLACE.md if they changedSee Push an App’s README and marketplace listing
for the MARKETPLACE.md format and how conflicts are resolved.
Promote to production from GitHub Actions
Section titled “Promote to production from GitHub Actions”Automating a push to the dev workspace is rarely what you want: dev is where you and the console
edit interactively, and a job pushing on every merge would fight it. The push worth automating is the
promotion to production, which is exactly the fcode push the release flow leaves for a human to
run.
Two things have to be true before a job can do it:
- The release exists. Requesting a release in the console is what creates the
prod-{app}workspace, snapshots dev as a workspace version carrying that release, and grants write access to the qualifying members of the owning team. Until then there is nothing to push to. - The identity is authorized. Only operators and the App’s qualifying team members may write to a prod workspace. Create the API credential in the prod workspace — someone who cannot promote by hand cannot create one either — and store its Client ID and Secret as repository secrets.
The checkout points at the dev workspace it was cloned from, so the job has to re-point it before pushing, or it would promote onto dev. The prod slug is per App, so keep it in a repository variable:
name: Promote to production
on: push: branches: [main]
jobs: promote: runs-on: ubuntu-latest defaults: run: working-directory: payroll-sync/app steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "22.23" - run: npm install -g @factorialco/fcode-cli - run: fcode login -c "$CLIENT_ID" -s "$CLIENT_SECRET" env: CLIENT_ID: ${{ secrets.FCODE_CLIENT_ID }} CLIENT_SECRET: ${{ secrets.FCODE_CLIENT_SECRET }} # Without this the push would land on the dev workspace the folder was cloned from. - run: fcode remote:set "$PROD_WORKSPACE" env: PROD_WORKSPACE: ${{ vars.FCODE_PROD_WORKSPACE }} # e.g. prod-b5hiralj5hpupls4otok6vxah - run: fcode add - run: fcode pushfcode add registers the resources against the newly targeted remote, which starts out tracking
nothing — the same sequence the promotion in the CLI page
performs by hand.
Three things to know before you rely on this:
- Everything is per App. The prod slug, the API credential and the
working-directoryall belong to one App, so a repository holding several needs one job (or matrix entry) each. - Prod code is meant to be read-only. This automates the promotion; it does not make the repository authoritative over dev. Changes still start in the dev workspace and reach production through a release.
- A diverged prod fails the job, which is what you want: it stops rather than discarding
something promoted another way. Resolve it by pulling prod into the branch rather than reaching for
--force.
Publishing the workspace version and moving the stable alias stays a separate, deliberate step —
see workspace versioning.