Skip to main content

Generic Packages in CI/CD

Generic PackagePackages Publishingin Debugging

CI/CD\n\nForgejo

Problem

(and

WhenGitea) tryingprovide a generic package registry that allows you to publish releasearbitrary binariesfiles asfrom GiteaCI/CD pipelines — ideal for distributing compiled binaries, configuration files, or any static assets.\n\n## Authentication\n\nForgejo requires Basic authentication for the generic packages viaAPI. CI/CD,This alldiffers uploadfrom attempts failed with various error codes (401, 404, 405, 500).

Root Cause Discovery

The instance is Forgejo (v14.0.2 / gitea-1.22.0), notthe standard Gitea.REST TheAPI authenticationwhich methodaccepts required differs between the two.

What Didn't Work

MethodResult
Authorization: token <PAT> + raw PUT500 (content-type mismatch)
headers.\n\nAuthorization: token <PAT> + multipart form POST404
Authorization: token <PAT> + multipart form PUT405
--user "user:token" + raw PUT to /api/v1/packages/...404

What Worked

curl -X PUT \bash\ncurl --user \"<username>:<PAT>\" ...\n\n\nWhere <PAT> is a Personal Access Token with the write:package scope.\n\n## Publishing Files\n\n### Upload a single file\n\nbash\ncurl -X PUT \\\n  --user \"<username>:<PAT>\" \\\n  --upload-file target/release/bookstack-cli-rs./mybinary \\\n  https://git.example.com/api/packages/<owner>/generic/bookstack-cli-rs/v0.1.2-rc1/bookstack-cli-rs-x86_64-unknown-linux-gnu<repo>/<version>/<filename>\n\n\nPath components:\n\n| Component | Description |\n|-----------|-------------|\n| <owner>

Key:| UseYour Basicusername author organization name |\n| <repo> | Repository name (used as package namespace) |\n| --user<version> user:token| Version string (e.g. v1.0.0, 0.2.1-rc1), not|\n| Authorization: token<filename> header.

|

ForgejoThe vsactual Giteafile Authto Difference

upload |\n\n###Uploadmultiple
API EndpointGitea AuthForgejo Auth
/api/v1/... (REST)Authorization: token <PAT>Authorization: token <PAT>
/api/packages/... (packages)Authorization: token <PAT>Basic auth (--user user:token)

Forgejo requires Basic authenticationfiles for the genericsame packageversion\n\nbash\ncurl registry-X API,PUT while\\\n standard Gitea accepts the token header. The REST API works with both.

CI/CD Workflow Fix

Changed from:

-H "Authorization: token ${{ secrets.PACKAGE_TOKEN }}"

To:

--user \"<username>:${{ secrets.PACKAGE_TOKEN }}<PAT>\" 

Download URL

Users can download binaries with:

curl -fsSL \\\n  --userupload-file "<username>:$GITEA_TOKEN"./linux-binary \\\n  https://git.example.com/api/packages/<owner>/generic/bookstack-cli-rs/0.1.0/bookstack-cli-rs-x86_64-unknown-<repo>/<version>/linux-gnubinary\n\ncurl -X PUT \
  -o bookstack-cli-rs && chmod +x bookstack-cli-rs

Release Process

cargo test\\n  --alluser &&\"<username>:<PAT>\" \\\n  --upload-file ./macos-binary \\\n  https://git.example.com/api/packages/<owner>/generic/<repo>/<version>/macos-binary\n\n\nEach file is stored independently under the same version path.\n\n## Downloading Files\n\n### Direct download with authentication\n\nbash\ncurl -fsSL --user \"<username>:<PAT>\" \\\n  https://git.example.com/api/packages/<owner>/generic/<repo>/<version>/<filename> \\\n  --output ./downloaded-binary\n\n\nThe -fsSL flags:\n- -f: Fail silently on HTTP errors\n- -s: Silent mode (no progress meter)\n- -S: Show errors if silent mode is used\n- -L: Follow redirects\n\n### Without authentication (public packages)\n\nIf the package repository is public, you can download without credentials:\n\nbash\ncurl -fsSL \\\n  https://git.example.com/api/packages/<owner>/generic/<repo>/<version>/<filename> \\\n  --output ./downloaded-binary\n\n\n## GitHub Actions Workflow Example\n\nyaml\nname: Release\non:\n  push:\n    tags:\n      - \"v*\"\n\npermissions:\n  contents: write\n  packages: write\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Build release binary\n        run: cargo build --release\n\n      - name: Upload to generic registry\n        env:\n          GITEA_TOKEN: ${{ secrets.PACKAGE_TOKEN }}\n        run: |\n          curl -X PUT \\\n            --user \"$(echo \"${{ github.actor }}:$GITEA_TOKEN\" | base64)\" \\\n            --upload-file target/release/myapp \\\n            https://git.example.com/api/packages/${{ github.repository_owner }}/generic/myapp/${{ github.ref_name }}/myapp\n\n\n## Gitea Actions Workflow Example\n\nyaml\nname: Release\non:\n  push:\n    tags:\n      - \"v*\"\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Build release gitbinary\n        tagrun: v0.1.0cargo gitbuild push--release\n\n      origin- v0.1.0name: Upload to generic registry\n        env:\n          GITEA_TOKEN: ${{ secrets.PACKAGE_TOKEN }}\n        run: |\n          curl -X PUT \\\n            --user \"username:$GITEA_TOKEN\" \\\n            --upload-file target/release/myapp \\\n            https://git.example.com/api/packages/${{ github.repository_owner }}/generic/myapp/${{ github.ref_name }}/myapp\n
\n\n##

ThisTroubleshooting\n\n| triggersError | Cause | Solution |\n|-------|-------|----------|\n| 401 Unauthorized | Missing or invalid PAT | Verify token has write:package scope |\n| 403 Forbidden | Insufficient permissions | Ensure user has write access to the CIrepository workflow|\n| which:

404
    Not
  1. RunsFound tests
  2. |
  3. BuildsWrong releasepath binary
  4. or
  5. Publishesprivate package | Check owner/repo/version/filename; use auth for private packages |\n| 405 Method Not Allowed | Wrong HTTP method | Use PUT, not POST |\n| 500 Internal Server Error | Content-Type mismatch | Remove Content-Type: application/json; let curl auto-detect |\n\n## Key Differences: Forgejo vs Gitea\n\nBoth Forgejo and Gitea support generic packages, but authentication differs:\n\n| Endpoint | Gitea Auth | Forgejo Auth |\n|----------|-----------|---------------|\n| /api/v1/... (REST) | Authorization: token <PAT> | Authorization: token <PAT> |\n| /api/packages/... (packages) | Authorization: token <PAT> | Basic auth (--user user:token) |\n\nAlways use Basic auth for the packages API to Giteaensure genericcompatibility packageacross registry
  6. both
  7. Createsplatforms. a Gitea release