Skip to main content

faynoSync CLI

faynoSync-cli is a command line tool focused on uploading new application versions to faynoSync in a native and predictable way.

The main problem it solves is inconsistent shell escaping in some CI environments. Instead of relying on CI-specific quoting behavior, the CLI provides a unified upload flow and stable changelog input modes, so the same command behaves the same way locally and across CI runners.

Why use the CLI?

BenefitWhat it means in practice
Predictable uploadsA single, unified upload command instead of hand-crafted curl calls that break on CI-specific quoting.
Stable changelog inputThree mutually exclusive modes (--changelog, --changelog-file, --changelog-stdin) so Markdown and special characters survive shell escaping.
Config + env splitserver and owner live in a config file and can be overridden per-run by environment variables, while the token stays in the environment only.
CI friendlyDesigned to drop into pipelines: token from environment, server/account overridable per job.

Install and configure

Build the binary and initialize a config:

go build -o faynosync-cli .
./faynosync-cli --log-level info init

init creates ~/.faynosync/config.yaml and prompts for server, owner, and tuf:

server: https://example.com
owner: example
tuf: false

View or update config fields:

faynosync config view
faynosync config set server https://updates.example.com
faynosync config set owner myteam

If a value is omitted, config set prompts for it.

Runtime settings priority

  • FAYNOSYNC_TOKEN is required and loaded only from the environment. It is sent as Authorization: Bearer <token>.
  • server is loaded from config and can be overridden by FAYNOSYNC_URL.
  • owner is loaded from config and can be overridden by FAYNOSYNC_ACCOUNT.

Commands

Global flag:

  • --log-level <level> where level is trace|debug|info|warn|error|fatal|panic (default: info)
CommandPurpose
faynosync initCreate ~/.faynosync/config.yaml and prompt for server, owner, tuf.
faynosync config viewPrint the current config.
faynosync config set <server|owner|tuf> [value]Update a config field (prompts if value is omitted).
faynosync upload [flags]Upload one or more files to <server>/upload as multipart/form-data.

upload flags

  • --app <name>
  • --file <path> (repeatable, at least one required)
  • --version <value>
  • --channel <value>
  • --platform <value>
  • --arch <value>
  • --updater <value>manual|velopack|squirrel_darwin|squirrel_windows|electron-builder|tauri|sparkle (validated locally)
  • --signature <value> — Tauri base64 signature
  • --publish[=true|false]
  • --critical[=true|false]
  • --intermediate[=true|false]
  • --changelog <text>
  • --changelog-file <path>
  • --changelog-stdin
Changelog modes are mutually exclusive

Use only one of --changelog, --changelog-file, or --changelog-stdin. For Markdown with special symbols, prefer --changelog-file or --changelog-stdin.

Uploading for a specific updater

The --updater flag selects the updater type the upload targets, so artifacts land in the correct isolated folder and the server generates the right feed files (RELEASES, *.yml, releases.{channel}.json, appcast.{channel}.xml). The CLI validates only that the value is a known updater type — all other rules are enforced server-side. When omitted, the platform's default updater is used.

The --signature flag passes the base64 signature required by the Tauri updater. Read it straight from the .sig file Tauri produces next to the artifact:

faynosync upload \
--app myapp \
--file ./myapp.app.tar.gz \
--version 1.0.0 \
--channel stable \
--platform darwin \
--arch amd64 \
--updater tauri \
--signature "$(cat ./myapp.app.tar.gz.sig)" \
--publish

Upload examples

faynosync upload \
--app test \
--file ./test.apk \
--version 1.2.3 \
--channel stable \
--platform android \
--arch universal \
--publish \
--critical \
--intermediate \
--changelog "Bugfixes"

# Multiple files in one upload
faynosync upload --file ./test.rpm --file ./test.deb --app myapp --publish=true

# Changelog from a file
faynosync upload --file ./test.rpm --app myapp --changelog-file ./CHANGELOG.md

# Changelog piped via stdin
cat ./CHANGELOG.md | faynosync upload --file ./test.rpm --app myapp --changelog-stdin

For changelogs with shell-sensitive characters, use a quoted heredoc delimiter ('EOF') so the shell does not attempt parameter expansion before the here-doc is formed:

faynosync upload \
--app=cli \
--file=./faynoSync-cli \
--version=0.0.0.1 \
--channel=nightly \
--platform=linux \
--arch=amd64 \
--publish \
--critical \
--intermediate \
--changelog-stdin <<'EOF'
# Changes
- fixed ! bug
- added ${feature}
EOF

CI / GitHub Actions

The CLI ships as a composite GitHub Action, published on the GitHub Marketplace. It downloads the prebuilt CLI binary matching the runner OS/arch, then runs faynosync upload — so no Go toolchain is needed on the runner.

- name: Upload to faynoSync
uses: ku9nov/faynoSync-cli@v1
with:
app: myapp
file: |
./dist/myapp.deb
./dist/myapp.rpm
version: 1.2.3
channel: stable
platform: linux
arch: amd64
publish: true
changelog-file: ./CHANGELOG.md
env:
FAYNOSYNC_TOKEN: ${{ secrets.FAYNOSYNC_TOKEN }}
FAYNOSYNC_URL: ${{ secrets.FAYNOSYNC_URL }}
FAYNOSYNC_ACCOUNT: ${{ secrets.FAYNOSYNC_ACCOUNT }}

Each input maps to the matching upload flag:

InputMaps toNotes
app--appRequired.
file--fileOne path per line — each becomes a separate --file.
version--version
channel--channel
platform / arch--platform / --arch
updater--updaterSee updater types.
signature--signatureTauri base64 signature.
publish / critical / intermediate--publish / --critical / --intermediateAdd the flag when true.
changelog / changelog-file--changelog / --changelog-fileMutually exclusive.
log-level--log-levelDefault info.
cli-versionWhich CLI release to download; defaults to the tag the action is pinned to.

Auth comes from the environment: FAYNOSYNC_TOKEN is required, and FAYNOSYNC_URL / FAYNOSYNC_ACCOUNT override the server / owner a config file would supply. Keep all three in secrets.

Pin to a floating major

Use @v1 to receive patches automatically, or an exact release like @v1.0.0 to pin. A full example workflow lives in examples/github-actions.

CI / Jenkins

For Jenkins there is a ready-to-use Shared Library step, faynosyncUpload, that wraps faynosync upload. It writes the changelog to a file (avoiding CI shell-escaping issues), runs the upload under a credentials binding, and marks the build unstable if the response does not contain Upload completed.

@Library('faynosync-shared') _

faynosyncUpload(
app: 'myapp',
version: "1.2.3.${env.BUILD_NUMBER}",
channel: 'stable',
file: './dist/myapp', // String or List of paths
platform: 'linux',
arch: 'amd64',
changelog: "Branch: ${env.BRANCH_NAME}",
credentialsId: 'faynosync-token-myapp',
publish: true,
)

The step reads the token from a Jenkins Secret text credential (via credentialsId) and exposes it to the CLI as FAYNOSYNC_TOKEN. Setup instructions and the full parameter list are in the Jenkins example.