Skip to content
This page was generated and translated with the assistance of AI. If you spot any inaccuracies, feel free to help improve it. Edit on GitHub

Release Management

A release represents a specific uploaded build under a variant. Each release has a version string, build number, changelog, and the binary file itself. Releases are displayed on the product download page in reverse chronological order.

Release Fields

FieldTypeDescription
idstringAuto-generated ID (e.g., rel_b1cqa)
variant_idstringParent variant ID
versionstringVersion string (e.g., "1.2.0")
buildintegerBuild number (e.g., 120)
changelogtextRelease notes (shown on download page)
min_osstringMinimum OS version
channelstringDistribution channel (e.g., "internal", "beta", "production")
size_bytesintegerFile size in bytes
sha256stringSHA-256 hash of the uploaded file
download_countintegerNumber of times this release has been downloaded
file_namestringOriginal filename
file_extstringFile extension (e.g., "ipa", "apk")
created_atdatetimeUpload timestamp

Uploading a Release

Standard Upload

Upload a build file to a specific variant:

bash
curl -X POST http://localhost:8000/upload \
  -H "X-Auth-Token: YOUR_UPLOAD_TOKEN" \
  -F "variant_id=var_def456" \
  -F "app_file=@build/MyApp.ipa" \
  -F "version=1.2.0" \
  -F "build=120" \
  -F "channel=beta" \
  -F "changelog=Bug fixes and performance improvements"

Response:

json
{
  "ok": true,
  "data": {
    "app": {
      "id": "app_xxx",
      "name": "MyApp",
      "platform": "ios",
      "bundle_id": "com.example.myapp"
    },
    "release": {
      "id": "rel_b1cqa",
      "version": "1.2.0",
      "build": 120
    },
    "urls": {
      "page": "https://dist.example.com/products/myapp",
      "download": "https://dist.example.com/d/rel_b1cqa",
      "ios_manifest": "https://dist.example.com/ios/rel_b1cqa/manifest.plist",
      "ios_install": "itms-services://..."
    }
  }
}

Smart Upload

The smart upload endpoint auto-detects metadata from the uploaded package:

bash
curl -X POST http://localhost:8000/admin/api/smart-upload \
  -H "X-Auth-Token: YOUR_ADMIN_TOKEN" \
  -F "variant_id=var_def456" \
  -F "app_file=@build/MyApp.ipa"

Auto-Detection

Smart upload extracts the following from IPA and APK files:

  • Bundle ID / Package Name
  • Version string (CFBundleShortVersionString / versionName)
  • Build number (CFBundleVersion / versionCode)
  • App icon (extracted and stored as the product icon)
  • Minimum OS version

You can still override any auto-detected field by providing it explicitly in the upload request.

Upload Fields

FieldRequiredDescription
variant_idYesTarget variant ID
app_fileYesThe binary file (IPA, APK, DMG, etc.)
versionNoVersion string (auto-detected for IPA/APK)
buildNoBuild number (auto-detected for IPA/APK)
channelNoDistribution channel
min_osNoMinimum OS version
changelogNoRelease notes

File Storage

Uploaded files are stored at:

uploads/{product_id}/{variant_id}/{release_id}/filename.ext

Each release also has a meta.json snapshot (local storage only) for recovery purposes.

S3 Storage

When S3-compatible storage is configured, files are uploaded to the configured bucket. The storage path structure remains the same. See Configuration for S3 setup.

Download URLs

Each release provides several URLs:

URLDescription
/d/:releaseIDDirect binary download (supports HTTP Range requests)
/ios/:releaseID/manifest.plistiOS OTA manifest (for itms-services:// links)
/products/:slugProduct download page
/products/:slug?r=:releaseIDProduct page with specific release highlighted

Deleting a Release

bash
curl -X DELETE http://localhost:8000/admin/api/releases/rel_b1cqa \
  -H "X-Auth-Token: YOUR_ADMIN_TOKEN"

WARNING

Deleting a release permanently removes the uploaded binary file and all associated metadata.

Exporting Release Data

Export all releases as CSV for reporting:

bash
curl -o releases.csv http://localhost:8000/admin/exports/releases.csv \
  -H "X-Auth-Token: YOUR_ADMIN_TOKEN"

CI/CD Integration

Fenfa is designed to be called from CI/CD pipelines. A typical GitHub Actions step:

yaml
- name: Upload to Fenfa
  run: |
    curl -X POST ${{ secrets.FENFA_URL }}/upload \
      -H "X-Auth-Token: ${{ secrets.FENFA_UPLOAD_TOKEN }}" \
      -F "variant_id=${{ secrets.FENFA_VARIANT_ID }}" \
      -F "app_file=@build/output/MyApp.ipa" \
      -F "version=${{ github.ref_name }}" \
      -F "build=${{ github.run_number }}" \
      -F "changelog=${{ github.event.head_commit.message }}"

Next Steps

Released under the Apache-2.0 License.