DEV Community

Cover image for How to store installers and binaries: path layout and curl download
Wen Ping
Wen Ping

Posted on Originally published at anguskit.com

How to store installers and binaries: path layout and curl download

Installers travel by chat or shared drives, every script invents its own path, and partners stay on last month’s zip—usually the files are fine and there is no single binary entry: no path convention, no authenticated upload, no stable download URL.

This article walks through AngusRepo Community self-hosted: install → create Raw HOSTED → path layout → curl upload / download → verify. Host: repo.example.com. For anonymous external distribution, mark the repo public—see Public portal for partners.

1. Install (get a working console)

Prerequisites: Docker Engine + Compose v2; ~2 CPU / 4 GB; port 80; DNS for gm.example.com / repo.example.com. See First run.

curl --fail --location --progress-bar -o AngusRepo-Community-1.0.0.zip \
  https://repo.anguskit.com/raw/raw-public/AngusKit/repo/AngusRepo-Community-1.0.0.zip
unzip AngusRepo-Community-1.0.0.zip
cd AngusRepo-1.0.0
./install.sh
Enter fullscreen mode Exit fullscreen mode

Wizard: Compose; Access 2; TLS 4 (HTTP :80); Compose MySQL; Public DNS example.com. Then ./bin/angusctl.sh doctor. Open GM and Repo. HTTPS: Install & deploy. Default below is HTTP.

2. Repo, token, and path layout

Raw usually needs one HOSTED repo; organize with paths, not one repo per file.

Example name Type Role
raw-hosted HOSTED Any file: installers, scripts, reports, binaries

Recommended layout: project/version/filename:

raw-hosted/
├── my-app/
│   ├── 1.0.0/
│   │   └── my-app-1.0.0-linux-amd64.tar.gz
│   └── 1.1.0/
│       └── my-app-1.1.0-linux-amd64.tar.gz
└── scripts/
    └── deploy.sh
Enter fullscreen mode Exit fullscreen mode

Upload overwrites: the same path replaces the previous object with no history. Put the version in the path—do not rely on a lone latest.tar.gz.

Prepare:

  • [ ] Access token (shown once)
  • [ ] CI Write on raw-hosted; downloaders Read (or anonymous Read if public)
export REPO_USER="admin"
export REPO_TOKEN="your_access_token"
export REPO_HOST="repo.example.com"
Enter fullscreen mode Exit fullscreen mode

Details: Raw Format.

3. Upload: curl --upload-file

mkdir -p /tmp/angus-raw-demo && cd /tmp/angus-raw-demo
echo "hello-angus-raw" > README.txt
tar -czf my-app-1.0.0-linux-amd64.tar.gz README.txt

curl -u "$REPO_USER:$REPO_TOKEN" \
  --upload-file ./my-app-1.0.0-linux-amd64.tar.gz \
  "http://$REPO_HOST/raw/raw-hosted/my-app/1.0.0/my-app-1.0.0-linux-amd64.tar.gz"
Enter fullscreen mode Exit fullscreen mode

Batch:

for file in dist/*.tar.gz; do
  filename=$(basename "$file")
  curl -u "$REPO_USER:$REPO_TOKEN" \
    --upload-file "$file" \
    "http://$REPO_HOST/raw/raw-hosted/my-app/1.0.0/${filename}"
done
Enter fullscreen mode Exit fullscreen mode

Writes always require auth, even when the repository is public (public means anonymous read only).

4. Download: private and public

Private (Bearer preferred):

curl -H "Authorization: Bearer $REPO_TOKEN" \
  -O "http://$REPO_HOST/raw/raw-hosted/my-app/1.0.0/my-app-1.0.0-linux-amd64.tar.gz"
Enter fullscreen mode Exit fullscreen mode

Or Basic: curl -u "$REPO_USER:$REPO_TOKEN" -O "http://…".

Public anonymous:

curl -O "http://$REPO_HOST/raw/raw-hosted/my-app/1.0.0/my-app-1.0.0-linux-amd64.tar.gz"
Enter fullscreen mode Exit fullscreen mode

5. Verify the loop

curl -u "$REPO_USER:$REPO_TOKEN" \
  -o /tmp/got.tar.gz \
  "http://$REPO_HOST/raw/raw-hosted/my-app/1.0.0/my-app-1.0.0-linux-amd64.tar.gz"
tar -tzf /tmp/got.tar.gz | head
Enter fullscreen mode Exit fullscreen mode

You should see README.txt. Optional listing (see console / manual for the exact API):

curl -u "$REPO_USER:$REPO_TOKEN" \
  "http://$REPO_HOST/api/v1/repositories/raw-hosted/contents?path=/my-app/1.0.0/"
Enter fullscreen mode Exit fullscreen mode

Minimal CI snippet

- run: |
    curl -u "$REPO_USER:$REPO_TOKEN" \
      --upload-file "./dist/my-app-${VERSION}-linux-amd64.tar.gz" \
      "http://$REPO_HOST/raw/raw-hosted/my-app/${VERSION}/my-app-${VERSION}-linux-amd64.tar.gz"
Enter fullscreen mode Exit fullscreen mode

6. Day-two ops

  • Versions belong in the path.
  • Watch reverse-proxy timeouts and upload body limits for large files.
  • Dry Run before deleting old version trees—Cleanup Policies.
  • External distribution: a dedicated public repo + /explore; do not open the whole internal script repo.

Error cheat sheet

Symptom Likely cause Fix
401 Unauthorized bad token or unauthenticated upload -u user:token or Bearer
404 Not Found wrong path or never uploaded match Browse and URL
Stale download same path overwritten version in path; do not overwrite releases
Upload timeout proxy body/timeout too small raise limits and retry

FAQ

How is Raw different from Maven/npm?

Raw stores files by path with no package metadata parsing. Use format-specific HOSTED repos when you need coordinates.

Why does upload overwrite?

Raw has no version protection. Keep history by putting versions (or hashes) in the path.

Can uploads be anonymous?

No. Public only unlocks read; writes always need credentials.

Takeaways

  • Console → raw-hosted → project/version/filename.
  • curl --upload-file to upload; Bearer/Basic or anonymous public download.
  • Smoke-test upload → Browse → download and extract.

Reference articles

Top comments (0)