CI/CD
Branch Management
---
title: Trunk-based with CD
---
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'main'}} }%%
gitGraph
commit tag: "0.37.0"
branch "feat/add-foo-123"
branch "feat/add-bar-123"
checkout "feat/add-foo-123"
commit id: "feat(foo): ..."
commit id: "doc(foo): ..."
checkout main
merge "feat/add-foo-123" tag: "v1.0.0.canary.1"
branch "feat/add-baz-123"
commit id: "feat(baz): ..."
commit id: "doc(baz): ..."
commit id: "test(baz): ..."
checkout "feat/add-bar-123"
commit id: "chore(bar): ..."
commit id: "feat(bar): ..."
checkout main
merge "feat/add-bar-123" tag: "v1.0.0.canary.2"
checkout main
branch "version/1.0.0"
commit id: "bump(v1.0.0): pyproject.toml"
checkout main
branch release
merge "version/1.0.0"
commit id: "release(v1.0.0): ..." tag: "v1.0.0"
checkout main
merge release
checkout main
merge "feat/add-baz-123" tag: "v1.1.0.canary.1"
Protected Branches
main(default), the trunkrelease
The trunk is considered stable and MUST:
- Reject commits not included in a Pull Request
- Reject commits from branches other than
releasethat have adiffon thepyproject.tomlversion bump - Require Docs and TestNet deployments to be healthy to accept commits from
release - Meet the quality criteria defined in the CI/CD pipeline
The release branch MUST:
- Be kept in sync with
main - Be used to generate release tags
- Reject commits from branches other than
mainthat are:- Not included in a Pull Request
- Have a
diffthat is not strictly equal to thepyproject.tomlversion bump.
The CI/CD pipeline ensures that:
-
The
mainbranch contains release-grade code at any time (both for Smart Contracts and Docs) -
The
releasebranch is synced withmainand used only to generate release tags (reflected aspyproject.tomlversion)
Unprotected Branches
Features, major refactoring, dependency bumps, or bugfixes SHALL be carried
out on a dedicated unprotected branch pointing to the trunk (main).
The pyproject.toml release version bumps SHALL be carried out on a dedicated
unprotected branch pointing to the release branch.
Draft Pull Requests from unprotected branches, either to main or release, SHOULD
skip the CI.
Deployments
The CD makes use of the following deployment environments:
-
preview: to host the static documentation (mdBook) -
contract-testnet: to continuously deploy Smart Contracts to the Algorand TestNet -
contract-mainnet: to deploy Smart Contracts to the Algorand MainNet on release
Implementation
The CI/CD pipeline is implemented with the following automated workflows:
-
Smart Contracts CI (tests, lint, output stability, mock deployment)
-
Smart Contracts CD (to TestNet)
-
xGov Registry committee publisher (to MainNet)
-
xGov Registry committee watchdog (MainNet freshness checks)
-
Documentation CI (tests, lint, preview)
-
Release CI (validate release tag, version, etc.)
-
Release (to MainNet)
And the following manually dispatchable workflows:
- Documentation preview for external contributions
- Documentation deployment (to https://docs.xgov.algorand.co/)
- xGov Registry parameters configuration
- xGov Registry RBAC management
- xGov Registry/Proposals pause and resume
- xGov Registry committee publisher
- Release and Update xGov Council
Committee Publishing
The xGov Registry committee publication flow is implemented as a dedicated CI/CD feature.
MainNet publisher
The automated MainNet publisher workflow:
- Fetches the xGov Registry global state.
- Reads
committee_last_anchorandgovernance_periodfrom the global state. - Computes the target anchor as the latest round aligned to the current
governance_period. - Fetches the committee entry from
COMMITTEE_INDEX_URL. - Publishes the next committee only when:
- the Registry is behind the target anchor
- the committee entry exists in the index
committeeId,totalMembers, andtotalVotesare valid
The public pre-check is implemented by .github/scripts/committee-precheck.sh and
uses only curl and jq. This allows the workflow to skip AlgoKit and Python dependency
installation when the Registry is already up to date.
When publication is required, the workflow invokes:
algokit project deploy mainnet xgov_registry
with:
XGOV_REG_DEPLOY_COMMAND=declare_committeeXGOV_REG_COMMITTEE_ID_B64XGOV_REG_COMMITTEE_TOTAL_MEMBERSXGOV_REG_COMMITTEE_TOTAL_VOTESXGOV_REG_EXPECTED_TARGET_ANCHOR
MainNet watchdog
The automated MainNet watchdog workflow uses the same public pre-check inputs but never installs AlgoKit and never sends transactions.
It raises an alert when:
target_anchor > committee_last_anchorlast_round >= target_anchor + 5000
The watchdog opens or updates a GitHub issue for the overdue anchor and closes it automatically after the Registry catches up.
TestNet integration
The TestNet committee workflow is intentionally manual only. It is used as an integration
test for the declare_committee deploy command.
The TestNet run:
- Reads rounds and Registry state from
ALGOD_API_BASE_TESTNET - Resolves
committeeIdfromCOMMITTEE_INDEX_URL - Uses manual workflow inputs for
committee_membersandcommittee_votes, or defaults them to30and9000000 - Calls:
algokit project deploy testnet xgov_registry
with XGOV_REG_DEPLOY_COMMAND=declare_committee
GitHub Variables
The committee publication workflows require the following GitHub variables:
COMMITTEE_INDEX_URLALGOD_API_BASE_MAINNETALGOD_API_BASE_TESTNETXGOV_REGISTRY_ID_MAINNETXGOV_REGISTRY_ID_TESTNET
Local Testing Guide
Deploy commands used in the dispatchable workflows (e.g., set_roles, pause_or_resume,
etc.) can be tested locally against LocalNet.
Start LocalNet
algokit localnet reset
Prepare a configured local Registry
Use the default LocalNet account as both the temporary admin and daemon so the Registry is configured and ready to accept committee declarations:
export TEST_ADMIN="$(poetry run python -c 'from algokit_utils import AlgorandClient; print(AlgorandClient.default_localnet().account.dispenser_from_environment().address)')"
export TEST_XGOV_DAEMON="$TEST_ADMIN"
XGOV_REG_DEPLOY_COMMAND=deploy \
XGOV_REG_SET_ROLES=true \
XGOV_REG_CONFIGURE=true \
algokit project deploy localnet xgov_registry \
-c "poetry run python -m smart_contracts deploy"
Run declare_committee locally
The following command uses mocked committee data and exercises the same deploy command used by the CI workflows:
XGOV_REG_DEPLOY_COMMAND=declare_committee \
XGOV_REG_COMMITTEE_ID_B64='AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=' \
XGOV_REG_COMMITTEE_TOTAL_MEMBERS='30' \
XGOV_REG_COMMITTEE_TOTAL_VOTES='9000000' \
XGOV_REG_EXPECTED_TARGET_ANCHOR='1' \
algokit project deploy localnet xgov_registry \
-c "poetry run python -m smart_contracts deploy"
Expected result:
- the command resolves the default LocalNet deployer
- it looks up the
XGovRegistryapp by creator and name - it submits
declare_committee - it logs
Committee successfully declared