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-version—Which 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.