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?
| Benefit | What it means in practice |
|---|---|
| Predictable uploads | A single, unified upload command instead of hand-crafted curl calls that break on CI-specific quoting. |
| Stable changelog input | Three mutually exclusive modes (--changelog, --changelog-file, --changelog-stdin) so Markdown and special characters survive shell escaping. |
| Config + env split | server 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 friendly | Designed 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_TOKENis required and loaded only from the environment. It is sent asAuthorization: Bearer <token>.serveris loaded from config and can be overridden byFAYNOSYNC_URL.owneris loaded from config and can be overridden byFAYNOSYNC_ACCOUNT.
Commands
Global flag:
--log-level <level>where level istrace|debug|info|warn|error|fatal|panic(default:info)
| Command | Purpose |
|---|---|
faynosync init | Create ~/.faynosync/config.yaml and prompt for server, owner, tuf. |
faynosync config view | Print 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
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:
| Input | Maps to | Notes |
|---|---|---|
app | --app | Required. |
file | --file | One path per line — each becomes a separate --file. |
version | --version | |
channel | --channel | |
platform / arch | --platform / --arch | |
updater | --updater | See updater types. |
signature | --signature | Tauri base64 signature. |
publish / critical / intermediate | --publish / --critical / --intermediate | Add the flag when true. |
changelog / changelog-file | --changelog / --changelog-file | Mutually exclusive. |
log-level | --log-level | Default 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.
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.