Deploy process details
This page documents the current RobotPy deploy implementation for SystemCore. It is intended for developers who need to understand or manually debug deploy failures.
The robotpy command itself is provided by
robotpy-cli, but the actual deploy
implementation lives in
robotpy-installer. Useful
entry points when reading the code are:
Overview
At a high level, deploy does the following:
python -m robotpydiscovers thedeploysubcommand from therobotpy_cli.YEARentry point group.robotpy-installeroptionally runs local tests.It optionally validates the project’s
pyproject.tomlrequirements against the local environment.It resolves the robot address and opens SSH as
systemcore.It ensures the robot has the expected Python runtime and the required Python packages.
It writes
/home/systemcore/robotCommand.It uploads code to
/home/systemcore/py_newvia SFTP.It atomically swaps
py_newinto/home/systemcore/py.It byte-compiles the deployed tree and restarts the
robotsystemd service.
Deploy pipeline
Command entry
python -m robotpy (or robotpy) loads subcommands from entry points.
If a subcommand requests main_file or project_path, robotpy-cli
passes the selected robot file and its parent directory into the command.
Deploy therefore operates relative to the directory containing the selected
robot main file.
If you need to create your own robotpy subcommands, see the
robotpy-cli README.
It documents how subcommands are discovered from the robotpy_cli.YEAR entry
point group and the class structure they must implement.
Deploy also refuses to run if the project path is the user’s home directory. This prevents accidental uploads of an entire home directory.
Local test execution
Unless --skip-tests is specified, deploy runs:
python -m robotpy --main <robot file> test
If tests fail, deploy aborts unless the user explicitly confirms that it should continue anyway. This means that a deploy failure can happen before any robot connection or file transfer occurs.
Project requirement checks
Unless --no-install is used, deploy loads pyproject.toml and reads the
[tool.robotpy] section. It logs the required package set and, unless
--no-verify is used, verifies that the local environment satisfies those
requirements.
If local packages do not match the project requirements, deploy aborts before connecting to the robot. The usual recovery is to run:
python -m robotpy sync
Robot resolution and SSH
Deploy connects as:
username:
systemcorepassword:
systemcore
Connection settings are loaded from .wpilib/wpilib_preferences.json in the
project directory. The file is managed by
wpilib_preferences.py
and stores teamNumber and/or robotHostname.
The address source is, in order:
--robotor--teamif specifiedthe saved values in
.wpilib/wpilib_preferences.jsonan interactive prompt if neither exists
If deploy resolves by team number, RobotFinder races a small set of likely
addresses and uses the first one that accepts a TCP connection on port 22. The
current probes are:
10.TE.AM.2robot.local172.28.0.1172.30.0.1
If a hostname matches an SSH alias in ~/.ssh/config, or if --no-resolve
is used, deploy skips its own DNS resolution and lets SSH handle the hostname.
Preflight behavior on connect
RobotpyInstaller.connect_to_robot calls ensure_image_version and logs
robot disk and memory usage before and after deploy.
Note
ensure_image_version currently returns immediately, so the image-version
check is effectively a no-op even though the CLI still exposes
--ignore-image-version.
Requirement preparation on the robot
Before copying user code, deploy may modify the robot runtime environment:
It removes Java/C++ user programs if present.
It checks for
/home/systemcore/.python/bin/python3.It verifies that the robot Python major/minor version matches the installer’s expected version.
It checks whether the robot already has the packages required by the project.
If Python is missing or has the wrong version, deploy installs or replaces the
runtime under /home/systemcore/.python. The Python archive is not
downloaded on the robot. It must already exist in the local installer cache.
If project packages are missing, deploy creates a venv at
/home/systemcore/venv and installs packages into it from the local RobotPy
cache. The robot does not use the public internet for this step; the installer
starts a temporary local cache server and points pip on the robot at that
server.
The package install path uses a pip stub to force SystemCore-compatible platform
markers such as linux-systemcore and platform.machine() == 'systemcore'.
That behavior lives in
_pipstub.py.
File staging and upload
The deploy command kills the running robot program before starting the copy.
It then writes a new command line to /home/systemcore/robotCommand.
Normal deploy writes a command equivalent to:
/home/systemcore/venv/bin/python3 -u -O -m robotpy --main /home/systemcore/py/robot.py run
Debug deploy writes a command equivalent to:
/home/systemcore/venv/bin/python3 -u -m robotpy --main /home/systemcore/py/robot.py -v run
The exact main file name depends on the file passed via --main.
User code is not copied directly into /home/systemcore/py. Instead deploy:
removes any stale
/home/systemcore/py_newcopies the project into a temporary local directory
writes
deploy.jsoninto that temporary treeuploads the temporary tree as
/home/systemcore/py_newvia SFTPreplaces
/home/systemcore/pywithpy_newon the robot
This ensures if a deploy is interrupted, the previous robot code is still available on the robot.
The upload step intentionally skips some files and directories:
directories starting with
.__pycache__ctre_simvenvfiles ending in
.pyc,.whl,.ipk,.zip,.gz, or.wpilogfiles whose filename starts with
.
Large files are also checked before upload. By default deploy warns about files
larger than 250000 bytes unless --large is specified.
Activation
After the swap, deploy runs:
python -m compileallagainst/home/systemcore/pysudo syncsudo systemctl enable robotsudo systemctl start robot
Stopping the robot uses:
sudo systemctl stop robot
That command is shared with other maintenance commands such as undeploy.
Important files and directories
Local development machine
These paths are the most useful when debugging package/download issues:
.wpilib/wpilib_preferences.json~/wpilib/YEAR/robotpy~/wpilib/YEAR/robotpy/pip_cache~/wpilib/YEAR/robotpy/pkg_cache
pip_cache contains downloaded wheels used for robot package installation.
pkg_cache contains the cached Python runtime archive and related artifacts.
Robot
These are the important robot-side paths:
/home/systemcore/.python: installed standalone Python runtime/home/systemcore/venv: venv used to install RobotPy and project packages/home/systemcore/robotCommand: command consumed by the robot service/home/systemcore/py: active deployed code/home/systemcore/py_new: staging directory during deploy/home/systemcore/py/deploy.json: deploy metadata/home/systemcore/*.jar: Java user program artifacts removed during cleanup/home/systemcore/frcUserProgram: C++ user program artifact removed during cleanup
Deploy metadata
Deploy writes deploy.json into the uploaded tree. It contains:
{
"git-desc": "2022.1-8-gb4fc2aca-dirty",
"git-hash": "b4fc2aca399810f1fe28faf23314cd422a6db920",
"git-branch": "feat/working_code",
"deploy-host": "MyLaptop",
"deploy-user": "me",
"deploy-date": "2018-6-10T02:40:55",
"code-path": "/home/me/robots/MyRobotCode"
}
Git fields are included only if deploy is running inside a git repository and
git is available locally.
To fetch this file without SSHing manually, use:
python -m robotpy deploy-info
That command simply connects over SSH and prints the contents of
/home/systemcore/py/deploy.json if it exists.
Example code:
#!/usr/bin/env python3
import os
import json
import wpilib.deployinfo
class MyRobot(wpilib.TimedRobot):
def __init__(self):
data = wpilib.deployinfo.getDeployData()
print(data)
Manual debugging cookbook
SSH access
Start by SSHing to the robot as systemcore:
ssh systemcore@<robot-hostname-or-ip>
If name resolution is suspicious, connect to the raw address that deploy found.
Is the service running?
sudo systemctl status robot
sudo journalctl -u robot -n 200 --no-pager
Use these first when the driver station reports No Robot Code or when deploy
appears to succeed but nothing starts.
What command will the robot service run?
cat /home/systemcore/robotCommand
Deploy updates this file before the SFTP upload. If the main filename or Python path is wrong, this file is the first thing to inspect.
What code actually got deployed?
ls -al /home/systemcore
ls -al /home/systemcore/py
find /home/systemcore/py -maxdepth 2 -type f | sort
cat /home/systemcore/py/deploy.json
If a deploy was interrupted, also inspect:
ls -al /home/systemcore/py_new
If py_new exists but py was not replaced, the failure happened after the
upload but before activation completed.
Is Python installed?
/home/systemcore/.python/bin/python3 --version
/home/systemcore/.python/bin/python3 -c "import sys; print(sys.executable)"
If this path is missing, deploy could not install the standalone Python runtime, or the runtime was manually removed.
Does the venv exist?
/home/systemcore/venv/bin/python3 --version
/home/systemcore/venv/bin/python3 -c "import sys; print(sys.prefix)"
If the runtime exists but the venv does not, package installation probably never ran or the venv was cleared during recovery.
What packages are installed in the venv?
This matches the metadata-based query used by robotpy-installer:
/home/systemcore/venv/bin/python3 -c "from importlib.metadata import distributions; import json, sys; json.dump({dist.name: dist.version for dist in distributions()}, sys.stdout, indent=2, sort_keys=True)"
For a quick package list, you can also run:
/home/systemcore/venv/bin/python3 -m pip list
Can I run the code manually?
Yes, but stop the service first so two copies are not fighting each other:
sudo systemctl stop robot
cat /home/systemcore/robotCommand
Then copy the printed command and run it manually. For interactive debugging, using the debug-style command is often more useful:
/home/systemcore/venv/bin/python3 -u -m robotpy --main /home/systemcore/py/robot.py -v run
If your robot entry file is not robot.py, substitute the actual file named in
robotCommand.
Useful local-side commands
These commands are often enough to explain a failed deploy without touching the robot filesystem directly:
python -m robotpy deploy-info
python -m robotpy installer cache location
python -m robotpy installer cache list
python -m robotpy sync
If Python itself is missing on the robot and you want to repair that separately from a full deploy, use:
python -m robotpy installer install-python
Notes
A few details that are easy to forget when debugging deploy internals:
The robot-side package install uses cached wheels and a temporary local HTTP server, not a direct pip install from the public internet.
Direct URL or VCS requirements may need cached wheels produced by
robotpy syncbefore deploy can install them.deploy-inforeads the deployed metadata file over SSH; it does not inspect local state.The current implementation writes only
robotCommand. Older references torobotDebugCommandare obsolete.