Remote Build
The remote build feature allows you to run build commands on a remote machine via SSH. This is useful when:
The build requires software not installed locally (e.g., Vivado for FPGA synthesis)
The build is resource-intensive and you want to offload it to a server
You need to run builds on a specific machine with licensed software
Basic Usage
Import the run_remote_build function from ygrader:
from ygrader import run_remote_build, RemoteBuildError
Then call it in your grading callback:
def grading_handler(lab_name, student_code_path, repo_url, tag, **kwargs):
run_remote_build(
remote_host="server.example.com",
remote_work_path="/tmp/grading",
repo_url=repo_url,
tag=tag,
commands=[
("lab_tools/adder", ["make", "sim"]),
],
files_to_copy=["lab_tools/adder/output.txt"],
student_code_path=student_code_path,
)
How It Works
The run_remote_build function performs these steps:
SSH Connection Test: Verifies SSH connectivity to the remote host
Clone Repository: Clones the student’s repo at the specified tag on the remote machine
Run Commands: Executes each command in sequence from the specified directory
Copy Files Back: Uses SCP to copy specified output files back to the local machine
Cleanup: Removes the remote clone (unless
cleanup=False)
Parameters
Parameter |
Type |
Description |
|---|---|---|
|
str |
SSH hostname (e.g., |
|
str |
Base path on remote machine for cloning (e.g., |
|
str |
Git repository URL to clone |
|
str |
Git tag or branch to checkout |
|
List[Tuple[str, List[str]]] |
List of (relative_path, command_list) tuples |
|
List[str] |
File paths relative to repo root to copy back |
|
Path |
Local directory to copy files into |
|
bool |
Delete remote clone after completion (default: |
|
bool |
Create username subdirectory under work path (default: |
|
str |
Shell command(s) to run before each command (e.g., |
|
TextIO |
File object for output (default: |
Example: FPGA Synthesis with Vivado
def grading_handler(lab_name, student_code_path, repo_url, tag, output=None, **kwargs):
print("Running remote build for FPGA synthesis...", file=output)
run_remote_build(
remote_host="fpga-server.example.com",
remote_work_path="/nvme/grading",
repo_url=repo_url,
tag=tag,
commands=[
("lab_tools/logic_functions", ["make", "implement"]),
],
files_to_copy=["lab_tools/logic_functions/work"],
student_code_path=student_code_path,
env_setup="source /tools/Xilinx/Vivado/2024.1/settings64.sh",
output=output,
)
SSH Configuration
The remote build uses SSH with these options:
BatchMode: Fails if password is required (use SSH keys)
StrictHostKeyChecking=accept-new: Automatically accepts new host keys
ConnectTimeout=10: 10 second connection timeout
X11 forwarding disabled: Prevents X11 errors
Setting Up SSH Keys
Ensure you have passwordless SSH access to the remote host:
# Generate SSH key if you don't have one
ssh-keygen -t ed25519
# Copy public key to remote server
ssh-copy-id user@server.example.com
# Test connection
ssh user@server.example.com echo "Connected!"
Error Handling
The function raises RemoteBuildError on failure:
from ygrader import run_remote_build, RemoteBuildError
try:
run_remote_build(...)
except RemoteBuildError as e:
print(f"Remote build failed: {e}")
# e.command_output contains the command's stdout/stderr
Username Subdirectory
By default, use_username_subdir=True creates a subdirectory named after the SSH user under remote_work_path. This prevents conflicts when multiple TAs grade simultaneously:
/tmp/grading/
├── ta1/
│ └── student-repo/
└── ta2/
└── student-repo/
Set use_username_subdir=False to clone directly in remote_work_path.
Output Logging
All SSH and SCP commands are logged with prefixes:
[SSH]- Remote commands[SCP]- File copy operations
When using parallel build mode, output goes to per-student log files automatically.