Skip to content
Source
Sign inSign up

[Bug]: A root tea login prevents resolving another Gitea instance mounted on the same host #35904

Send to my agent
Opencoygeek(GitHub profile, opens in a new tab) opened this issue 0 comments

Summary

A configured tea login for a Gitea instance under a URL subpath becomes unusable when another login points to the host's root. T3 Code reports that no login matches even when the repository URL explicitly identifies the mounted instance. An isolated run of the production repository resolver reproduced the failure; the same repository resolves correctly with only the mounted login configured.

Before submitting

  • Related issues: none found in open and closed issues for this login-selection failure.
  • Removed credentials, tokens, private paths, and sensitive data.

Area

apps/server

Observed behavior

With tea login URLs https://forge.example and https://forge.example/gitea, resolving https://forge.example/gitea/owner/repo.git fails with ForgejoCliError, reason authentication, and “No matching Forgejo login.” The same production call returns login mounted-instance, repository owner/repo, and base URL https://forge.example/gitea when the root login is absent. The fixture injects the process runner and filesystem; it executes no real fj or tea command and contacts no forge.

Reproduction

  1. Save the script below as repro-mount.sh.
  2. Run sh repro-mount.sh /path/to/t3code against the stated commit.
  3. Observe the passing single-login control followed by the authentication failure with both configured logins.

The completed script reproduced the failure in one recorded run. An earlier harness setup attempt was inconclusive because it read an incorrect Effect Result property; that harness error was corrected before the recorded run below.

The script requires POSIX sh, Git, Node with native TypeScript support and registerHooks, npm, and access to the npm registry. It downloads the repository-pinned Effect package with scripts disabled, imports unchanged production files through a temporary module resolver, and injects synthetic tea login-list output. All generated files and state live under a disposable directory that is removed on exit. Exit 1 means the defect reproduced, exit 0 means the mounted login resolved correctly, and exit 2 means setup or the control failed.

Reproduction script: repro-mount.sh
#!/bin/sh
set -u
root=$(cd "$1" && pwd) || exit 2
scratch=$(mktemp -d) || exit 2
trap 'rm -rf "$scratch"' EXIT HUP INT TERM
export HOME="$scratch/home" TMPDIR="$scratch/tmp" XDG_CONFIG_HOME="$scratch/config" XDG_DATA_HOME="$scratch/data" XDG_STATE_HOME="$scratch/state" T3CODE_HOME="$scratch/t3"
export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null
mkdir -p "$HOME" "$TMPDIR" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_STATE_HOME" "$T3CODE_HOME"
git -C "$root" rev-parse HEAD
node --version
npm --version
if ! (cd "$scratch" && npm pack effect@4.1.3 --ignore-scripts --silent > artifact-name && tar -xzf effect-4.1.3.tgz); then
  echo 'RESULT: inconclusive'; exit 2
