Skip to content

gitea forge preset is broken against the real tea CLI (0.14.2) #1137

Description

@pseudoseed

gitea forge preset is broken against the real tea CLI (0.14.2)

Summary

The gitea provider preset (packages/codev/scripts/forge/gitea/*.sh, added for #589) does
not work against the actual tea CLI. Most read concepts either error out or emit a JSON
shape that doesn't match forge-contracts.ts. The scripts appear to have been written against
the Gitea REST API JSON shape, but they invoke the tea CLI, whose --output json
emits a different, flattened shape (and in some cases the referenced flag/field/subcommand
doesn't exist at all). This matches the in-repo note in issue-search.sh:
"tea is not available in the authoring environment (#920)" — the preset was authored blind.

Verified end-to-end with tea 0.14.2 (latest release, 2026-06-26) against a live Forgejo
instance (Forgejo is Gitea-API-compatible). Not a version-gap issue — 0.14.2 is current.

Broken concepts (verified)

Script Current command Observed failure
user-identity.sh tea whoami --output json | jq -r .login tea whoami has no --output json flagError: flag provided but not defined: -output
pr-list.sh tea pulls list --fields …,description --output json Error: invalid field 'description' — the whole concept errors; tea pulls list exposes no description/body field
pr-view.sh tea pulls view N --output json Returns [] / a table header, not the PR object
issue-view.sh tea issues view N --output json | jq '.url = (.html_url // .url)' Returns a flattened single-element array (list shape) with no body/html_url/url fields
issue-comment.sh tea issues comment … No such subcommand — tea issues has no comment; commenting lives under tea comments add
pr-exists.sh … | jq 'select(.head.ref == $B and (.state=="closed" and .merged==true) …)' tea pulls list emits .head as a string, not {ref}, and reports merged PRs as state:"merged" (no .merged bool) → Cannot index string with string "ref"
recently-merged.sh --fields …,head,description,merged + .head.ref + .merged==true Same string-head + invalid description field problems

Working as-is: auth-status, issue-list, recently-closed, pr-merge, repo-archive
(the last already uses tea api).

Root cause

Two divergent JSON surfaces in tea:

  1. tea <entity> list --output json — a flattened, --fields-limited view
    (head/base are strings, index is a string, no nested objects, no body/description
    for pulls, state is synthesized to "merged").
  2. tea api <endpoint> — a raw passthrough to the Gitea REST API, returning the exact
    documented API shape.

codev's jq normalizers and forge-contracts.ts are written for (2) but the scripts call (1).

Recommended fix: route read concepts through tea api

tea api returns precisely the shape the existing jq already assumes, so the fix is mostly a
data-source swap with the jq kept intact. Verified against live Forgejo:

# user-identity
tea api user | jq -r '.login'                       # => the login, matches gh api user

# pr-list / pr-exists (repo from git remote, e.g. $CODEV_REPO)
tea api "repos/$CODEV_REPO/pulls?state=all&limit=200" \
  | jq '.[0] | {number, state, merged, headRef: .head.ref, body, author: .user.login}'
  # => number:int, state:"closed", merged:true, head.ref, body, user.login — all present

# pr-view
tea api "repos/$CODEV_REPO/pulls/$CODEV_PR_NUMBER"    # full PR object

# issue-view
tea api "repos/$CODEV_REPO/issues/$CODEV_ISSUE_ID"    # full issue object incl. body

# issue-comment
tea comments add "$CODEV_ISSUE_ID" "$CODEV_COMMENT_BODY"

This is minimal-diff (keep the jq contracts), and robust across tea versions since it avoids
the CLI's --fields/flattening quirks entirely.

One inherent caveat (not a codev bug)

For a merged PR whose source branch was deleted, Gitea's API returns
.head.ref == "refs/pull/N/head" instead of the original branch name — so a branch-name match
in pr-exists won't hit a merged+deleted branch. That's a Gitea behavior; worth a comment in
the script but doesn't affect the "does an open/merged PR exist for the branch I'm about to
push" use case.

Reproducibility (not environment-specific)

Verified two independent ways, neither dependent on the reporting instance:

1. The failures are defined in the tea binary, before any server contact:

$ tea whoami --help          # OPTIONS: only --help. No --output/json exists, any server.
$ tea issues --help          # subcommands: list, create, edit, close — NO 'comment'.

2. Canonical upstream Gitea (gitea.com, reference implementation) via plain curl — no tea:

$ curl -s https://gitea.com/api/v1/repos/gitea/tea/pulls?state=all&limit=1 \
    | jq '.[0] | {head_type: (.head|type), head_ref: .head.ref, user_login: .user.login, merged}'
{ "head_type": "object", "head_ref": "renovate/go-dependencies",
  "user_login": "renovate-bot", "merged": false }

The REST shape codev's jq assumes (.head.ref, .user.login, .merged, object responses) is
correct and canonical — the scripts just read from the wrong source (tea <entity> list, which
flattens/limits it) instead of tea api. A self-hosted Forgejo returns byte-identical shape, so
this is not a fork divergence; it reproduces against gitea.com itself.

Environment

  • tea 0.14.2 (latest release, 2026-06-26; brew install tea)
  • codev @cluesmith/codev 3.2.2
  • Confirmed against both a self-hosted Forgejo and public gitea.com (canonical Gitea)
  • Note: the gitea .sh scripts are not exercised by CI (forge.test.ts uses mock scripts; per
    the in-repo #920 note tea isn't available in the authoring/CI env), so these were validated
    manually against live instances.
  • Happy to open a PR implementing the tea api approach above if you're on board.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions