# AI Coding Instructions for docker-image-controller

Config-driven container image sync tool. Pushes images from source registries to AWS ECR, Alibaba Cloud ACR, Azure ACR, and Harbor using `regsync-cloud`.

## Critical: `allow: []` Means Sync ALL Tags

An empty `allow` list is **not** "no tags" — `regsync` treats it as no filter, syncing every tag in the source registry. Always provide at least one explicit pattern.

```yaml
# WRONG — syncs every tag in the source registry
tags:
  allow: []
  deny:
    - ".*-dev$"

# CORRECT — omit tags entirely if the entry has no tag restrictions
# or provide explicit patterns:
tags:
  allow:
    - "v[0-9]+\\.[0-9]+\\.[0-9]+"
```

CI (`ci.yml`) blocks PRs that contain an explicit `allow: []` (empty array) in any `image-sync-*.yaml` file. Omitting `tags:` or `allow:` entirely is allowed (regsync syncs all tags, which is intentional for some entries).

## Config File Format

```yaml
repositorySets:
  commonRepositories:           # included in all ECR target registries
    - name: tidbcloud/pd        # image name in target registry
      source: docker.io/pingcap/pd
      tags:
        allow:
          - "v8\\.5\\.[0-9]+"  # regex; auto-anchored with ^ and $
        deny:
          - ".*-dev$"

  myNamedSet:                   # named set, opt-in per targetRegistry
    - name: third-party/foo
      source: docker.io/foo/bar
      tags:
        allow:
          - "1\\.2\\.3"

targetRegistries:
  - name: 380838443567.dkr.ecr.us-west-2.amazonaws.com
    type: ECR
    includeRepositorySets:
      - commonRepositories
      - myNamedSet
    repositories:               # registry-specific overrides
      - name: third-party/something
        source: docker.io/something/image
        tags:
          allow:
            - "v1\\.0\\.0"
```

- `name` convention: `tidbcloud/{name}` for self-built images, `third-party/{originalPath}` for third-party images.
- `repositorySets` is a `map[string][]repo`; referenced by `includeRepositorySets`.
- `tags.allow`/`deny` values are Go regexes, automatically anchored with `^` and `$`.

## Key Files

| File / Pattern | Purpose |
|---|---|
| `image-sync-prod.yaml` | Prod full sync (ECR 8 regions + ACR + Harbor) |
| `image-sync-dev.yaml` | Dev full sync |
| `image-sync-nextgen-{dev,prod}.yaml` | NextGen images; used by Fast Image Delivery |
| `image-sync-byoc-{dev,prod}.yaml` | BYOC AWS customer registries |
| `image-sync-byoc-azure-{dev,prod}.yaml` | BYOC Azure customer registries |
| `image-sync-{dev,prod}-harbor.yaml` | Harbor targets |
| `image-sync-serverless-dev.yaml` | Serverless dev |
| `image-sync-db9-dev.yaml` | DB9 dev |
| `byoc-targets-{dev,prod,azure-dev,azure-prod}.yaml` | BYOC customer registry target lists |
| `registries.yaml` | All target registry metadata (provider, region, roleArn, byoc flag) |
| `generate-sync-config.py` | Generates regsync YAML from image config + `registries.yaml` |
| `.github/workflows/ci.yml` | Lints YAML + blocks empty allow lists in all `image-sync-*.yaml` |
| `.github/scripts/check_repo_tag.py` | Tag safety checks (called from sync workflows) |

## Common Tasks

### Add an image to all regions (prod)

Add an entry to the `commonRepositories` repositorySet in `image-sync-prod.yaml`:

```yaml
repositorySets:
  commonRepositories:
    - name: tidbcloud/my-service
      source: us-docker.pkg.dev/my-project/hub/my-service
      tags:
        allow:
          - "v[0-9]+\\.[0-9]+\\.[0-9]+"
```

For dev only, edit `image-sync-dev.yaml` instead. For a specific region only, add under that registry's `repositories` block in `targetRegistries`.

### Add a new target region

Add a new entry to `targetRegistries` in the appropriate `image-sync-*.yaml`:

```yaml
  - name: 380838443567.dkr.ecr.ap-northeast-3.amazonaws.com
    type: ECR
    includeRepositorySets:
      - commonRepositories
```

ECR registries share the same account ID (`380838443567` for prod, `385595570414` for dev).

### Add a named repositorySet (opt-in per region)

Define the set under `repositorySets`, then reference it in the specific `targetRegistries` entries that need it via `includeRepositorySets`.

## Fast Image Delivery (FID)

Syncs specific NextGen images immediately without a PR or waiting for the scheduled run.

**Comment trigger** (Issues or PRs):
```
/fid env=<dev|prod> [cid=<uuid>]
us.gcr.io/pingcap-public/tidbx/pd:master-next-gen
us.gcr.io/pingcap-public/tidbx/tidb:release-nextgen-20241022
```

**Manual dispatch**: Actions → Fast Image Delivery → Run workflow.

Images must match patterns defined in `image-sync-nextgen-{env}.yaml`. The workflow validates before syncing.

## GitHub Actions Workflows

| Category | Workflows |
|---|---|
| Scheduled sync | `image-sync-{dev,prod}`, `image-sync-nextgen-{dev,prod}`, `image-sync-byoc-{dev,prod}`, `image-sync-byoc-azure-{dev,prod}`, `image-sync-db9-dev`, `image-sync-serverless-dev` |
| FID | `fast-image-delivery-dispatch` (listens for `/fid`), `fast-image-delivery` (executes) |
| Maintenance | `image-clean-prod`, `update-ecr-policy-{dev,prod}`, `sync-dataplane-fabric-images`, `sync-prod-registry` |
| BYOC | `add-byoc-target`, `update-byoc-component-tag` |
| CI | `ci` (yamllint + empty-allow-list guard) |

Sync workflows use `cancel-in-progress: true` — a new scheduled run will cancel a still-running one. Keep sync time well under 2 hours by avoiding unbounded allow lists.

## Git & PR Workflow

- All PRs target `main`.
- CI (`ci.yml`) runs on every PR; it will block merge if any `image-sync-*.yaml` contains an empty allow list.
- After merge, sync runs on the next scheduled cron (`0 */2 * * *` for prod) or can be triggered manually via Actions.