fi
cat > "$scratch/run.mjs" <<'JS'
import {registerHooks} from 'node:module';
import {pathToFileURL} from 'node:url';
import {resolve} from 'node:path';
const root=process.argv[2];
registerHooks({resolve(s,c,next){
 if(s.startsWith('effect/')){
  const p=s.slice(7);
  return {url:pathToFileURL(resolve(import.meta.dirname,'package/dist',p+(['http','process'].includes(p)?'/index.js':'.js'))).href,shortCircuit:true};
 }
 if(s.startsWith('@t3tools/')){
  const [,pkg,...rest]=s.split('/');
  return {url:pathToFileURL(resolve(root,'packages',pkg,'src',rest.join('/')+'.ts')).href,shortCircuit:true};
 }
 return next(s,c);
}});
try {
 const Effect=await import('effect/Effect');
 const Layer=await import('effect/Layer');
 const FileSystem=await import('effect/FileSystem');
 const {HttpClient}=await import('effect/http');
 const Cli=await import('@t3tools/source-control-forgejo/server/ForgejoCli');
 const Host=await import('@t3tools/source-control-core/server/SourceControlHost');
 const HostProcess=await import('@t3tools/shared/HostProcess');
 console.log('effect 4.1.3');
 const out=stdout=>({exitCode:0,stdout,stderr:'',stdoutTruncated:false,stderrTruncated:false});
 const mounted={name:'mounted-instance',url:'https://forge.example/gitea',user:'reviewer',default:'false'};
 const top={name:'root-instance',url:'https://forge.example',user:'reviewer',default:'false'};
 async function run(rows){
  const cli=await Effect.runPromise(Cli.make.pipe(
   Effect.provide(Layer.mock(Host.SourceControlHost)({process:{run:i=>{
    if(i.command!=='tea'||i.args.join(' ')!=='login list --output json')return Effect.die('Unexpected command');
    return Effect.succeed(out(JSON.stringify(rows)));
   }}})),
   Effect.provideService(FileSystem.FileSystem,FileSystem.makeNoop({exists:()=>Effect.succeed(false)})),
   Effect.provideService(HttpClient.HttpClient,HttpClient.make(()=>Effect.die('Network forbidden'))),
   Effect.provideService(HostProcess.HomeDirectory,process.env.HOME)
  ));
  return Effect.runPromise(cli.resolveRepository({cwd:process.env.HOME,repository:'https://forge.example/gitea/owner/repo.git'}).pipe(Effect.result));
 }
 const control=await run([mounted]);
 if(control._tag!=='Success'||control.success.login!=='mounted-instance'||control.success.repository!=='owner/repo')throw new Error('Control failed');
 console.log('Control: mounted-instance resolves owner/repo');
 const result=await run([top,mounted]);
 if(result._tag==='Failure'){
  console.log('Root and mounted logins: '+result.failure.reason+'; '+result.failure.detail);
  console.log('RESULT: defect reproduced');process.exitCode=1;
 }else if(result.success.login==='mounted-instance'&&result.success.repository==='owner/repo'){
  console.log('RESULT: expected behavior');
 }else throw new Error('Unexpected resolution');
}catch(error){console.log('Setup or control failed: '+error.message);console.log('RESULT: inconclusive');process.exitCode=2;}
JS
node --disable-warning=ExperimentalWarning "$scratch/run.mjs" "$root"

Expected behavior

An explicit repository URL under the mounted Gitea base should select its configured login even when another instance shares the hostname. T3's docs/user/source-control.md#L49-L64 documents mounted instances and full server URLs as supported inputs. Gitea's reverse-proxy documentation permits a server root containing a subpath; tea v0.16.0's login-list formatter supplies each configured login's complete URL.

Impact and scope

Impact: P2 - Adding another valid login can disable repository resolution and subsequent Forgejo/Gitea operations for a mounted instance. The failure is bounded to overlapping instance base URLs on one host and does not affect the single-login control.

NOT_ENOUGH_INFO: A practical workaround within T3 Code was not verified; checking a separately configured tea environment would establish one. Choosing a default is not enough in the observed resolver: its default selection applies only when all matching login URLs are equal. No real accounts or installations were tested.

Version

T3 Code commit 59e64eef881632a23f89c4138bcf37f23d92bf93; tea output follows released v0.16.0 formatter source. Effect 4.1.3, pinned by the repository.

Runtime and platform

Darwin arm64; Node v26.12.1; npm 11.20.0 under the isolated HOME. Git revision printed by the script identifies the checkout. The fixture did not execute a tea or fj binary.

Interface

Library: ForgejoCli.resolveRepository, used by the server's Forgejo/Gitea source-control provider.

Earliest failure stage

Setup or configuration

Relevant output or evidence

Recorded output from the script:

59e64eef881632a23f89c4138bcf37f23d92bf93
v26.12.1
11.20.0
effect 4.1.3
Control: mounted-instance resolves owner/repo
Root and mounted logins: authentication; No matching Forgejo login. Use `fj auth login`, `fj auth add-token`, or `tea login add` for this server; choose a default when multiple tea accounts match.
RESULT: defect reproduced

All hostnames, repository names, account names, and login names in the fixture are synthetic. The production matcher accepts both the root login and the longer mount prefix, then rejects matches with differing URLs: packages/source-control-forgejo/src/server/ForgejoCli.ts#L177-L210. The supported repository resolver applies that matcher and converts the unresolved selection to the observed authentication failure: packages/source-control-forgejo/src/server/ForgejoCli.ts#L590-L639.

Analysis or suspected cause

Source evidence: A root login's empty path matches every repository on the host. The matcher does not distinguish the more specific mount in this input, and its default-login branch cannot select between different base URLs.

  1. tanstack-source added labelsbug

  2. github-actions[bot](GitHub profile, opens in a new tab) added labelsvouch:denounced

Sign in to join this conversation.