Features
Updating ccam
ccam update downloads a ccam release from GitHub, checks it against the release's checksums.txt and replaces the running binary in one rename. Use it when you installed ccam with the install script or from an archive. ccam never checks for updates on its own: only this command contacts GitHub, so every other command works the same offline.
Usage
ccam update # install the latest release
ccam update --check # only say whether a newer release exists
ccam update --version v0.3.0 # install that release, also an older one
ccam --dry-run update # show what would be downloaded and replaced$ ccam update --check
ccam 0.5.0 is available (you have 0.4.0). Run `ccam update`.
$ ccam --dry-run update
Would update ccam 0.4.0 → 0.5.0:
download https://github.com/jhyunwoo/ccam/releases/download/v0.5.0/ccam_linux_amd64.tar.gz
verify with https://github.com/jhyunwoo/ccam/releases/download/v0.5.0/checksums.txt
replace /home/you/.local/bin/ccam
$ ccam update
→ downloading https://github.com/jhyunwoo/ccam/releases/download/v0.5.0/ccam_linux_amd64.tar.gz
✓ Updated ccam 0.4.0 → 0.5.0 (/home/you/.local/bin/ccam)When no newer release exists, ccam prints ccam 0.4.0 is up to date. and changes nothing. With --version and the version you already run, it prints ccam 0.4.0 is already installed.
Options
| Option | What it does | Default |
|---|---|---|
--check |
Only report whether a newer release exists | off |
--version <tag> |
Install this release tag, such as v0.3.0, newer or older than the current one |
latest |
--dry-run |
Honoured: looks up the latest tag (unless --version names one), runs the same checks on the binary, and prints the download, checksum and replace paths without downloading |
off |
--check and --version cannot be used together. The tag must look like vMAJOR.MINOR.PATCH, with an optional pre-release or build suffix; the leading v may be left out. Anything else fails, for example --version takes a release tag such as v0.3.0, not "1.2".
The environment variable CCAM_RELEASES_URL replaces https://github.com/jhyunwoo/ccam/releases, for a mirror or for tests. It must be laid out the same way: <url>/latest redirects to <url>/tag/<tag>, and assets are at <url>/download/<tag>/<asset>.
How it works
The code is in internal/update, which only ccam update imports. The launch path never calls it.
cli.updateCmdfinds the running binary withos.Executableand resolves symlinks withfilepath.EvalSymlinks. That real path is what gets replaced.- Without
--version,Updater.Latestrequests<releases>/latestwithout following the redirect, and reads the tag from theLocationheader (…/tag/v0.5.0). This uses the releases page instead of the GitHub API, so the API's limit of 60 requests an hour does not apply. The lookup has 10 seconds. parseVersionchecks the tag against a strict pattern. A tag ends up in download URLs and in a printed shell command, so nothing with/,%, spaces or shell characters gets through.compareVersionscompares it with the running version; a pre-release sorts before its release.managed()andwritable()decide whether ccam may replace the binary at all (see below).Updater.Fetchdownloadsccam_<os>_<arch>.tar.gzandchecksums.txtat the same time, with 2 minutes for both. Each download is capped at 256 MB.- The archive's SHA-256 must match its line in
checksums.txtbefore anything else happens. A missing line or a different checksum stops the update withnothing was changed. extract()reads the tar and takes the first regular file namedccam. Directories, links and other entries are ignored, and the binary is capped at 256 MB too.Updater.Installwrites the new binary withfsutil.AtomicWrite: a temporary file.ccam.tmp*next to the binary, with the old file's permission bits, flushed to disk and renamed over the old one.
The whole pipeline, from tag lookup to the rename:
The archive and checksums.txt are downloaded side by side, and nothing is written until the checksum matches. The rename is atomic, so a crash or a full disk leaves either the old binary or the new one, never half of one. Shortcut commands such as ~/.local/bin/ccwork are symlinks to the ccam binary, so they run the new version without any change.
Package-managed installs
A ccam installed from the .deb belongs to apt, and replacing it behind apt's back would leave the package database wrong. Before downloading, ccam checks who owns the binary:
- A binary in
/usr/bin,/bin,/usr/sbinor/sbincounts as package-managed. - Elsewhere, if
dpkgis installed anddpkg -S <path>succeeds, it also counts as package-managed. - Otherwise ccam creates and removes a temporary
.ccam-update-*file next to the binary to make sure it can write there.
For a package-managed binary ccam stops and prints the apt steps:
$ ccam update
ccam: /usr/bin/ccam was installed by a package, so `ccam update` leaves it alone.
To update the .deb, download ccam_0.5.0_linux_amd64.deb from https://github.com/jhyunwoo/ccam/releases/tag/v0.5.0 and run:
sudo apt install ./ccam_0.5.0_linux_amd64.deb
Or install a copy in ~/.local/bin with the install script:
curl -fsSL https://raw.githubusercontent.com/jhyunwoo/ccam/main/install.sh | shOn systems other than Linux, only the install script line is printed. When the folder is not writable, ccam suggests rerunning the install script, which installs ccam in ~/.local/bin.
Safety and edge cases
- A development build reports its version as
devand has no release to compare with, soccam updateandccam update --checkrefuse it.ccam update --version vX.Y.Zinstalls a release over it. - A failed download, a missing asset for your platform, or a checksum mismatch leaves the installed binary as it was.
- A release that does not exist fails with
there is no release <tag>. When GitHub refuses with HTTP 403 or 429, ccam says it is probably a rate limit and asks you to try again later. --checkonly reads. It does not check whether the binary is package-managed or writable.--versionwith an older tag is how you roll back.