# Generic

---

## Overview

Upload any file to Fly Registry and organize it by package name and version. Generic storage works with any file type -- you choose a package name and version, and Fly stores your files under that path.

---

## How It Works

Generic storage organizes files by **package name** and **version**:

| Concept | What it is | Example |
|---------|-----------|---------|
| **Package name** | A name that groups related files | `my-app`, `ml-models`, `config-bundle` |
| **Version** | An identifier for a specific set of files | `1.0.0`, `latest`, `nightly-2025-03-18` |
| **Files** | One or more files stored under that package and version | `app.dmg`, `checksums.txt` |

A file's full path in Fly Registry is:

`<your-fly-subdomain>.jfrog.io/artifactory/api/generic/<name>/<version>/<filename>`

Use generic storage for anything that doesn't go through a package manager: release binaries, build outputs, signed archives, configuration bundles, or any file your team needs to share and version.

### Parameters

| Parameter | Required | Description |
|-----------|----------|-------------|
| `--name` | Yes | Package name to group your files under |
| `--version` | Yes | Version label for this set of files |
| `--exclude` | No | Wildcard pattern to skip matching files (can be used multiple times) |
| `--output-dir` | No | Directory to save downloaded files to (download only) |

Files are listed at the end of the command. You can specify one or more file paths, or use wildcards like `*.zip`.

---

## Upload Package

### With Fly App

Ask your coding agent:

*"Upload my release files to Fly under my-app version 1.0.0"*

Or use the terminal directly -- Fly App handles authentication automatically:

```bash
fly upload --name my-app --version 1.0.0 dist/app.dmg
```

Upload multiple files or use wildcards:

```bash
# Multiple files or wildcards
fly upload --name my-app --version 1.0.0 release/*.zip build/**/*.bin

# Skip specific files
fly upload --name my-app --version 1.0.0 --exclude '*.log' release/*
```

When you use a pattern like `dist/**`, Fly uploads every file it finds inside that folder tree. Folders and symlinks are skipped -- only files are uploaded.

Each file is stored under its **filename only**, with no folder path. So `dist/linux/app` and `dist/macos/app` would both arrive as `app` and clash. When that happens, the upload stops and lists which files collided. Rename them, or skip one with `--exclude`.

### With an Access Token

To upload without Fly App authentication, use an access token with cURL:

```bash
curl -u <your-fly-username>:<your-fly-token> \
  -T my-file.bin \
  https://<your-fly-subdomain>.jfrog.io/artifactory/api/generic/my-app/1.0.0/my-file.bin
```

---

## Download Package

### With Fly App

Ask your coding agent:

*"Download my-app version 1.0.0 from Fly"*

Or use the terminal directly:

```bash
fly download --name my-app --version 1.0.0 app.dmg
```

Download multiple files to a specific folder:

```bash
fly download --name my-app --version 1.0.0 --output-dir ./release file1.zip file2.tar.gz
```

> [!NOTE]
> Unlike upload, download expects exact filenames -- glob patterns are not expanded.

### With an Access Token

To download without Fly App authentication, use an access token with cURL:

```bash
curl -u <your-fly-username>:<your-fly-token> \
  -O https://<your-fly-subdomain>.jfrog.io/artifactory/api/generic/my-app/1.0.0/my-file.bin
```

---

## Upload Package with CI

Ask your coding agent: **"Add my app (generic package) version 1.0.0 to my workflow"** -- Fly will configure it for you.

**1. Add permissions** (top level, after `on:`):
```yaml
permissions:
  contents: read
  id-token: write
```

