From 606f85df34cd341cb44f86af2e11effdad6dfd29 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Tue, 25 Aug 2026 22:55:26 +0200 Subject: [PATCH] Send the LFS object endpoint one Authorization header, not two The model fetch has been failing on every run with LFS: Client error: .../info/lfs/objects/0672d7a7... which reads like a rejected credential and is not one. The object is on the server and downloads fine; what fails is the shape of the request. `git lfs pull` makes two calls. The first, to `/info/lfs/objects/batch`, succeeds -- and Gitea answers it with a short-lived `Bearer` JWT scoped to that one object, for git-lfs to use on the second. git-lfs sends that JWT *and* the `Authorization` header this step had installed in git config, and two `Authorization` headers is a 400 from Gitea. Hence a client error on the object one step after the batch call it just made successfully, which is what made this look like an auth problem rather than a duplication. Confirmed directly against the server: the JWT alone on that URL is a 200, the JWT plus any second `Authorization` is a 400, and a lone token header that is merely wrong is a 401 -- so the scheme was never the issue. `lfs: true` on the checkout fails the same way and for the same reason, because actions/checkout persists a header of its own; the comment here blaming a credential the endpoint would not accept was wrong on both counts. So the headers are stripped -- checkout's included, since nothing later in either job talks to the remote -- and the token is handed to git-lfs as an ordinary credential instead. It authenticates the batch call and leaves the per-object JWT alone. This is what fails the Android job today: the build script sees a 133-byte pointer and panics by design, which is the message it is supposed to give and the one nobody could act on. Co-Authored-By: Claude Opus 5 --- .gitea/workflows/build-and-test.yml | 70 ++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/.gitea/workflows/build-and-test.yml b/.gitea/workflows/build-and-test.yml index 604b602..509f3fc 100644 --- a/.gitea/workflows/build-and-test.yml +++ b/.gitea/workflows/build-and-test.yml @@ -39,24 +39,39 @@ jobs: # pointer and failing at inference. That failure reads like a broken build # instead of a missing fetch, which is how it went unnoticed. # - # Not `lfs: true` on the checkout above: that makes the *checkout* fail on - # this server — `git lfs fetch` is rejected at - # `/info/lfs/objects/` with a client error, because the credential - # checkout installs for git is not one the LFS endpoint accepts. Fetching - # it as its own step with an explicit token keeps a credential problem - # from looking like a checkout problem, and lets the rest of the job say - # what it thinks. + # Not `lfs: true` on the checkout above, and no `Authorization` header + # here either. Both install a blanket header for every request to this + # host, and the object download is the one request that already carries + # one: `git lfs pull` asks `/info/lfs/objects/batch` first, and Gitea + # answers with a short-lived `Bearer` JWT scoped to that object. git-lfs + # then sends the JWT *and* the configured header, and two `Authorization` + # headers is a 400 from Gitea — reported as + # LFS: Client error: .../info/lfs/objects/ + # one step after the batch call that had just succeeded, which reads like + # a rejected credential rather than a duplicated one. A lone token header + # is understood fine; it is only the collision that fails. + # + # So: strip the headers and hand the token to git-lfs as an ordinary + # credential instead. It authenticates the batch call and leaves the + # per-object JWT untouched. Gitea authenticates on the password, so the + # username is a placeholder. Nothing later in this job talks to the + # remote, so dropping checkout's header costs us nothing. # # `continue-on-error` deliberately: if this cannot authenticate, the build # below still runs and fails with the build script's own message, which # names the real problem. A checkout that dies here says nothing. - name: Fetch the segmentation model continue-on-error: true + env: + LFS_TOKEN: ${{ secrets.GITEA_TOKEN || github.token }} run: | - set -x + set -e git lfs install --local - git config --local lfs.https://gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs.access basic - git config --local http.extraheader "Authorization: token ${{ secrets.GITEA_TOKEN || github.token }}" + git config --local --get-regexp '^http\..*extraheader$' \ + | cut -d' ' -f1 | sort -u \ + | while read -r key; do git config --local --unset-all "$key"; done || true + git config --local lfs.url \ + "https://x-access-token:${LFS_TOKEN}@gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs" git lfs pull ls -l core/dr-segment/models/ @@ -123,24 +138,39 @@ jobs: # pointer and failing at inference. That failure reads like a broken build # instead of a missing fetch, which is how it went unnoticed. # - # Not `lfs: true` on the checkout above: that makes the *checkout* fail on - # this server — `git lfs fetch` is rejected at - # `/info/lfs/objects/` with a client error, because the credential - # checkout installs for git is not one the LFS endpoint accepts. Fetching - # it as its own step with an explicit token keeps a credential problem - # from looking like a checkout problem, and lets the rest of the job say - # what it thinks. + # Not `lfs: true` on the checkout above, and no `Authorization` header + # here either. Both install a blanket header for every request to this + # host, and the object download is the one request that already carries + # one: `git lfs pull` asks `/info/lfs/objects/batch` first, and Gitea + # answers with a short-lived `Bearer` JWT scoped to that object. git-lfs + # then sends the JWT *and* the configured header, and two `Authorization` + # headers is a 400 from Gitea — reported as + # LFS: Client error: .../info/lfs/objects/ + # one step after the batch call that had just succeeded, which reads like + # a rejected credential rather than a duplicated one. A lone token header + # is understood fine; it is only the collision that fails. + # + # So: strip the headers and hand the token to git-lfs as an ordinary + # credential instead. It authenticates the batch call and leaves the + # per-object JWT untouched. Gitea authenticates on the password, so the + # username is a placeholder. Nothing later in this job talks to the + # remote, so dropping checkout's header costs us nothing. # # `continue-on-error` deliberately: if this cannot authenticate, the build # below still runs and fails with the build script's own message, which # names the real problem. A checkout that dies here says nothing. - name: Fetch the segmentation model continue-on-error: true + env: + LFS_TOKEN: ${{ secrets.GITEA_TOKEN || github.token }} run: | - set -x + set -e git lfs install --local - git config --local lfs.https://gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs.access basic - git config --local http.extraheader "Authorization: token ${{ secrets.GITEA_TOKEN || github.token }}" + git config --local --get-regexp '^http\..*extraheader$' \ + | cut -d' ' -f1 | sort -u \ + | while read -r key; do git config --local --unset-all "$key"; done || true + git config --local lfs.url \ + "https://x-access-token:${LFS_TOKEN}@gitea.tourolle.paris/dtourolle/DarkRoom.git/info/lfs" git lfs pull ls -l core/dr-segment/models/