# Migrate from Forgejo

Codebahn is built on Forgejo, so this is the closest migration. Code, issues, pull requests, labels, milestones, and releases carry over through the web UI or the [`codebahn` CLI](/docs/cli/). Your workflow files come across with your git history and run on Codebahn's hosted EU runners.

## Prerequisites

- A Codebahn account with an organization (see [getting started](/docs/getting-started/))
- An access token on your source Forgejo or Gitea instance (see below)
- Git installed locally

## Create an access token

On your source instance, go to **Settings** > **Applications** and generate a token with:

| Permission | Level |
|---|---|
| repository | Read |
| issue | Read |

The `repository` scope covers files, pull requests, and releases. The `issue` scope covers issues, labels, and milestones. You need both for a full import. On Forgejo v15.0+, you can scope the token to specific repositories. Copy the token value; it is shown once.

:::note
Gitea token creation is the same, except repository-scoped tokens are not available. A Gitea token can see every repository your account can.
:::

## Import via the web UI

1. In Codebahn, click **+** > **Import repository** > **Forgejo**

2. In **Clone URL**, paste the HTTPS URL of your repository (`https://your-instance.org/your-org/your-repo.git`)

3. Paste your access token

4. Select what to import: issues, pull requests, labels, milestones, releases

5. Choose your org as the owner, confirm the name, and click **Migrate repository**

This is a one-time import. The Codebahn repository is independent of your source instance afterward. The import does not carry over teams or permissions; re-create access control on the Codebahn side. For the field-by-field migration options, see the [upstream Forgejo docs](https://forgejo.org/docs/latest/).

Point your local clone at Codebahn:

```bash
git remote set-url origin git@codebahn.net:your-org/your-repo.git
```

## Import with the CLI

The [`codebahn` CLI](/docs/cli/) can import from any Forgejo or Gitea instance. [Install the CLI](/docs/cli/#install) first. Pass the access token with `--source-token`.

### Single repo

```bash
codebahn repo import your-instance.org/your-org/your-repo --source-token $FORGEJO_TOKEN
```

By default, the CLI imports issues, pull requests, labels, milestones, and releases. To skip specific metadata:

```bash
codebahn repo import your-instance.org/your-org/your-repo --source-token $FORGEJO_TOKEN --no-issues --no-prs
```

### Entire organization

```bash
codebahn repo import your-instance.org/your-org --owner my-codebahn-org --source-token $FORGEJO_TOKEN -y
```

Use `--include` and `--exclude` with glob patterns to select repos.

## CI workflows

Your workflows carry over with your git history. Common build, test, and lint workflows typically need no changes. Codebahn scans `.forgejo/workflows/`, `.github/workflows/`, `.codebahn/workflows/`, and `.gitea/workflows/`; see [Workflow directories](/docs/workflow-directories/) for precedence.

Hosted runners answer to `ubuntu-latest` (alias for `codebahn-small`), `codebahn-medium`, and `codebahn-large`. If a workflow targets a custom runner label, register a [BYO runner](/docs/ci-runners/) with that label or change `runs-on`.

Codebahn cancels in-progress runs when you push a new commit to the same branch (this is the default, unlike GitHub). To let every run finish, override it:

```yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false
```

See [Set up CI](/docs/set-up-ci/) for runner specs, minute counting, and the toolchains pre-cached in the job image.

## Migrate an organization

**CLI:** import an entire org in one command. See [Import with the CLI](#import-with-the-cli) above.

**Web UI:** run one migration per repository with the org as the owner.

**API:** script it against the [migration API](https://forgejo.org/docs/latest/developer/api-usage/).

Invite your team to the org with a [team invite](/docs/getting-started/) after the repos are in.

## Troubleshooting

**"Authentication failed" or 401.** The token is expired or missing `repository` read. Generate a new one.

**"Not found" or 404.** The token can't access this repository. If you scoped it to specific repos, confirm this one is in the list.

**Issues or pull requests missing.** The token needs `issue` read. Create a token with both scopes and re-run the migration.

## Fallback: manual push

If the import does not fit your situation, clone and push by hand:

```bash
git clone --bare https://your-instance.org/your-org/your-repo.git
cd your-repo.git
git push --mirror git@codebahn.net:your-org/your-repo.git
```

This moves code and branches only. Issues, pull requests, and releases are not included.

## Leaving Codebahn later

You are never locked in. From **Org Settings > Export** you can pull a `tar.gz` of every repo plus its issues, pull requests, labels, milestones, and releases, any time. See [Export your data](/docs/export/) for what's in the archive and how to restore it.