**2. Add Fly Action and upload step** (in your job's `steps:`):
```yaml
- uses: jfrog/fly-action@v1              # Setup Fly package managers

- uses: jfrog/fly-action/upload@v1       # Upload artifacts
  with:
    name: my-app
    version: '1.0.0'
    files: |
      dist/*.zip
      dist/*.tar.gz
```

---

## Download Package with CI

**1. Add permissions** (top level, after `on:`):
```yaml
permissions:
  contents: read
  id-token: write
```

**2. Add Fly Action and download step** (in your job's `steps:`):
```yaml
- uses: jfrog/fly-action@v1              # Setup Fly package managers

- uses: jfrog/fly-action/download@v1     # Download artifacts
  with:
    name: my-app
    version: '1.0.0'
    files: |
      installer.dmg
      checksums.txt
    exclude: |                             # Skip files matching pattern
      *.log
    output-dir: ./release
```

---

## GitHub Action Example

A complete workflow with both upload and download:

```yaml
name: Release Artifacts

on:
  push:
    tags: ['v*']

permissions:
  contents: read
  id-token: write                        # Authentication

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6

      - uses: jfrog/fly-action@v1              # Setup Fly package managers

      - uses: jfrog/fly-action/upload@v1       # Upload artifacts
        with:
          name: my-app
          version: ${{ github.ref_name }}
          files: |
            dist/*.zip
            dist/*.tar.gz

      - uses: jfrog/fly-action/download@v1     # Download artifacts
        with:
          name: my-app
          version: ${{ github.ref_name }}
          files: installer.dmg
          output-dir: ./release
```

---

## Distribution

Generic artifacts support public distribution -- make a specific version downloadable by anyone without authentication. Once you distribute a version, your customers can download it directly with no Fly account, no token, and no sign-in.

To manage and track your distributions, see [Distribution →](../../fly-platform/distribution/).

### Distribute with CI

Add the distribute step after your upload:

```yaml
- uses: jfrog/fly-action@v1                  # Setup Fly

- uses: jfrog/fly-action/distribute@v1       # Distribute publicly
  with:
    name: my-app
    version: '1.0.0'
    type: generic
```

To distribute multiple artifacts, add a step for each:

```yaml
- uses: jfrog/fly-action/distribute@v1
  with:
    name: my-app
    version: '1.0.0'

- uses: jfrog/fly-action/distribute@v1
  with:
    name: my-lib
    version: '2.3.1'
```

| Input | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Package name to distribute |
| `version` | Yes | Version to distribute |
| `type` | No | Package type (defaults to `generic`) |

### Public Download URL

Once a version is distributed, anyone can download it using this URL:

```
https://<your-fly-subdomain>.jfrog.io/public/generic/<name>/<version>/<file>
```

| Parameter | Description | Example |
|-----------|-------------|---------|
| `<your-fly-subdomain>` | Your Fly organization subdomain | `acme` |
| `public` | The public registry path (fixed) | `public` |
| `generic` | The package type (fixed) | `generic` |
| `<name>` | The generic package name | `my-app`, `ml-models` |
| `<version>` | The distributed version | `2.1.0`, `nightly-2025-03-18` |
| `<file>` | The filename to download | `my-app.dmg`, `checksums.txt` |

**Example:**

```bash
curl -O https://acme.jfrog.io/public/generic/my-app/2.1.0/my-app.dmg
```

### Latest Version

To always point to the most recently distributed version, use `[LATEST]` in place of the version:

```
https://<your-fly-subdomain>.jfrog.io/public/generic/<name>/[LATEST]/<file>
```

`[LATEST]` resolves to the most recently distributed version **by time**, not by version number. If you distribute versions `1.0.0`, `2.0.0`, and then `1.9.9` in that order, `[LATEST]` serves `1.9.9`. `[latest]`, `[Latest]`, and `[LATEST]` all work the same way.

The `[LATEST]` URL redirects to the actual latest version. Browsers and most download tools follow the redirect automatically. With `curl`, add the `-L` flag so the download follows it:

```bash
curl -L -O https://acme.jfrog.io/public/generic/my-app/[LATEST]/my-app.dmg
```

> [!NOTE]
> `[LATEST]` is only available for public downloads. It cannot be used as a version when uploading -- use a concrete version like `1.0.0` instead.

---

## Verify Results

After each upload or download, you can verify your files were transferred successfully. The action outputs a summary for each file:

```yaml
- name: Upload artifacts
  id: upload
  uses: jfrog/fly-action/upload@v1
  with:
    name: my-app
    version: '1.0.0'
    files: dist/*.zip

- name: Check results
  run: echo '${{ steps.upload.outputs.results }}'
  # [{"name":"app.zip","status":"success","message":"..."}]
```

---

*Back to [Package Managers →](../)*
