Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: CI

# One workflow, one job graph:
#
# check ──→ image (main, release tags and manual runs: publish to GHCR)
#
# A pull request runs check alone. The image is built and published only once
# the code on main, or a release tag, has passed every check. A manual run
# publishes the chosen branch's image too, but only main moves `latest`.
on:
pull_request:
# Run by hand from the Actions tab, on any branch or tag.
workflow_dispatch:
push:
branches: [main]
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
- "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
# To publish the image to this repository's GHCR package.
packages: write

jobs:
# Every gate pre-commit runs, again, since hooks can be skipped.
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true

# --locked fails if uv.lock disagrees with pyproject.toml, so a dependency
# added without its lockfile fails here rather than resolving afresh.
- name: Install
run: uv sync --locked

- name: Lint
run: uv run ruff check .

- name: Check formatting
run: uv run ruff format --check .

- name: Type check
run: uv run pyright

- name: Dead code
run: uv run vulture

# The e2e tier needs a running DSS; the `e2e` marker keeps it out of the
# default run (pyproject.toml). Coverage is printed, not gated.
- name: Test
run: uv run pytest

# Not on pull requests: one has already had every check, and an image it will
# never publish would cost a build on every push.
image:
if: github.event_name != 'pull_request'
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# GHCR names must be lowercase; the owner is "OpenAgriNet".
- name: Set image name
run: echo "IMAGE=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"

# `main` or the release tag, with any slash made safe for a tag.
- name: Set image tag
run: echo "TAG=$(echo '${{ github.ref_name }}' | tr '/' '-')" >> "$GITHUB_ENV"

# The ref (main, v1.2.0, a branch run by hand) and the exact commit. Only
# main also moves `latest`, so a manual run from a branch cannot replace
# what a deployment pulls.
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ env.IMAGE }}:${{ env.TAG }}
${{ env.IMAGE }}:${{ github.sha }}
${{ github.ref == 'refs/heads/main' && format('{0}:latest', env.IMAGE) || '' }}
# Links the package to this repository on GitHub, so it appears on the
# repo page and inherits its access.
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
# Reuse layers from earlier runs, so only what changed is rebuilt.
cache-from: type=gha
cache-to: type=gha,mode=max
41 changes: 0 additions & 41 deletions .github/workflows/run-tests.yml

This file was deleted.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes to this project are recorded here, in the format of
## [Unreleased]

### Added
- CI publishes the image to `ghcr.io/openagrinet/experience-api` from main and release tags.
- `EXPERIENCE_API_DSS_MODE=http` calls the real DSS: the §7 request, its event stream read leniently.
- A Dockerfile and a compose file that run the API with a healthcheck.
- `POST /v1/chat`: SSE or JSON by `Accept`, answered by a fake DSS for now.
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ Same port, same fake DSS. `EXPERIENCE_API_DSS_MODE=http docker compose up
too. Set `EXPERIENCE_API_HOST_PORT` to publish on another port. The container
reports healthy once `/healthz` answers.

### The published image

CI publishes an image on every push to `main` and every release tag, once
every check passes. It can also be run by hand from the Actions tab, for any
branch; only `main` moves `latest`.

```bash
docker pull ghcr.io/openagrinet/experience-api:latest # or :main, :v1.2.0, :<commit>
```

### Try a chat turn

```bash
Expand Down
Loading