Metadata-Version: 2.5
Name: depdelta-cli
Version: 0.4.0
Summary: DepDelta for CI: review a change's dependency updates on Gerrit, GitLab, Forgejo or Gitea.
Project-URL: Homepage, https://depdelta.com
Project-URL: Documentation, https://depdelta.com/ci
Project-URL: Source, https://github.com/mbobiosio/depdelta-cli
Project-URL: Issues, https://github.com/mbobiosio/depdelta-cli/issues
Author: Mbuodile Obiosio
License-Expression: MIT
License-File: LICENSE
Keywords: breaking-changes,ci,codeberg,dependencies,forgejo,gerrit,gitea,gitlab,renovate
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# depdelta-cli

[![PyPI](https://img.shields.io/pypi/v/depdelta-cli)](https://pypi.org/project/depdelta-cli/)
[![CI](https://github.com/mbobiosio/depdelta-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/mbobiosio/depdelta-cli/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)

Know what a dependency update breaks before you merge, on Gerrit, GitLab,
Forgejo and Gitea (Codeberg included), Bitbucket Cloud and Azure DevOps.

[DepDelta](https://depdelta.com) reads every release's notes, security
advisories and published API between two versions of a package, and says
what changed and what will break: see
[eslint 8.57.0 → 9.0.0](https://depdelta.com/npm/eslint/compare/8.57.0...9.0.0)
for an example. `depdelta` brings that review to your CI. On every change that
updates a dependency (from Renovate or anyone) it posts the breaking changes,
security fixes, removed APIs and where your code uses them, right on the
change.

On GitHub, the [DepDelta GitHub app](https://depdelta.com/github-app) does
this with no CI job. Setup guide, example review and FAQ:
**[depdelta.com/ci](https://depdelta.com/ci)**.

## How it works

On every change that touches a manifest or lockfile, `depdelta`:

1. sends the changed manifests and lockfiles (both ends) to DepDelta. Source
   code is never sent;
2. gets back each update's verdict and, for npm, Maven, PyPI, Go, Composer,
   NuGet, pub and RubyGems, the declarations it removes or changes;
3. looks for those declarations in the checkout, and sends only the hits
   (path, line, symbol);
4. posts the review: a Gerrit change message (no vote), or one GitLab merge
   request note, Forgejo/Gitea or Bitbucket pull request comment, or Azure
   DevOps pull request thread that later runs edit in place. Each update
   links to its full comparison on depdelta.com.

Beyond each update, the review says what the change does to your lockfiles:
packages before and after, new and removed direct dependencies (dev or
production, and what each pulls in), and packages that newly run code at
install time (an npm install script, a Python package built from source, a
Composer plugin, a Cargo build script).

Nine ecosystems: npm, PyPI, Maven, Composer, Cargo, RubyGems, pub.dev,
NuGet and Go. For Android and other Gradle builds that means
`build.gradle(.kts)`, `settings.gradle(.kts)`, version catalogs
(`gradle/libs.versions.toml`, libraries and plugins) and
`gradle.lockfile`; plugins count as updates too (AGP, Kotlin, KSP, Hilt).
Needs Python 3.11+ and git, nothing else.

```
docker pull ghcr.io/mbobiosio/depdelta-cli:latest
# or
pip install depdelta-cli
```

## API key

Sign in at [depdelta.com](https://depdelta.com) with GitHub or Google,
make a key in [Settings → API keys for CI](https://depdelta.com/settings),
and store it in your CI's secrets as `DEPDELTA_API_KEY`. Each key gets 240
reviews an hour, waiting on release notes included.

## Jenkins with Gerrit Trigger

The job runs on `patchset-created`. Gerrit Trigger sets `GERRIT_PROJECT`,
`GERRIT_CHANGE_NUMBER`, `GERRIT_PATCHSET_REVISION` and `GERRIT_CHANGE_URL`.
The review is posted with a Gerrit account's HTTP password: a service
account works best.

The checkout needs the patch set's parent: don't clone with depth 1 (or let
`depdelta` fetch it; it does when the parent is missing).

```groovy
pipeline {
  agent { docker { image 'ghcr.io/mbobiosio/depdelta-cli:latest' } }
  stages {
    stage('DepDelta') {
      steps {
        withCredentials([
          string(credentialsId: 'depdelta-api-key', variable: 'DEPDELTA_API_KEY'),
          usernamePassword(credentialsId: 'gerrit-http',
                           usernameVariable: 'GERRIT_HTTP_USER',
                           passwordVariable: 'GERRIT_HTTP_PASSWORD'),
        ]) {
          // A DepDelta outage shouldn't fail the build.
          catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') {
            sh 'depdelta'
          }
        }
      }
    }
  }
}
```

Without Docker: `pip install depdelta-cli` on the agent, then the same
`sh 'depdelta'`.

`DEPDELTA_GERRIT_URL` overrides Gerrit's address when `GERRIT_CHANGE_URL`
isn't the REST root (a proxy, a different host name for agents).

Messages carry the tag `autogenerated:depdelta`, so Gerrit folds older patch
sets' reviews away, and notify only the change owner.

## GitLab CI

Runs in merge request pipelines. `CI_JOB_TOKEN` can't write notes: give the
job a project access token with the `api` scope (Reporter role is enough) as
`GITLAB_TOKEN`.

```yaml
depdelta:
  image: ghcr.io/mbobiosio/depdelta-cli:latest
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      changes:
        - "**/package.json"
        - "**/package-lock.json"
        - "**/pom.xml"
        - "**/requirements*.txt"
        - "**/pyproject.toml"
        - "**/*.lock"
        - "**/go.mod"
  variables:
    GIT_DEPTH: 0
  script:
    - depdelta
  allow_failure: true
```

Uses link to the files at the merge request's head commit.

## Forgejo and Gitea

Codeberg, self-hosted Forgejo and Gitea share one API, so one setup covers
them: Forgejo Actions, Gitea Actions, or Woodpecker CI. The review is one
pull request comment that later runs edit in place. The token comes from
`FORGEJO_TOKEN`, else `GITEA_TOKEN`, else `GITHUB_TOKEN`.

### Forgejo Actions and Gitea Actions

The workflow's own token can comment on pull requests from branches of the
same repository. On a pull request from a fork it is read-only (and secrets
are not passed), so the review can't be posted there.

```yaml
# .forgejo/workflows/depdelta.yml (Gitea: .gitea/workflows/depdelta.yml)
on: pull_request

jobs:
  depdelta:
    runs-on: docker
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: DepDelta
        # A DepDelta outage shouldn't fail the pull request.
        continue-on-error: true
        env:
          DEPDELTA_API_KEY: ${{ secrets.DEPDELTA_API_KEY }}
          FORGEJO_TOKEN: ${{ github.token }}
        run: |
          curl -LsSf https://astral.sh/uv/install.sh | sh
          ~/.local/bin/uvx --from depdelta-cli depdelta
```

The job needs Python 3.11+ and git; the lines above fetch Python with
[uv](https://docs.astral.sh/uv/). With Python on the runner,
`pip install depdelta-cli && depdelta` does the same. A pull request that
touches no manifests or lockfiles ends at once with nothing to review.

### Woodpecker CI

For a Forgejo or Gitea forge (`CI_FORGE_TYPE` is `forgejo` or `gitea`).
Woodpecker has no token for the forge: make an access token with the
`write:issue` and `read:repository` scopes (a bot account works best) and
store it as a secret. Woodpecker hides secrets from pull requests until
you allow the `pull_request` event on each one.

```yaml
# .woodpecker/depdelta.yaml
when:
  - event: pull_request

steps:
  - name: depdelta
    image: ghcr.io/mbobiosio/depdelta-cli:latest
    commands:
      - depdelta
    environment:
      DEPDELTA_API_KEY:
        from_secret: depdelta_api_key
      FORGEJO_TOKEN:
        from_secret: forgejo_token
    failure: ignore
```

Woodpecker names only the target branch, so `depdelta` asks the forge for
the pull request's merge base, and fetches it when the clone is shallow.
Uses link to the files at the pull request's head commit.

## Bitbucket Pipelines

Bitbucket Cloud, in a pull request pipeline. Pipelines has no token that can
comment: make a repository access token with the `pullrequest:write` scope
(Repository settings → Access tokens) and store it as a secured repository
variable `BITBUCKET_ACCESS_TOKEN`, next to `DEPDELTA_API_KEY`. An Atlassian
API token works too: set `BITBUCKET_EMAIL` and `BITBUCKET_API_TOKEN`.

```yaml
# bitbucket-pipelines.yml
pipelines:
  pull-requests:
    "**":
      - step:
          name: DepDelta
          image: ghcr.io/mbobiosio/depdelta-cli:latest
          clone:
            depth: full
          script:
            # A DepDelta outage shouldn't fail the pull request.
            - depdelta || true
```

Bitbucket shows HTML as text, so the review comes as plain Markdown, one
section per update. Bitbucket names only the destination's tip, so
`depdelta` asks Bitbucket for the pull request's merge base. Uses link to
the lines at the pull request's head commit. To fail the build with
`--fail-on`, drop the `|| true`.

## Azure Pipelines

Azure Repos pull requests, run as build validation: add the pipeline to the
target branch's policies (Branch policies → Build validation). Give the
build service permission to comment: Project settings → Repositories → your
repository → Security → "*Project* Build Service" → Contribute to pull
requests: Allow. The job's token reaches `depdelta` only when mapped into
the step's environment:

```yaml
# azure-pipelines.yml
trigger: none

pool:
  vmImage: ubuntu-latest

steps:
  - checkout: self
    fetchDepth: 0
  - script: pipx run --spec depdelta-cli depdelta
    displayName: DepDelta
    # A DepDelta outage shouldn't fail the pull request.
    continueOnError: true
    env:
      DEPDELTA_API_KEY: $(DEPDELTA_API_KEY)
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)
```

A personal access token with Code: Read & write works too, as
`AZURE_DEVOPS_TOKEN`. The review is one pull request thread that later
builds edit in place; it starts active, so it can be resolved like any
comment. The build checks out a merge commit, so `depdelta` asks Azure
DevOps where the pull request starts. Repositories on GitHub built in Azure
Pipelines aren't covered: use the GitHub app.

## Options

```
depdelta [--repo .] [--base REV --head REV] [--print]
         [--format markdown|commonmark|text] [--wait 300]
         [--fail-on never|breaking|review|unknown|safe]
         [--api-url URL]
```

- `--print` prints the review instead of posting it. With `--base` and
  `--head` (outside CI) it always prints:
  `depdelta --base origin/main --head HEAD`.
- `--wait` is how long to wait for DepDelta to read release notes it hasn't
  seen yet. After that the review posts as it stands, saying what is still
  being read.
- `--fail-on breaking` exits 1 when an update is breaking, to fail the job.

Exit status: 0 posted (or nothing to review), 1 the `--fail-on` verdict was
met or `.depdelta.yml` blocks new install-time code, 2 it couldn't review or
post.

## .depdelta.yml

An optional file at the repository root, read at the change's head commit.
The GitHub app reads the same file.

```yaml
install_scripts: warn        # off | warn (default) | block
allow_install_scripts:       # never flagged
  - esbuild
  - sharp
```

- `warn` flags each package that newly runs code at install.
- `block` flags them too, and `depdelta` exits 1, so the job fails until the
  package is allowed or removed.
- `off` leaves install-time code out of the review.

`depdelta` sends the file as it is; DepDelta reads it. A file it can't read
falls back to the defaults, and the review says why.

## Privacy

Only the changed manifests and lockfiles leave CI, plus, for updates with
removed or changed APIs, the path, line and symbol of each use found. The
source code stays in CI. Forge credentials (Gerrit, GitLab, Forgejo, Gitea,
Bitbucket, Azure DevOps) are sent only to that forge. The [privacy policy](https://depdelta.com/privacy) has the
details.

## Links

- Website: [depdelta.com](https://depdelta.com)
- CI setup guide: [depdelta.com/ci](https://depdelta.com/ci)
- GitHub app: [depdelta.com/github-app](https://depdelta.com/github-app)
- Check a whole project's dependencies: [depdelta.com/report](https://depdelta.com/report)
- Issues: [github.com/mbobiosio/depdelta-cli/issues](https://github.com/mbobiosio/depdelta-cli/issues)

## Development

```
uv sync
make check format test
```

`src/depdelta_cli/usage.py` is the same usage matcher the DepDelta service
and GitHub app run, and `manifests.py` uses the same manifest names: changes
to either start in the service, then are copied here.

Releases: bump `version` in `pyproject.toml`, then tag `v<version>` and
push the tag. GitHub Actions publishes to PyPI and GHCR.

## License

MIT
