Skip to main content
EXAMPLE

Native CI on Gitea Actions

Run the recommended Atmos CI workflows on Gitea Actions on your laptop: no GitHub account, no hosted runner, and no cloud credentials. A local Gitea server and a Gitea runner run the same plan,…

AutomationEmulators
cloudposse / atmos

Try it out

Copy the example into a new directory and make it your own:

atmos init examples/gitea-actions
cd gitea-actions

Follow the README for prerequisites and next steps.

README.md5.5 KB
View on GitHub

Example: Native CI on Gitea Actions

Run the recommended Atmos CI workflows on Gitea Actions on your laptop: no GitHub account, no hosted runner, and no cloud credentials. A local Gitea server and a Gitea runner run the same plan, apply, deploy-affected, and deploy-all workflows that the GitHub Actions guides describe, adapted for Gitea.

git push ──▶ Gitea (emulator) ──job──▶ Gitea runner ──docker──▶ job container (Atmos)
                  ▲                                                     │
                  └────── commit statuses + pull request comments ◀─────┘

The directory has two halves:

  • The harness (atmos.yaml, stacks/, components/container/) runs on your machine. It starts a Gitea server emulator with Actions enabled, and a gitea/runner container that registers with it.
  • The project (repo/) is what you push to Gitea. It has its own atmos.yaml with native CI enabled, a provider-free Terraform component, dev and prod stacks, and the workflows in repo/.gitea/workflows/.

Try It

Prerequisites: Docker. The runner mounts /var/run/docker.sock to start job containers, which gives it root-equivalent access to your Docker daemon, so only run this locally.

cd examples/gitea-actions

# Start Gitea (http://localhost:3300, login atmos / atmos) and the runner.
atmos gitea up

Create the repository, a token for Atmos, and the variables the workflows read:

API=http://atmos:atmos@localhost:3300/api/v1
curl -X POST "$API/user/repos" -H 'Content-Type: application/json' \
-d '{"name":"infra","default_branch":"main"}'

# A scoped token Atmos uses for commit statuses and pull request comments.
TOKEN=$(curl -s -X POST "$API/users/atmos/tokens" -H 'Content-Type: application/json' \
-d '{"name":"atmos-ci","scopes":["write:repository","write:issue","read:user"]}' | jq -r .sha1)
curl -X PUT "$API/repos/atmos/infra/actions/secrets/ATMOS_CI_GITEA_TOKEN" \
-H 'Content-Type: application/json' -d "{\"data\":\"$TOKEN\"}"

# The Atmos image tag the jobs run. Use a release that includes the Gitea provider.
curl -X POST "$API/repos/atmos/infra/actions/variables/ATMOS_VERSION" \
-H 'Content-Type: application/json' -d '{"value":"<atmos version>"}'

Push the project. The push to main runs apply.yml:

HARNESS_DIR=$PWD
cp -R repo /tmp/infra && cd /tmp/infra
git init -b main && git add -A && git commit -m "Add infrastructure"
git push http://atmos:atmos@localhost:3300/atmos/infra.git main

Open a pull request that changes only dev to run plan.yml and deploy-affected.yml:

git checkout -b change-dev
sed -i.bak 's/Hello from dev/Hello again from dev/' stacks/deploy/dev.yaml && rm stacks/deploy/dev.yaml.bak
git commit -am "Change the dev message"
git push http://atmos:atmos@localhost:3300/atmos/infra.git change-dev
curl -X POST "$API/repos/atmos/infra/pulls" -H 'Content-Type: application/json' \
-d '{"title":"Change the dev message","head":"change-dev","base":"main"}'

Watch the runs at http://localhost:3300/atmos/infra/actions. The pull request gets atmos/plan/prod/demo and atmos/plan/dev/demo commit statuses and an Atmos plan comment. deploy-affected.yml plans only dev, the instance the change affects. Run deploy-all.yml from the Actions tab to deploy every instance.

Tear it down:

cd "$HARNESS_DIR"
atmos gitea down

What Changes from GitHub Actions

GitHub ActionsGitea Actions
Workflow directory.github/workflows/.gitea/workflows/
Checkoutactions/checkoutatmos git clone --ci: the Atmos image has no Node.js for JavaScript actions
TokenGITHUB_TOKENATMOS_CI_GITEA_TOKEN secret; Atmos never sends GITHUB_TOKEN to Gitea
permissions:id-token, checks, statuses, pull-requestsRestrict the job token where supported; Atmos reports through commit statuses
ChecksCheck runsCommit statuses
Apply on mergeVerifies the plan reviewed in the pull requestPlans the merged commit fresh

How Jobs Reach Gitea

The emulator and the runner join the stack's shared network, atmos-local, where Gitea's alias is local-gitea. The runner starts each job container on the same network (container.network in runner.yaml), and Gitea advertises http://local-gitea:3000 as its URL, so jobs clone and call the API through it. From your machine, use the published port, http://localhost:3300.

The local server speaks plain HTTP, so runner.yaml sets ATMOS_CI_GITEA_ALLOW_INSECURE=true for jobs. Never set it against a real server.

Key Files

FilePurpose
atmos.yamlThe harness project and the atmos gitea up / atmos gitea down commands
stacks/deploy/local.yamlThe Gitea emulator and the runner container
components/container/gitea-runner/runner.yamlRunner configuration: job network, labels, and job environment
repo/atmos.yamlThe project's native CI configuration
repo/.gitea/workflows/Plan, apply, deploy-affected, and deploy-all workflows