# Drone Build Metadata CI Contract

This document is the machine-readable contract for AI coding assistants and curl-friendly Drone CI integrations. The endpoints are public and caller-supplied identity headers can be forged; they identify stored records only.

Base URL:

```text
https://api.niusys.com/api/ci
```

## Identity Headers

Every build endpoint requires these headers. Drone pipelines normally derive them as follows:

```bash
ci_headers=(
  -H "X-Drone-Url: ${DRONE_SYSTEM_PROTO}://${DRONE_SYSTEM_HOST}"
  -H "X-Drone-Project: ${DRONE_REPO}"
  -H "X-Drone-Build-Number: ${DRONE_BUILD_NUMBER}"
)
```

`X-Drone-Url` must be an absolute HTTP(S) URL without credentials, query, or fragment. The service normalizes scheme, host, default port, and trailing slash. Project and build number are trimmed and retain their original case. These headers are not authentication or authorization credentials.

## Non-blocking CI Submissions

Metadata submission is best-effort auxiliary data. A failed metadata write must emit a searchable `WARNING` to stderr and must not fail its Drone step or the CI build. Use this wrapper for every metadata write; its command runs in an `if` condition, so it is safe when the caller enables `set -e`.

```bash
ci_warn_submit() {
  label=$1
  shift

  if "$@"; then
    return 0
  else
    status=$?
    echo "WARNING: CI metadata submission failed: ${label} (exit=${status}); continuing CI." >&2
    return 0
  fi
}
```

Do not use a bare `curl --fail-with-body`, `exit 1`, retries after a failure, or `|| true` for a metadata write. The wrapper preserves curl's error output while making the warning visible and the submission non-blocking. Context and variables are independent: attempt each one even when the other fails.

## JSON Context Files

`PATCH /context` accepts a raw JSON object. Use `--data-binary` to send a local file unchanged. The request applies RFC 7396 JSON Merge Patch: nested objects merge, arrays and scalar values replace, and `null` removes a field.

```bash
ci_warn_submit "metadata context" curl --fail-with-body -X PATCH "$CI_METADATA_API/context" \
  "${ci_headers[@]}" \
  -H 'Content-Type: application/merge-patch+json' \
  --data-binary @context.json
```

The request and the merged, stored context must each be JSON objects. The final stored context is limited to 64 KiB UTF-8. A rejected patch does not change the stored record. `GET /context` returns the complete record with `droneUrl`, `projectName`, `buildNumber`, `context`, `createdAt`, and `updatedAt`:

```bash
curl --fail-with-body "$CI_METADATA_API/context" "${ci_headers[@]}" | jq '.context'
```

## Variables Files

`PATCH /context/vars` accepts a UTF-8 `text/plain` file. It maps all parsed entries into `context.variables` and uses the same atomic JSON Merge Patch behavior, so separate pipeline stages can upload separate files.

```bash
cat > build.env <<'EOF'
# Values remain literal strings.
BUILD_IMAGE=registry.example.com/niusys/webapi:1842
COMMIT=0123456789abcdef
RELEASE_CHANNEL=stable
EOF

ci_warn_submit "metadata variables" curl --fail-with-body -X PATCH "$CI_METADATA_API/context/vars" \
  "${ci_headers[@]}" \
  -H 'Content-Type: text/plain; charset=utf-8' \
  --data-binary @build.env
```

The supported dotenv subset is deliberately strict:

- Blank lines and lines whose first non-whitespace character is `#` are ignored.
- Each other line is `KEY=value`; the first `=` separates the key and literal value.
- Keys must match `[A-Za-z_][A-Za-z0-9_]*`. Duplicate keys, invalid keys, and lines without `=` return `400`.
- Values are not unquoted, unescaped, expanded, or evaluated. `KEY=` stores an empty string.
- Delete a variable with the JSON endpoint, for example `{"variables":{"RELEASE_CHANNEL":null}}`.

Read variables as a stable, ascending-key `key=value` file:

```bash
curl --fail-with-body "$CI_METADATA_API/context/vars" "${ci_headers[@]}" \
  --output build.env.received
```

Do not run `source build.env.received` or otherwise evaluate content returned from the network. Parse it as data, for example with `while IFS= read -r line; do ...; done`. A build with no `context.variables` returns an empty file. A missing build returns `404`. A non-object `context.variables`, or a variables object containing invalid keys, non-string values, or newline-containing values, returns `409` until repaired with the JSON endpoint.

## Help and Safety

Retrieve this exact contract from a running API with:

```bash
curl --fail-with-body "$CI_METADATA_API/help"
```

Context and variable values are public through the APIs and query page. Never upload API keys, tokens, passwords, private keys, or unredacted credentials.

For CI artifact registration and release publication, fetch the separate catalog contract at:

```text
https://api.niusys.com/api/ci/artifact-catalog/help
```
