Skip to content
Merged
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Commands are organized into two product groups — `floci aws` (or bare `floci`)
| `floci config profile` | Manage named profiles |
| `floci config default-product` | Set the default product (aws or az) |
| `floci completion bash\|zsh` | Generate shell completion scripts |
| `floci update` | Update the CLI to the latest release |

### AWS commands (`floci` / `floci aws`)

Expand Down Expand Up @@ -393,6 +394,24 @@ floci completion bash >> ~/.bashrc
floci completion zsh >> ~/.zshrc
```

### `floci update`

Self-update the CLI in place: downloads the release binary from GitHub, verifies its
sha256 against the release's `sha256sums.txt`, and atomically replaces the running binary.

```sh
floci update # update to the latest release
floci update --check # exit 0: up to date, exit 1: update available
floci update --version 0.1.7 # install a specific version
```

Homebrew installs are managed by brew and are detected and refused — use `brew upgrade floci` there instead.

When run from an interactive terminal, floci also checks for new releases in the
background (at most once per 24h, cached in `~/.floci/update-check.json`) and prints a
hint before the command output when one is available. Set `FLOCI_NO_UPDATE_CHECK=1` to
opt out; the check is automatically disabled in CI and for piped output.

---

## CI Usage
Expand Down
34 changes: 32 additions & 2 deletions src/main/java/io/floci/cli/FlociCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.floci.cli.commands.snapshot.SnapshotCommand;
import io.floci.cli.config.GlobalConfigStore;
import io.floci.cli.output.Ansi;
import io.floci.cli.update.UpdateNotifier;
import picocli.CommandLine;
import picocli.CommandLine.*;

Expand All @@ -21,7 +22,8 @@
" floci env — print environment variables%n" +
" floci aws — explicit AWS emulator commands%n" +
" floci az — Azure emulator commands%n" +
" floci gcp — GCP emulator commands%n",
" floci gcp — GCP emulator commands%n" +
" floci update — Update the CLI to the latest release",
mixinStandardHelpOptions = true,
versionProvider = FlociCli.VersionProvider.class,
subcommands = {
Expand All @@ -41,7 +43,8 @@
AwsCommand.class,
AzCommand.class,
GcpCommand.class,
HelpCommand.class
HelpCommand.class,
UpdateCommand.class,
}
)
public class FlociCli implements Runnable {
Expand Down Expand Up @@ -81,10 +84,29 @@ public static void main(String[] args) {
}
}

// npm/gh-style update hint: announce a newer version (from the local cache — no
// network) before the command runs, and refresh that cache in the background at
// most once per 24h. Skipped for scripts/CI (non-interactive) and for 'update'
// itself, which already talks about versions.
boolean notify = UpdateNotifier.interactiveRunEnabled() && !isUpdateCommand(effectiveArgs);
if (notify) {
UpdateNotifier.pendingNotice(VersionCommand.CLI_VERSION).ifPresent(latest ->
System.err.println(Ansi.yellow("A new release of floci is available: "
+ VersionCommand.CLI_VERSION + " → " + latest + "\n"
+ "Run 'floci update' to install it") + "\n"));
UpdateNotifier.refreshInBackground();
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}

int exitCode = new CommandLine(new FlociCli())
.setExecutionExceptionHandler(new ExceptionHandler())
.setCaseInsensitiveEnumValuesAllowed(true)
.execute(effectiveArgs);

if (notify) {
// Fast commands would otherwise exit before the background check persists
// the cache for the next run.
UpdateNotifier.awaitRefresh(java.time.Duration.ofMillis(250));
}
System.exit(exitCode);
}

Expand All @@ -99,6 +121,14 @@ private static boolean isExplicitProduct(String[] args) {
return false;
}

private static boolean isUpdateCommand(String[] args) {
for (String arg : args) {
if (arg.startsWith("-")) continue;
return "update".equals(arg);
}
return false;
}

private static String[] prepend(String token, String[] args) {
String[] result = new String[args.length + 1];
result[0] = token;
Expand Down
Loading
Loading