Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## Unreleased

### Features

- Passive update check. On a normal `lam` run (a query or the REPL),
`lam` checks at most once a day whether a newer release exists and, if
so, prints a one-line notice to stderr with the upgrade command for how
it was installed (`brew upgrade lambe`, `scoop update lambe`,
`dart pub global activate lambe`, or re-running the installer). The
check is cached, never blocks the query, and never writes to stdout. It
is off automatically in CI and when stderr is not a terminal, and can
be disabled with `--no-update-check` or `LAM_NO_UPDATE_CHECK`. Lives
entirely in the CLI harness — the library stays `dart:io`-free.

## 0.13.0

Distribution and install ergonomics: this release is about how people
Expand Down
66 changes: 66 additions & 0 deletions bin/lam.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/// `cat data.json | lam 'expression'`
library;

import 'dart:async';
import 'dart:convert';
import 'dart:io';

Expand All @@ -14,6 +15,7 @@ import 'package:lambe/lambe.dart';
import 'completions.dart';
import 'repl.dart' show runRepl;
import 'schema_io.dart';
import 'update_check.dart';

void main(List<String> arguments) {
final argParser =
Expand Down Expand Up @@ -125,6 +127,16 @@ void main(List<String> arguments) {
negatable: false,
help: 'Print the Lambë version and exit',
)
..addFlag(
'update-check',
defaultsTo: true,
help:
'On a normal run, check for a newer lam release and print a '
'one-line notice to stderr. Cached daily, never blocks the '
'query. Disable with --no-update-check or by setting '
'LAM_NO_UPDATE_CHECK; off automatically in CI and when '
'stderr is not a terminal.',
)
..addFlag('help', abbr: 'h', negatable: false, help: 'Show usage');

final ArgResults args;
Expand Down Expand Up @@ -261,6 +273,22 @@ void main(List<String> arguments) {
}
}

// Passive update notice — fires only for the interactive query path
// (a normal `lam EXPR file` run or the REPL). Suppressed for
// machine-consumed and scripted/static modes: ndjson streaming,
// -n/--null-input, --assert, --print-shape, and --explain*. (--version,
// --skill, --completions already returned above.) Synchronous cache
// read; any network refresh is fire-and-forget for the next run.
_maybeNotifyUpdate(
args,
suppressedMode:
isNdjsonMode ||
nullInput ||
isAssertMode ||
isPrintShapeMode ||
isExplainMode,
);

if (isNdjsonMode) {
if (isInteractive) {
stderr.writeln('Error: --ndjson cannot be combined with --interactive.');
Expand Down Expand Up @@ -738,6 +766,44 @@ bool _looksLikeDataFile(String path) {
return false;
}

/// Emit a one-line "newer lam available" notice to stderr when every
/// gate allows it. The decision is a synchronous cache read; a stale
/// cache schedules a fire-and-forget network refresh for the next run
/// (never awaited, so it can't block or delay this run).
void _maybeNotifyUpdate(ArgResults args, {required bool suppressedMode}) {
final env = Platform.environment;
if (!shouldCheck(
stderrIsTerminal: stderr.hasTerminal,
optedOutByEnv: env.containsKey('LAM_NO_UPDATE_CHECK'),
// `--update-check` defaults true; `--no-update-check` makes it false.
optedOutByFlag: !args.flag('update-check'),
isCi: env.containsKey('CI'),
suppressedMode: suppressedMode,
)) {
return;
}

final store = FileCacheStore(resolveCachePath(env));
final now = DateTime.now();
final decision = decideNotice(
currentVersion: lambeVersion,
store: store,
channel: detectChannel(Platform.resolvedExecutable),
now: now,
);
if (decision.stale) {
unawaited(
refreshCache(
store: store,
fetch: defaultFetcher(currentVersion: lambeVersion),
now: now,
),
);
}
final notice = decision.notice;
if (notice != null) stderr.writeln(notice.render());
}

/// Print usage information to stderr.
void _usage(ArgParser parser) {
stderr.writeln('Usage: lam [options] <expression> [file]');
Expand Down
Loading