Help us shape the future of Go! Take the 2026 Go Developer Survey

sync-assign

command module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 5 Imported by: 0

README

sync-assign

sync-assign copies assignments from a teacher-owned Git repository into a student Git repository. The teacher maps assignment IDs to top-level directories; a student syncs one assignment at a time.

[!WARNING] Synchronization is one-way: teacher files are copied into the student repository. With clean mode enabled, an existing assignment directory is removed and replaced. Uncommitted student work blocks replacement unless --force is also used.

Requirements and supported platforms

  • Git available on PATH
  • Access to the configured teacher repository
  • A student Git repository; commands must run from its root
  • uv available on PATH when checking or grading Python or Jupyter assignments

Release binaries are built for macOS on Apple silicon (darwin/arm64) and Linux on x86-64 (linux/amd64).

Install

Review install.sh, then run:

curl -fsSL https://raw.githubusercontent.com/ludanortmun/sync-assign/main/install.sh | zsh

The installer selects the matching release, verifies its SHA-256 checksum, and installs sync-assign in ~/.local/bin without sudo. Set SYNC_ASSIGN_INSTALL_DIR to install somewhere else.

Install with Go

If Go 1.26.6 or newer is installed, build and install the latest version with:

go install github.com/ludanortmun/sync-assign@latest

Go installs the binary in $GOBIN, or in $(go env GOPATH)/bin when GOBIN is unset. Ensure that directory is included in PATH.

Teacher repository

Place each assignment in a single top-level directory and add sync-assign.yml at the repository root:

teacher-repository/
|-- sync-assign.yml
|-- lab-1/
|   |-- README.md
|   `-- starter.go
`-- project/
    `-- README.md
assignments:
  lab-1: lab-1
  final-project:
    path: project
    archetype: jupyter

Each key is the assignment ID students pass to the CLI. A scalar value is the name of one top-level directory and leaves the archetype unset; it can be synced, but not checked or graded. A mapping supplies path plus an archetype of python or jupyter. Assignment paths must be single top-level directory names; absolute and nested paths are rejected. The default teacher branch is main.

Student setup

From the root of a student Git repository:

sync-assign init-student https://github.com/example/course-assignments.git

If the terminal is interactive, omitting the repository argument prompts for it. The command creates .sync-assign.yml and refuses to overwrite an existing file unless --force is supplied. Pass --config=PATH to create it elsewhere.

teacher-repository: https://github.com/example/course-assignments.git
commit: true
clean: false
ephemeral: false
branch: main

The exact supported student configuration keys are:

Key Meaning
teacher-repository Required teacher Git URL or local repository path.
commit Create a local commit after a successful sync. Default: true.
clean Permit replacement of an existing assignment. Default: false.
teacher-path Use this local path for the teacher mirror.
ephemeral Clone into a temporary directory and remove it afterward. Default: false.
skip-mirror Compatibility alias for ephemeral; if both are set, their values must agree.
branch Teacher branch to clone or update. Default: main.

teacher-path and an enabled ephemeral mode cannot be used together. Unknown YAML keys are rejected.

Sync an assignment

sync-assign lab-1

The assignment ID is looked up in the teacher's sync-assign.yml. Command-line options override .sync-assign.yml for that invocation.

Sync flags
Flag Behavior
--config=PATH Read the student configuration from another path. Relative paths are resolved from the student repository root.
--[no-]commit Enable or disable the local commit after syncing.
--[no-]clean Enable or disable replacement of an existing assignment.
--force Allow clean mode to replace an assignment that has uncommitted changes. Requires clean mode.
--mirror-path=PATH Override the local teacher mirror path and disable ephemeral mode.
--[no-]ephemeral Enable or disable a temporary teacher clone; enabling it clears a configured mirror path.
--branch=BRANCH Override the teacher repository branch.
-m, --message=TEXT Set the commit message; the default is Sync assignment <id>.
--version Print version information and exit.
-h, --help Show help.

Without clean mode, syncing fails if the target assignment directory already exists. Clean mode replaces that whole directory, but first refuses to proceed if Git reports tracked or untracked changes within it. --force bypasses only that dirty-worktree protection and is invalid unless clean mode is enabled.

By default, a successful sync stages changes under the assignment directory and creates a local commit containing only that directory. Unrelated staged changes remain in the index, and the sync does not push the commit. Set commit: false or pass --no-commit to opt out.

init-student flags
sync-assign init-student [<teacher-repo>] [flags]

--[no-]commit, --[no-]clean, --mirror-path, --[no-]ephemeral, and --branch write the corresponding defaults to .sync-assign.yml, or the path selected by --config. --force overwrites an existing student configuration.

Check an assignment before committing

sync-assign check lab-1

check runs the same archetype-specific checker pipeline and produces the same live, colored report as grade, but evaluates the current working directory instead of a historical commit. Modified and untracked files are included, so students can validate their work before committing and pushing.

The command must run from the student Git repository root. It does not create a commit or modify the working tree.

Check flags
Flag Behavior
--config=PATH Read the student configuration from another path. Relative paths are resolved from the student repository root.
--mirror-path=PATH Override the local teacher mirror path and disable ephemeral mode.
--[no-]ephemeral Enable or disable a temporary teacher clone; enabling it clears a configured mirror path.
--teacher-branch=BRANCH Override the teacher repository branch.
-h, --help Show help.

Grade an assignment

sync-assign grade lab-1 --due=2026-09-18

Grading selects the latest commit on the requested student branch at or before the due date and evaluates it in a detached temporary worktree. The branch defaults to the currently checked-out branch. --due accepts RFC3339 (including its explicit offset) or YYYY-MM-DD; a date-only value means the end of that day in the machine's local time zone. When omitted, the current time is used.

Grade flags
Flag Behavior
--due=DATE Optional cutoff in RFC3339 or YYYY-MM-DD form; defaults to the current time.
--mount=SOURCE:DESTINATION Symlink a local directory into the assignment in the grading worktree; repeat for multiple mounts.
--keep-worktree Preserve the grading worktree after the run and print its path.
--config=PATH Read the student configuration from another path. Relative paths are resolved from the student repository root.
--branch=BRANCH Select the student branch; defaults to the current branch.
--pull Update from origin first. The checked-out branch is fetched and fast-forwarded only; another local branch is updated directly by fetch, which refuses non-fast-forward updates.
--mirror-path=PATH Override the local teacher mirror path and disable ephemeral mode.
--[no-]ephemeral Enable or disable a temporary teacher clone; enabling it clears a configured mirror path.
--teacher-branch=BRANCH Override the teacher repository branch.
-h, --help Show help.

A single teacher-owned configuration can be reused while grading repositories stored as sibling directories:

students/
|-- .sync-assign.yml
|-- alan/
|-- beth/
|-- carl/
`-- diego/

