mirror of
https://github.com/harvard-edge/cs249r_book.git
synced 2026-07-18 17:03:56 -05:00
The dashboard did not reflect CLI progress for users across Windows and Ubuntu. Three independent client defects, now fixed: 1. Automatic sync was silently skipped on any non-TTY shell. _trigger_submission gated on `not sys.stdin.isatty()`, which is False on Windows Git Bash/MinTTY and many IDE terminals even when interactive, so `tito module complete` updated local progress.json but never uploaded, with no message. Decouple "should we sync" (logged-in and not CI) from "should we prompt" (needs a TTY): non-interactive real users now sync without a prompt instead of being skipped. 2. A 2xx response with synced_modules null/0 was reported as success using the local count, hiding backend failures. sync_progress now returns a SyncResult and reports an honest accepted-but-unconfirmed warning when the server does not confirm persistence. 3. No standalone resync path and login did not sync, so modules completed before logging in never reached the dashboard. Add `tito community sync` and an offer to sync existing progress after login. The sync trigger decision now lives in one shared helper (auto_sync_after_completion) used by the module, milestone, and login paths, plus a small core/runtime.py that owns is_ci()/is_interactive(). Adds tests/cli/test_progress_sync.py (15 tests) covering the non-TTY regression, the CI/not-logged-in branches, honest 2xx interpretation, and the new command.
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""
|
|
Runtime environment detection for the TinyTorch CLI.
|
|
|
|
Single source of truth for two *separate* questions that the rest of the CLI
|
|
must never conflate:
|
|
|
|
1. ``is_ci()`` -- are we running inside automation (CI, a pipeline)?
|
|
2. ``is_interactive()`` -- can we safely prompt the user and read an answer?
|
|
|
|
Why these are separate
|
|
----------------------
|
|
A previous version gated progress *syncing* on ``sys.stdin.isatty()``. That
|
|
silently broke sync for real students on Windows Git Bash / MinTTY, where the
|
|
shell pipes stdin to ``python.exe`` and ``sys.stdin.isatty()`` returns ``False``
|
|
*even in an interactive terminal*. The same happens in some IDE-integrated
|
|
terminals on any OS.
|
|
|
|
The lesson encoded here: "can I prompt?" is NOT the same as "should I do the
|
|
network action?". Whether to *prompt* depends on having a usable TTY; whether to
|
|
*sync* depends only on being a real (non-CI) user who is logged in. Callers
|
|
decide whether to prompt with ``is_interactive()`` and decide whether to act
|
|
with ``is_ci()`` -- never the other way around.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
# Environment variables set by common CI / automation providers.
|
|
_CI_ENV_VARS = (
|
|
"CI", # generic; set by GitHub Actions, GitLab, CircleCI, Travis, ...
|
|
"GITHUB_ACTIONS",
|
|
"GITLAB_CI",
|
|
"JENKINS_URL",
|
|
"BUILDKITE",
|
|
"TF_BUILD", # Azure Pipelines
|
|
"TEAMCITY_VERSION",
|
|
)
|
|
|
|
|
|
def is_ci() -> bool:
|
|
"""Return ``True`` when running in a CI / automation environment.
|
|
|
|
Used to suppress *all* interactive behavior and any "helpful" background
|
|
network calls (like auto-syncing progress) that have no place in automation.
|
|
"""
|
|
return any(os.environ.get(var) for var in _CI_ENV_VARS)
|
|
|
|
|
|
def is_interactive() -> bool:
|
|
"""Return ``True`` only when it is safe to prompt the user.
|
|
|
|
Requires a real console on **both** stdin and stdout, and that we are not in
|
|
CI. This is intentionally conservative: when it returns ``False`` the caller
|
|
should proceed *without* a prompt (e.g. auto-confirm), not skip the action.
|
|
|
|
See the module docstring for why this must never be used to decide whether
|
|
to perform a network sync.
|
|
"""
|
|
if is_ci():
|
|
return False
|
|
try:
|
|
return bool(sys.stdin.isatty() and sys.stdout.isatty())
|
|
except (AttributeError, ValueError):
|
|
# Streams can be replaced (e.g. by test harnesses) or closed.
|
|
return False
|