# Dependency management

Codebahn does not have a built-in Dependabot. If you are coming from GitHub, your `.github/dependabot.yml` file is ignored.

The standard alternative is [Renovate](https://docs.renovatebot.com/). It runs as a scheduled CI workflow on Codebahn's runners, scans your repo for outdated dependencies, and opens pull requests with the updates. This guide walks through the setup.

## Prerequisites

- A repository on Codebahn with CI set up ([Set up CI](/docs/set-up-ci/))
- A Crew plan with CI minutes, or Solo with a top-up pack

## Enable the dependency bot

Codebahn provides a built-in bot account (`@codebahn-bot`) for automated dependency updates. Add it as a collaborator on your repository and Codebahn handles the rest: a short-lived, repo-scoped token is generated for each CI run and injected as `CODEBAHN_BOT_TOKEN`. No manual token creation or secret management.

1. Open your repository on Codebahn

2. Go to **Settings > Collaborators**

3. Search for `codebahn-bot` and add it with **Write** access

Pull requests from Renovate will appear as authored by `@codebahn-bot`. CI runs on those PRs automatically.

:::caution[Collaborator, not org member]
Add the bot as a **repository collaborator**, not as an organization member. As a collaborator, the bot can only access the specific repository. As an org member, it would gain visibility into all repos in the organization.
:::

## Add the workflow

Create `.codebahn/workflows/renovate.yml`:

```yaml
name: Renovate

on:
  schedule:
    - cron: '0 6 * * *'
  workflow_dispatch:

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: renovatebot/github-action@v46.1.17
        with:
          token: ${{ secrets.CODEBAHN_BOT_TOKEN }}
        env:
          RENOVATE_PLATFORM: forgejo
          RENOVATE_ENDPOINT: https://codebahn.net/api/v1
          RENOVATE_REPOSITORIES: ${{ github.repository }}
          LOG_LEVEL: info
```

What each piece does:

- **`cron: '0 6 * * *'`** runs Renovate daily at 06:00 UTC. This controls how often Renovate _checks_ for updates. When it creates PRs is a separate setting (see [scheduling](#scheduling)).
- **`workflow_dispatch`** lets you trigger a run manually from the Actions tab.
- **`RENOVATE_PLATFORM: forgejo`** tells Renovate it is talking to a Forgejo instance. Do not use `gitea`.
- **`RENOVATE_ENDPOINT`** must include `/api/v1`.
- **`RENOVATE_REPOSITORIES`** tells Renovate which repo to scan. On GitHub this is auto-detected; on Forgejo you must set it explicitly. `${{ github.repository }}` resolves to `owner/repo`.
- **`LOG_LEVEL: info`** keeps output readable. Use `debug` during initial setup if something is not working.

The action resolves from GitHub automatically. `ubuntu-latest` routes to Codebahn's hosted runners. No special configuration needed.

:::note
Codebahn runners allow outbound HTTPS only. Renovate fetches package metadata from npm, PyPI, Maven Central, and the Go module proxy over HTTPS, so this works without changes.
:::

## Add a Renovate config

Create `renovate.json` in your repository root:

```json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "major": {
    "enabled": false
  }
}
```

`config:recommended` enables the dependency dashboard (an issue Renovate maintains with the status of all updates), semantic commit prefixes, and monorepo-aware grouping. `major` is disabled because major version bumps often have breaking changes and should be done by hand after reading the changelog. Renovate still creates PRs for minor and patch updates.

A more opinionated config that also auto-merges patch and minor updates:

```json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:best-practices"],
  "major": {
    "enabled": false
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
      "automerge": true
    }
  ]
}
```

Push both files and trigger a run from the Actions tab, or wait for the next cron schedule. Renovate scans your repo and opens its first pull requests. The `CODEBAHN_BOT_TOKEN` secret is injected automatically; you do not need to create it.

## Migrating from Dependabot

Renovate auto-detects all package managers. You do not list ecosystems. Instead, you use `packageRules` to customize behavior per manager.

### Field mapping

| Dependabot | Renovate |
|---|---|
| `package-ecosystem: "npm"` | Auto-detected. Scope with `"matchManagers": ["npm"]` in `packageRules` |
| `package-ecosystem: "github-actions"` | Auto-detected. Manager name: `"github-actions"` |
| `package-ecosystem: "docker"` | Auto-detected. Renovate splits Docker into `"dockerfile"`, `"docker-compose"`, and others. Use `"matchManagers": ["dockerfile"]` to scope to Dockerfiles only |
| `package-ecosystem: "gomod"` | Auto-detected. Manager name: `"gomod"` |
| `schedule.interval: "daily"` | `"schedule": ["before 5am"]` |
| `schedule.interval: "weekly"` | `"schedule": ["before 5am on Monday"]` |
| `schedule.interval: "monthly"` | `"schedule": ["before 5am on the first day of the month"]` |
| `directory: "/"` | Auto-scanned. Scope with `"matchFileNames"` in `packageRules` |
| `open-pull-requests-limit: 10` | `"prConcurrentLimit": 10` |
| `labels: ["dependencies"]` | `"labels": ["dependencies"]` |
| `reviewers: ["user1"]` | `"reviewers": ["user1"]` |
| `groups.name.patterns` | `"matchPackageNames"` with regex (e.g. `"/^@types/"`) + `"groupName"` in `packageRules` |
| `ignore` with `update-types` | `"matchPackageNames"` + `"matchUpdateTypes"` + `"enabled": false` in `packageRules` |
| `versioning-strategy: increase` | `"rangeStrategy": "bump"` |
| `versioning-strategy: lockfile-only` | `"rangeStrategy": "update-lockfile"` |

Renovate uses a natural-language schedule syntax, not cron. The schedule in `renovate.json` controls _when PRs are created_, separate from the workflow cron that controls _when Renovate checks_.

### Worked example

**Before** (`.github/dependabot.yml`):

```yaml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    open-pull-requests-limit: 10
    labels:
      - "dependencies"
    groups:
      types-packages:
        patterns:
          - "@types/*"
    ignore:
      - dependency-name: "react"
        update-types: ["version-update:semver-major"]

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "ci"
```

**After** (`renovate.json`):

```json
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "schedule": ["before 5am on Monday"],
  "prConcurrentLimit": 10,
  "labels": ["dependencies"],
  "packageRules": [
    {
      "matchManagers": ["npm"],
      "matchPackageNames": ["/^@types/"],
      "groupName": "type definitions"
    },
    {
      "matchManagers": ["npm"],
      "matchPackageNames": ["react"],
      "matchUpdateTypes": ["major"],
      "enabled": false
    },
    {
      "matchManagers": ["github-actions"],
      "labels": ["ci"],
      "pinDigests": true
    }
  ]
}
```

The npm and GitHub Actions ecosystems are auto-detected. `packageRules` entries scope behavior to each manager. The `@types/*` group and the React major ignore carry over directly.

Delete `.github/dependabot.yml` after you confirm Renovate is working. Keeping both creates duplicate PRs.

## Scheduling

Two layers control timing:

1. **Workflow cron** (`.codebahn/workflows/renovate.yml`): how often Renovate runs and checks for updates. Default in the example above: once a week (Monday at 06:00 UTC).
2. **Renovate schedule** (`renovate.json`): when Renovate is allowed to _create_ pull requests. If the schedule window is closed, Renovate still runs but holds new PRs until the window opens.

If you only want updates on Monday mornings:

```json
{
  "schedule": ["before 9am on Monday"],
  "timezone": "Europe/Stockholm"
}
```

Without a `schedule` key, Renovate creates PRs whenever it finds updates, on every cron run.

## Optional: GitHub token for changelogs

Most open-source dependencies live on GitHub. When Renovate fetches changelogs and release notes for PRs, it hits the GitHub API. Without authentication, GitHub rate-limits these requests and PR descriptions may be missing release notes.

Add a GitHub personal access token (read-only, no scopes needed for public repos) as a secret:

1. On GitHub: **Settings > Developer settings > Personal access tokens > Fine-grained tokens**

2. Generate a token with no repository access (public data only)

3. On Codebahn: add it as a repository secret named `GH_COM_TOKEN`

Forgejo blocks secret names starting with `GITHUB_`, so the secret is named `GH_COM_TOKEN`. Map it to the env var Renovate expects in the workflow:

```yaml
        env:
          RENOVATE_PLATFORM: forgejo
          RENOVATE_ENDPOINT: https://codebahn.net/api/v1
          GITHUB_COM_TOKEN: ${{ secrets.GH_COM_TOKEN }}
          LOG_LEVEL: info
```

The env var Renovate reads is `GITHUB_COM_TOKEN`, not `RENOVATE_GITHUB_COM_TOKEN`. The secret name (`GH_COM_TOKEN`) can be anything that Forgejo allows.

## Multiple repos

The setup above is per-repo: each repository gets its own workflow file and `renovate.json`, and `@codebahn-bot` is added as a collaborator on each one. The `CODEBAHN_BOT_TOKEN` is scoped to whichever repo the workflow runs in, so each repo is isolated.

For a team with many repos, Renovate also supports autodiscover mode: a single workflow in one "renovate-config" repo scans every repo the bot can access. Set `RENOVATE_AUTODISCOVER: 'true'` in the workflow env and use `autodiscoverFilter` to limit which repos are scanned. See the [Renovate documentation on autodiscovery](https://docs.renovatebot.com/self-hosted-configuration/#autodiscover) for the details.
