GitHub Workflow
Twenty-four people are working in one repository this semester. That is unusual for a course and normal for a job. It works only if everyone follows the same routine, so read this once carefully and then keep it open for the first few weeks.
One repository
All six teams share a single repository in the course GitHub organization.
The main branch is protected.
Nothing reaches main except through a pull request that has been reviewed by another team and merged by me.
You cannot push to main, and you cannot force push anywhere.
I can push to main, because that is how I merge, so if something lands there that looks wrong, tell me rather than assuming it was meant.
This is not distrust; it is what many projects you will work in after graduation look like.
What lives where
Each team owns one directory and does not edit any other.
pyproject.toml provided; makes the code installable as one package
src/planner/
core/ provided; do not edit
catalog/ one directory per team
requirements/
plans/
advising/
preferences/
scheduling/
web/ front ends, one directory per team, same six names
scripts/ build and check scripts
docs/ reference material and the core schema in DBML
.githooks/ the checks below, run on your machine
Everything under src/planner/ is one Python package, and each team's directory is a subpackage of it.
You install it once in a virtual environment, and you turn on the local checks with one git config line.
The repository's own README.md has the exact commands, including the two that differ on Windows, where the course uses Git Bash throughout.
Every version is pinned, so all 24 of us run the same code and a bug on your machine is a bug on mine. When I change a version, which I will do now and then between assignments, you re-run the install and your environment follows.
After the install, every import is absolute and says where the code came from.
from planner.core import Person, Course
from planner.advising.models import Recommendation
Every directory in the package has an __init__.py, including yours.
If you add a directory, add one, or Python will not find your code.
Inside your team's directory, each deliverable has a home:
README.mdholds your requirements and use cases from GP1.schema.dbmlandschema.pnghold your table design from GP2, kept up to date from then on.models.pyholds your SQLModel classes, andload.pyyour data generation, from GP3.queries.sqlholds your analytical queries from GP4.router.pyholds your API endpoints from GP5.
The front end your team generates for GP6 goes in web/ under your area's name, not in your feature directory.
Because each team writes in its own directory, two teams almost never touch the same file. Within a team you will, so divide the work by table or by endpoint and say who has which.
Do not edit another team's directory
If you need something another team owns, ask them for it. Changing their code to make yours work is the fastest way to break something you do not understand.
Branches
I'll create the branches.
At the start of each assignment I'll cut one branch per team from the current main, named for the team and the deliverable.
For example: advising/gp2 or scheduling/gp4.
The branches appear when the assignment starts, so a branch you do not see yet is not missing.
Everyone on the team commits to the team branch. Commit early and often, in small pieces, with messages that say what changed. Pull before you start working and pull again before you push, because three other people are on that branch with you.
Your commits are part of how the assignment is graded. A team branch with four commits, all from one person, on the night before the deadline, tells me something I would rather not know.
Pull requests
When your team is ready, open a pull request from your team branch into main.
Open it by the deadline even if you expect to make changes afterward, because the review starts from the pull request.
The description should say what the deliverable includes, who worked on what, and anything you want the reviewers to look at closely. If something is unfinished, say so there rather than hoping nobody notices.
Peer Review
Every pull request will be reviewed by another team before I merge it. The pairing rotates so that each team reviews every other team exactly once over the semester.
GitHub will also ask someone on your own team to review, because your team owns the directory you changed. Answer them the same way you answer anyone else, but leave the approval alone: approving and merging are mine. I will not merge until the reviewing team has actually been through the pull request, however green it looks.
| Deliverable | Catalog reviews | Requirements reviews | Plans reviews | Advising reviews | Preferences reviews | Scheduling reviews |
|---|---|---|---|---|---|---|
| GP1 | Requirements | Plans | Advising | Preferences | Scheduling | Catalog |
| GP2 | Plans | Advising | Preferences | Scheduling | Catalog | Requirements |
| GP3 | Advising | Preferences | Scheduling | Catalog | Requirements | Plans |
| GP4 | Preferences | Scheduling | Catalog | Requirements | Plans | Advising |
| GP5 | Scheduling | Catalog | Requirements | Plans | Advising | Preferences |
Reviews are due 48 hours after the deadline, and the authoring team has 48 hours after that to respond and push follow-up commits to the same branch. Then I'll approve and merge; my approval is what tells you the deliverable is accepted. The Code Reviews page says how to leave a review, what each deliverable asks reviewers to look at, and how the five points are earned.
GP6 is not in the table. Its branch works the same way, but the review happens in class and in the final presentation rather than in the pull request.
What not to commit
Nothing large and nothing secret.
Data files belong in the repository only when they are small and hand-maintained.
Anything sizable is downloaded or generated by a script in scripts/, so that the repository stores the code that produces the data rather than the data itself.
Database passwords, connection strings with credentials, and API keys never go in the repository, not even in a comment, not even temporarily.
Put them in a .env file, which is ignored.
Every push runs an automatic check, and it does three things.
It rejects files over the size limit.
It runs ruff, which reports the mistakes a linter can see.
Then it imports every team's models and builds the whole database in a scratch file.
If your models collide with another team's or reference a table that does not exist, that check fails and the pull request cannot merge until it passes.
You can run all three yourself before you push, and the hooks in .githooks/ run them for you once you have turned them on.
The repository's README.md gives the commands.
Before a commit, the hook checks file size only. A large file is worth catching now, because once it is in the history, taking it out means rewriting history, and that is not something to do on your own. Before a push, the hook runs all three, which is where the round trip is worth saving.
The build check deliberately waits until you push.
A table whose foreign key points at a table you are writing tomorrow will not build, and that should not stop you from committing today's progress.
Both hooks can be skipped with --no-verify when you mean to, such as handing broken work to a teammate on purpose.
The check on the server is the one that decides whether a pull request can merge.
The check is your friend
A failing check on your branch costs you ten minutes.
The same mistake merged into main costs 24 people an afternoon.
When something goes wrong
It will, and it is recoverable.
If you commit something you should not have, tell me before you try to fix it yourself. Rewriting shared history is how a small mistake becomes a large one.
If you are stuck in a merge conflict, stop and ask. Conflicts in this repository are almost always small, because teams work in separate directories, and the fix is usually obvious to someone who has seen a few.
Nothing you do on a team branch can damage main.
That is the entire point of the setup.