Run the grade command from each student's Git repository root and point it at the shared file:

cd students/alan
sync-assign grade lab-1 --due=2026-09-18 --config=../.sync-assign.yml

To make local assets available in the temporary grading worktree, repeat --mount=SOURCE:DESTINATION for each directory:

sync-assign grade lab-1 --due=2026-09-18 \
  --mount=./lab-1/data:data \
  --mount=/srv/datasets/models:assets/models

Relative sources resolve from the student repository root; absolute sources and sources outside the repository are supported. A source must be an existing directory, and source symlinks are resolved. Destinations resolve from the assignment directory inside the temporary worktree. Missing parent directories are created, but an existing destination (including a dangling symlink) causes an error. Destinations must be relative, cannot target the assignment root or contain .. or .git components, and cannot have symlink or file parents. Duplicate and overlapping destinations are rejected.

Mounts are directory symlinks to live assets: their contents reflect the current local files regardless of --due, and writes through a mount affect its source. Cleanup removes temporary links and preserves the source directories. Mounts apply only to that grading invocation; unmounted ignored files remain absent. Use explicit source and destination paths, without mount-mode suffixes such as :ro. The final colon separates the paths, and destinations cannot contain colons. Quote the entire flag value when paths contain spaces.

The grade report lists resolved source directories and assignment-relative destinations under mounts: alongside the branch, commit, and due date, before the checks. When no mounts are specified, it prints mounts: none.

Use --keep-worktree to inspect the staged assignment and generated files after grading. The command prints retained worktree: PATH as soon as the worktree is created and keeps it even if a check or mount setup fails. Mounted directories remain live symlinks. The teacher mirror still follows its usual cleanup rules. Without this flag, the grading worktree is removed as usual.

When finished, run git worktree remove --force PATH from the student repository and remove the now-empty .sync-assign-grade-* parent directory.

The current teacher mirror is the integrity baseline, even when grading an older student commit. Assignments without an archetype are not gradable. Checks run in this order:

  • python: main Python tests, extra-credit Python tests, then integrity checks for every teacher-supplied file under a tests directory and every test_*.py.
  • jupyter: main and extra-credit Python tests when a tests/ directory exists (otherwise both checks are skipped), main and extra-credit notebook tests, cleared notebook output/execution counts, an ordered teacher-cell subsequence check, then the same test-file integrity check.

Python tests run through uv run with pytest; notebook tests use pytest --nbmake with nbmake. If the assignment has requirements.txt, uv also loads it with --with-requirements; pytest and nbmake are supplied with uv's --with options. Files ending in extra_credit.py are excluded from the main Python test check and run by a separate extra-credit check. Pytest runs in quiet mode without tracebacks, and failed checks report only pytest's short test summary. Notebooks matching notebooks/*_extra_credit.ipynb are likewise excluded from the main notebook test check and run by a separate notebook extra-credit check.

Each check is printed as soon as it starts and again when it completes. Running checks use the default terminal color, successful checks are green, skipped checks are yellow, and failed checks are written to standard error in red.

Teacher mirror behavior

By default, the teacher repository is kept as a persistent mirror and updated from its configured branch on every sync. Its path is deterministic:

$XDG_CACHE_HOME/sync-assign/<sha256-of-teacher-repository>

If XDG_CACHE_HOME is unset, the operating system's user cache directory is used. XDG_CACHE_HOME, when set, must be an absolute path.

Set teacher-path or pass --mirror-path to choose a persistent location. The path is cloned when absent; an existing path must be a Git worktree.

Set ephemeral: true or pass --ephemeral to clone the teacher repository into a temporary directory for that invocation. The temporary mirror is removed when synchronization finishes.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
config
Package config loads and validates sync-assign configuration.
Package config loads and validates sync-assign configuration.
gitcmd
Package gitcmd provides the git command boundary used by sync-assign.
Package gitcmd provides the git command boundary used by sync-assign.
grader
Package grader evaluates student assignments with an ordered set of checks.
Package grader evaluates student assignments with an ordered set of checks.
mirror
Package mirror manages local mirrors of assignment repositories.
Package mirror manages local mirrors of assignment repositories.
syncer
Package syncer applies assignments from a mirror to a student's repository.
Package syncer applies assignments from a mirror to a student's repository.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL