Skip to content

Commit 7a37e07

Browse files
authored
init and setup explain how to create the App, and stop naming ours (#125)
Both told the adopter to create the simplycubed-code GitHub App. App names are globally unique and we hold that one, so every adopter after us got 'Name has already been taken'. The step was only performable by the account that already owns the App. Neither said the thing that makes the rest make sense: the App has to be theirs, because the private key is what mints tokens against their repository and a shared key would let its holder act on every other installation. Without that, 'create a GitHub App' reads as busywork we could have done for them, and the natural response is to look for ours. init now carries the creation URL, resolved to the org form when the repository belongs to one, the three permissions with their access levels, webhook off and why, install visibility and why it is the step most often missed, and where the Client ID and the download-once private key come from. A test asserts each element, so the gate holds prose it otherwise could not judge. docs/setup.md says the same things in the same order, and the references to simplycubed-code[bot] as the adopter's identity now read <your-app-name>[bot]. The decision record in docs/decisions is left alone: it documents a past decision rather than instructing anyone. Closes #110 Closes #113
1 parent 6bd9192 commit 7a37e07

5 files changed

Lines changed: 96 additions & 18 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ What that means for customers:
116116

117117
Setup files are generated locally by `simplycubed init` and then merged by a human, because the runtime holds no `workflows` permission and cannot add or update workflow files on its own.
118118

119-
The GitHub App identity is `simplycubed-code[bot]`. That bot is the single audit signal for everything the agent does.
119+
The GitHub App identity is your own App's `[bot]` account. That bot is the single audit signal for everything the agent does.
120120

121121
## Getting started
122122

@@ -143,7 +143,7 @@ Then follow the [setup guide in `docs/setup.md`](docs/setup.md) to add the repos
143143

144144
To run the loop inside your own GitHub Actions:
145145

146-
1. Create and install the GitHub App, `simplycubed-code`.
146+
1. Create and install **your own** GitHub App. App names are globally unique, so it cannot be named after ours, and its private key is what mints the tokens that act on your repository.
147147
Repository permissions: `Contents`, `Pull requests`, and `Issues` only.
148148
Do not grant `Workflows`, `Administration`, `Environments`, or `Secrets`.
149149
That means the App cannot push changes under `.github/workflows/`: if a run needs to edit a workflow file, make that commit as a human or run the CLI locally under your own `gh` auth instead of the App token.

‎cmd/simplycubed/main.go‎

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,30 @@ func initCmd(argv []string, stdout io.Writer) error {
707707
} else {
708708
fmt.Fprintf(stdout, "created labels: %s\n", strings.Join(created, ", "))
709709
}
710+
createURL, resolved := appCreateURL(*repoDir)
710711
fmt.Fprintln(stdout, "next steps:")
711-
fmt.Fprintln(stdout, " - install the simplycubed-code GitHub App with contents, issues, and pull requests permissions only")
712-
fmt.Fprintln(stdout, " - disable the App webhook, set install visibility to Any account, and install it on the repo")
712+
fmt.Fprintln(stdout, " 1. Create your OWN GitHub App. It has to be yours: its private key is what mints")
713+
fmt.Fprintln(stdout, " the tokens that act on your repository, so a shared key would let its holder act")
714+
fmt.Fprintln(stdout, " on every other installation. This is what keeps the agent inside your GitHub.")
715+
fmt.Fprintln(stdout, " "+createURL)
716+
if !resolved {
717+
fmt.Fprintln(stdout, " (if this repository belongs to an organisation, use")
718+
fmt.Fprintln(stdout, " https://github.com/organizations/<org>/settings/apps/new instead)")
719+
}
720+
fmt.Fprintln(stdout, " Name it anything you like; App names are globally unique, so you cannot reuse ours.")
721+
fmt.Fprintln(stdout, " Permissions, and nothing else: Contents: Read and write")
722+
fmt.Fprintln(stdout, " Issues: Read and write")
723+
fmt.Fprintln(stdout, " Pull requests: Read and write")
724+
fmt.Fprintln(stdout, " Uncheck Active under Webhook. Actions triggers this runtime; an enabled webhook")
725+
fmt.Fprintln(stdout, " with nothing listening only generates failures.")
726+
fmt.Fprintln(stdout, " Set Any account under Where can this GitHub App be installed, if the App belongs")
727+
fmt.Fprintln(stdout, " to your personal account and the repository belongs to an organisation. An App")
728+
fmt.Fprintln(stdout, " restricted to its owner cannot be installed anywhere else, and this is the step")
729+
fmt.Fprintln(stdout, " most often missed.")
730+
fmt.Fprintln(stdout, " 2. Generate a private key on the App settings page and keep the download. GitHub")
731+
fmt.Fprintln(stdout, " shows it once. Note the Client ID there too, the Iv23 string.")
732+
fmt.Fprintln(stdout, " 3. Install the App on this repository, from Install App on the same page.")
733+
fmt.Fprintln(stdout, " Reference: https://docs.github.com/apps/creating-github-apps")
713734
fmt.Fprintln(stdout, " - write the real gate in .github/simplycubed.yml")
714735
fmt.Fprintln(stdout, " - verify that gate is green on your main branch")
715736
fmt.Fprintln(stdout, " - add two repository VARIABLES, under Settings > Secrets and variables > Actions > Variables:")
@@ -727,6 +748,29 @@ func initCmd(argv []string, stdout io.Writer) error {
727748
return nil
728749
}
729750

751+
// appCreateURL returns the GitHub form for creating an App, which differs by
752+
// account type. Best effort: init must still work when gh cannot answer, so a
753+
// failure returns the personal form and a note rather than an error.
754+
func appCreateURL(repoDir string) (string, bool) {
755+
out, err := exec.Command("gh", "repo", "view", "--json", "owner", "--jq", ".owner.login").Output()
756+
_ = repoDir
757+
if err != nil {
758+
return "https://github.com/settings/apps/new", false
759+
}
760+
owner := strings.TrimSpace(string(out))
761+
if owner == "" {
762+
return "https://github.com/settings/apps/new", false
763+
}
764+
kind, err := exec.Command("gh", "api", "users/"+owner, "--jq", ".type").Output()
765+
if err != nil {
766+
return "https://github.com/settings/apps/new", false
767+
}
768+
if strings.TrimSpace(string(kind)) == "Organization" {
769+
return "https://github.com/organizations/" + owner + "/settings/apps/new", true
770+
}
771+
return "https://github.com/settings/apps/new", true
772+
}
773+
730774
func writeStarterConfig(path string) (bool, error) {
731775
return writeStarterFile(path, starterConfig)
732776
}

‎cmd/simplycubed/main_test.go‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,19 @@ exit 0
126126
for _, want := range []string{
127127
"wrote " + configPath,
128128
"created labels: sc:go, sc:queued, sc:working, sc:review, sc:blocked, sc:done",
129-
"install the simplycubed-code GitHub App with contents, issues, and pull requests permissions only",
130-
"disable the App webhook, set install visibility to Any account, and install it on the repo",
129+
// The App has to be the adopter's own, and the output has to say why.
130+
// Without that, "create a GitHub App" reads as busywork we could have
131+
// done for them, and the natural next move is to look for ours.
132+
"Create your OWN GitHub App",
133+
"its private key is what mints",
134+
"App names are globally unique, so you cannot reuse ours",
135+
"Contents: Read and write",
136+
"Issues: Read and write",
137+
"Pull requests: Read and write",
138+
"Uncheck Active under Webhook",
139+
"Set Any account under Where can this GitHub App be installed",
140+
"GitHub\n shows it once",
141+
"https://docs.github.com/apps/creating-github-apps",
131142
"write the real gate in .github/simplycubed.yml",
132143
"verify that gate is green on your main branch",
133144
// Naming the section is the point: two of the four go under Variables

‎docs/faq.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ You can use SimplyCubed Code two ways:
1515
runs your gate, and opens or updates a pull request as you;
1616
- or through the reusable workflow that runs inside your own GitHub Actions, as
1717
described in the README's install section, which now mints a per-job GitHub
18-
App installation token for `simplycubed-code[bot]`.
18+
App installation token for your own App's `[bot]` identity.
1919

2020
The GitHub App is configured with only `contents`, `pull requests`, and
2121
`issues`, and the reusable workflow proves that scope at runtime by expecting an

‎docs/setup.md‎

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,47 @@ simplycubed run owner/repo#N --repo-dir .
7171
4. Create and install the GitHub App identity, then add the required repository
7272
settings.
7373

74-
Create the `simplycubed-code` GitHub App with `Contents`, `Issues`, and `Pull
75-
requests` permissions only, disable the webhook, set install visibility to `Any
76-
account`, and install it on the repository.
77-
78-
Then add these repository settings:
74+
**The App has to be your own.** Its private key is what mints the tokens that act
75+
on your repository, so a shared key would let whoever holds it act on every other
76+
installation of that App. That is why there is no App to install *from us*, and
77+
it is what makes "runs in your GitHub, not ours" true rather than a slogan.
78+
79+
Create it at [github.com/settings/apps/new](https://github.com/settings/apps/new),
80+
or at `https://github.com/organizations/<org>/settings/apps/new` if the repository
81+
belongs to an organisation. GitHub's own reference is
82+
[Creating GitHub Apps](https://docs.github.com/apps/creating-github-apps).
83+
84+
- **Name:** anything you like. App names are globally unique, so you cannot reuse
85+
ours, and your bot will be `<your-app-name>[bot]`.
86+
- **Permissions**, and nothing else: `Contents: Read and write`,
87+
`Issues: Read and write`, `Pull requests: Read and write`.
88+
- **Webhook:** uncheck `Active`. Actions triggers this runtime, so an enabled
89+
webhook with nothing listening only generates failures.
90+
- **Where can this GitHub App be installed:** `Any account`, if the App belongs to
91+
your personal account and the repository belongs to an organisation. An App
92+
restricted to its owner cannot be installed anywhere else. This is the step most
93+
often missed.
94+
95+
Generate a private key on the App settings page and keep the download; GitHub
96+
shows it once. Note the Client ID there too, the `Iv23` string. Then install the
97+
App on the repository from `Install App`.
98+
99+
Then add these repository settings. **Variables and Secrets are different tabs**
100+
under Settings > Secrets and variables > Actions, and a value filed under the
101+
wrong one reads back as empty rather than failing:
79102

80103
- Variable: `SIMPLYCUBED_GH_APP_CLIENT_ID`, the App Client ID, the `Iv23` string on the App settings page
81104
- Secret: `SIMPLYCUBED_GH_APP_PRIVATE_KEY`
82105
- Variable: `SIMPLYCUBED_AZURE_OPENAI_ENDPOINT`
83106
- Secret: `SIMPLYCUBED_AZURE_OPENAI_API_KEY`
84107

85-
The Actions runtime authenticates as the `simplycubed-code` GitHub App, so the
86-
App ID and private key are required. Store the private key as the full PEM
87-
contents, including the `-----BEGIN` and `-----END` lines.
108+
The Actions runtime authenticates as your App, so the Client ID and private key
109+
are both required. Store the private key as the full PEM contents, including the
110+
`-----BEGIN` and `-----END` lines.
88111

89112
Each job mints its own installation token for that repository, so the agent
90-
authors commits, pull requests, and comments as `simplycubed-code[bot]`, and
91-
its pull requests receive their own CI runs. A personal access token is not an
113+
authors commits, pull requests, and comments as `<your-app-name>[bot]`, and its
114+
pull requests receive their own CI runs. A personal access token is not an
92115
alternative: the reusable workflow accepts App credentials only.
93116

94117
5. Commit the generated config and workflow files in the target repository, open
@@ -153,7 +176,7 @@ flowchart TB
153176
A2["repository secrets<br/>SIMPLYCUBED_AZURE_OPENAI_API_KEY<br/>SIMPLYCUBED_GH_APP_PRIVATE_KEY"]
154177
A3["per-job installation token<br/>contents, issues, pull requests"]
155178
A4["simplycubed run / address"]
156-
A5["commits and PR authored by<br/>simplycubed-code[bot]"]
179+
A5["commits and PR authored by<br/>your-app-name[bot]"]
157180
A2 --> A3
158181
A1 --> A4
159182
A2 --> A4

0 commit comments

Comments
 (0)