GitHub repository setup
install.sh can create your project's GitHub repository and apply a fixed set of general settings, labels and a default branch ruleset to it. This page is for developers who publish a Scaffold project on GitHub. Follow it to set up the repository during an install, or later on a project that already exists.
The GitHub step changes only git and the repository on GitHub. It runs after Initialization complete., so the project files are ready even when a request fails.
Before you begin
-
Install GitHub CLI.
install.shcallsghfor every GitHub request. -
Create a GitHub token and export it as
SCAFFOLD_GITHUB_TOKEN:export SCAFFOLD_GITHUB_TOKEN=github_pat_xxxReplace
github_pat_xxxwith your token.install.shsends only this token. It ignores the accountghis logged in with and anyGITHUB_TOKENin your shell. -
To create a repository, install git and set your git email with
git config --global user.email "[email protected]", replacing[email protected]with your address.
A fine-grained token needs these repository permissions:
| Permission | Needed for |
|---|---|
| Metadata: read | Reading the repository (always granted) |
| Administration: write | General settings, the ruleset and creating the repository |
| Issues: write | Labels |
| Checks: read | Reading the checks of a CI run |
| Actions: read | Waiting for CI and matching each check to its workflow file |
| Contents: read | Reading workflow triggers |
| Contents: write and Workflows: write | Pushing a new repository |
For settings only, you can limit a fine-grained token to the repositories it manages. Creating a repository needs All repositories access, because a token limited to selected repositories can't include one that doesn't exist yet. A classic token needs the repo and workflow scopes.
When gh or the token is missing, install.sh prints what's missing and the command to run later. An install carries on without the GitHub step and exits 0. The github command stops with exit code 1.
Configure the repository during an install
The last prompt of an interactive install depends on the directory:
- In a directory with a
.gitdirectory, such as a clone of a repository made with Use this template, the prompt is Configure GitHub repository settings. - In a directory without one, such as a run piped from
curl, the prompt is Create a GitHub repository. When git or your git email is missing, the install prints why and asks Configure GitHub repository settings instead.
Both prompts appear only when gh and the token are available, and both default to n. After a y, the install asks for the owner, the repository and each section. It reads the repository from GitHub before the section prompts, so the Description prompt shows the current description. Press Enter to keep it.
Without prompts, pass --github:
curl -fsSL https://getscaffold.dev/install.sh | \
bash -s -- --namespace=AcmeApp --name=acme-app --author="Jane Doe" --github
Create the repository
When the repository doesn't exist on GitHub, install.sh can create it and push the project. The following recording shows an install that creates the repository:
In an interactive run, answer y to Create a GitHub repository, or to the does not exist. Create it prompt that follows a repository GitHub doesn't know. Then pick the visibility. Without prompts, pass --github-create, which also turns on --github:
curl -fsSL https://getscaffold.dev/install.sh | \
bash -s -- --github-owner=acme-org --github-repo=acme-app --author="Jane Doe" --github-create
install.sh creates the repository in this order:
- Without a
.gitdirectory, it runsgit initon amainbranch. - Without commits, it commits every file in 1 initial commit. Unless you pass
--keep,install.shitself stays out of that commit. - It creates the repository with
gh repo create, aspublicunless you pass--github-visibility=private. - It adds an
originremote when there is none. An existingoriginstays as it is. - It pushes the current branch with the token, so your local git credentials don't matter.
- It applies the general settings, the labels and the ruleset.
- It waits for the first CI run and adds that run's checks to the ruleset. For details, see Required status checks.
- It removes
install.sh, unless you passed--keep.
A repository with commits keeps them, and install.sh warns about uncommitted changes because it pushes only what's committed. To create the repository without pushing, pass --no-github-push.
install.sh removes itself only as the very last step. A run that fails or is interrupted keeps the script, so you can run ./install.sh github again.
Configure an existing project
To configure the repository of a project that already exists, run the github command in the project's root directory:
curl -fsSL https://getscaffold.dev/install.sh | bash -s -- github
The github command leaves the project files untouched and needs no template. With no other options, it prompts for the owner, the repository and each section. Any option turns off the prompts and applies every section that isn't turned off:
curl -fsSL https://getscaffold.dev/install.sh | \
bash -s -- github --github-owner=acme --github-repo=acme-app --no-github-ruleset
The command ends with GitHub repository settings complete. Each section prints 1 line with what it changed.
How the owner and repository are found
Each value comes from the first source that has it:
| Value | Sources, in order |
|---|---|
| Owner | --github-owner, the origin remote, the login of the token |
| Repository | --github-repo, the origin remote, the project name |
The github command takes the current directory's name in place of the project name. install.sh reads origin only in the project's root directory and skips a remote that points at the Scaffold template.
When origin names a different repository than the options, install.sh stops with an error. It never rewrites the remote.
The owner and repository also set the defaults for --namespace and --name. An owner of acme-org gives the namespace AcmeOrg, and a repository of Acme_App gives the name acme-app. With those defaults, a non-interactive install needs only --author. An explicit --namespace or --name wins over the default. In an interactive install, the defaults appear on the Namespace and Project prompts.
A derived value that fails validation names its source:
Error: Name derived from --github-repo is invalid: 'acme.app'. Pass --name explicitly.
What gets applied
Each section reads the repository first and changes only what differs, so you can run it again safely. Each section has its own option to turn it off.
General settings
General settings go to GitHub in 1 request:
- Squash and rebase merges on, merge commits off. To change the methods, pass
--github-mergewith a comma-separated list ofmerge,squashandrebase. - Squash commits take the pull request title and an empty message.
- Auto-merge, the update branch button and deleting the branch after a merge on.
- Issues on. Wiki, projects and discussions off.
- The description from
--github-description. Without a value, the description stays as it is.
Labels
install.sh creates or updates these labels:
| Label | Color | Description |
|---|---|---|
AUTOMERGE | #934BF4 | Pull request has been approved and set to automerge |
BLOCKED | #D93F0B | Issue or pull request is blocked |
CONFLICT | #BC143E | Pull request has a conflict that needs to be resolved before it can be merged |
Dependencies | #62E795 | Pull request was raised automatically by a dependency bot |
DO NOT MERGE | #D93F0B | Do not merge this pull request |
Do not review | #D93F0B | Do not review this pull request |
Needs review | #5319E7 | Pull request needs a review from assigned developers |
Ready for test | #0E8A16 | Pull request is ready for manual testing |
Ready to be merged | #C2E0C6 | Pull request is ready to be merged (assigned after testing is complete) |
Requires more work | #B60205 | Pull request was reviewed and reviewer(s) asked to work further on the pull request |
UPSTREAM | #FBCA04 | Issue or pull request is related to an upstream project |
URGENT | #D93F0B | Pull request needs to be urgently reviewed |
Names match without regard to case, as they do on GitHub. Other labels stay in place. To delete them, pass --github-labels-prune.
Default branch ruleset
The default-branch ruleset protects the repository's default branch:
- It blocks deleting the branch and force-pushing to it.
- It requires the checks of the latest CI run, as the next section describes.
- The admin repository role bypasses it, so the owner can still push directly.
install.sh finds the ruleset by name. It updates an existing default-branch ruleset and creates one when none exists. An update keeps every other rule and bypass actor on the ruleset. A ruleset of another name stays as it is, so rename an existing ruleset to default-branch to have install.sh manage it.
Rulesets on a private repository of a personal account need GitHub Pro. On a free plan, install.sh skips the ruleset and applies the other sections.
Required status checks
install.sh reads the checks from the latest commit on the default branch. It keeps a check only when all 3 of these hold:
- GitHub Actions reported it.
- Its workflow file has a
pull_requesttrigger. A check from a workflow that runs only on push never reports on a pull request, so requiring it would block every pull request. - Its conclusion isn't
skipped,cancelledorneutral.
install.sh reads each workflow file through the GitHub API, so the check works from a curl run with no checkout. The checks it finds replace the ruleset's required checks. When it finds none, the ruleset keeps the checks it already had, and install.sh prints the command to run again after CI. On an existing repository, --no-github-required-checks leaves the required checks as they are.
A repository that install.sh created in the same run has no CI run yet. The ruleset goes on without checks, and install.sh then waits for the first CI run of the pushed commit:
- When no run starts within about 60 seconds, it stops waiting and prints the command to run again later.
- It waits up to 1800 seconds for every run to finish. To change the limit, pass
--github-wait-timeout. - When every run has finished, it adds the checks to the ruleset. A failed run still adds its checks, with a warning that pull requests stay blocked until CI passes.
- When the wait times out or you press Ctrl-C, the ruleset stays without checks and
install.shprints the command to run again.
To skip the wait, pass --no-github-wait-checks.
Preview the changes
To print the requests and git commands install.sh would run without running them, add --dry-run:
curl -fsSL https://getscaffold.dev/install.sh | bash -s -- github --dry-run
A dry run still reads the repository to work out what would change, so it needs gh and the token too. A dry run for a new repository creates nothing and doesn't wait for CI.
When a request fails
A 403 skips only its section and names the missing permission:
Ruleset: skipped (token lacks Administration: write)
The other sections still run, and a skipped section doesn't change the exit code. Any other failure also lets the other sections run, then makes install.sh exit with code 1. A failure to create or push the repository stops the GitHub step, because the sections need the repository.
Options
These options control the GitHub step. Each one also works with the github command, apart from --github:
| Option | Default | Description |
|---|---|---|
--github, --no-github | Off | Configures the repository after an install. |
--github-owner=VALUE | See How the owner and repository are found | Sets the GitHub user or organization. |
--github-repo=VALUE | See How the owner and repository are found | Sets the repository name. |
--github-create, --no-github-create | Off | Creates the repository when it doesn't exist, and turns on --github. |
--github-visibility=VALUE | public | Sets the visibility of a created repository: public or private. |
--github-push, --no-github-push | On | Pushes a created repository. |
--github-settings, --no-github-settings | On | Applies the general settings. |
--github-description=VALUE | Unchanged | Sets the repository description. |
--github-merge=VALUE | squash,rebase | Sets the allowed merge methods: merge, squash and rebase. |
--github-labels, --no-github-labels | On | Applies the labels. |
--github-labels-prune, --no-github-labels-prune | Off | Deletes labels that aren't in the list. |
--github-ruleset, --no-github-ruleset | On | Applies the default branch ruleset. |
--github-required-checks, --no-github-required-checks | On | Requires the checks of the latest CI run on an existing repository. |
--github-wait-checks, --no-github-wait-checks | On | Waits for the first CI run of a created repository and requires its checks. |
--github-wait-timeout=SECONDS | 1800 | Sets how long to wait for CI. |
--dry-run | Off | Prints the requests and git commands without running them. |
Every interactive install also prints the non-interactive command that repeats your answers, before the Proceed with project init prompt. The command leaves out the token.