Skip to content

Commit 779ce31

Browse files
authored
fix(coder/github-upload-public-key): follow pagination when checking existing keys (#1197)
## Description Stacked on #1193. Addresses [this review comment](#1193 (comment)). GitHub's key list endpoints return 30 keys per page by default, so a Coder key beyond the first page was missed and the module tried to upload it again. `upload_key` now requests `per_page=100` and keeps fetching pages until it finds the key or gets a page shorter than 100. This applies to both `/user/keys` and `/user/ssh_signing_keys`. ## Type of Change - [ ] New module - [ ] New template - [x] Bug fix - [ ] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information **Path:** `registry/coder/modules/github-upload-public-key` **New version:** `v1.2.0` (unreleased; same version as #1193) **Breaking change:** [ ] Yes [x] No ## Testing & Validation - [x] Tests pass (`bun test`): 10/10, with a new case where the key is on the second page for both endpoints. This case fails on the #1193 script. - [x] Code formatted (`bun fmt`) - [x] Changes tested locally against real GitHub ## Related Issues Stacked on #1193 --- > [!NOTE] > This PR was generated by Coder Agents on behalf of @35C4n0r, using Claude.
1 parent 8daec43 commit 779ce31

2 files changed

Lines changed: 62 additions & 25 deletions

File tree

‎registry/coder/modules/github-upload-public-key/main.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ const setupServer = () => {
113113
]);
114114
}
115115

116+
// case: key exists on the second page
117+
if (req.headers.get("Authorization") === "Bearer paginated") {
118+
const perPage = Number(url.searchParams.get("per_page") ?? 30);
119+
const page = Number(url.searchParams.get("page") ?? 1);
120+
const keys = [
121+
...Array.from({ length: perPage }, (_, i) => ({ key: `foo-${i}` })),
122+
{ key: "exists" },
123+
];
124+
return createJSONResponse(
125+
keys.slice((page - 1) * perPage, page * perPage),
126+
);
127+
}
128+
116129
// case: key does not exist
117130
return createJSONResponse([
118131
{
@@ -238,6 +251,23 @@ describe("github-upload-public-key", () => {
238251
]);
239252
});
240253

254+
it("finds an existing key on a later page", async () => {
255+
const { id, run, uploads } = await setupContainer(undefined, {
256+
key_type: "both",
257+
});
258+
await writeCoder(id, "echo paginated");
259+
260+
const exec = await run();
261+
expect(exec.stdout).toContain(
262+
"Your Coder public key is already a GitHub authentication key!",
263+
);
264+
expect(exec.stdout).toContain(
265+
"Your Coder public key is already a GitHub signing key!",
266+
);
267+
expect(exec.exitCode).toBe(0);
268+
expect(uploads).toEqual([]);
269+
});
270+
241271
it("skips existing keys when key_type is both", async () => {
242272
const { id, run, uploads } = await setupContainer(undefined, {
243273
key_type: "both",

‎registry/coder/modules/github-upload-public-key/run.sh‎

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,31 +69,38 @@ upload_key() {
6969
local label="$2"
7070

7171
echo "Fetching $label keys from GitHub..."
72-
local keys_response
73-
keys_response=$(
74-
curl -L -s \
75-
-w "\n%%{http_code}" \
76-
-H "Accept: application/vnd.github+json" \
77-
-H "Authorization: Bearer $GITHUB_TOKEN" \
78-
-H "X-GitHub-Api-Version: 2022-11-28" \
79-
"$GITHUB_API_URL/user/$endpoint"
80-
)
81-
local keys_status keys_body
82-
keys_status=$(tail -n1 <<< "$keys_response")
83-
keys_body=$(sed \$d <<< "$keys_response")
84-
85-
if [ "$keys_status" -ne 200 ]; then
86-
echo "Failed to fetch GitHub $label keys with status code $keys_status!"
87-
echo "$keys_body"
88-
return 1
89-
fi
90-
91-
local match
92-
match=$(jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' <<< "$keys_body")
93-
if [ "$PUBLIC_KEY" = "$match" ]; then
94-
echo "Your Coder public key is already a GitHub $label key!"
95-
return 0
96-
fi
72+
local per_page=100 page=1
73+
local keys_response keys_status keys_body match count
74+
while true; do
75+
keys_response=$(
76+
curl -L -s \
77+
-w "\n%%{http_code}" \
78+
-H "Accept: application/vnd.github+json" \
79+
-H "Authorization: Bearer $GITHUB_TOKEN" \
80+
-H "X-GitHub-Api-Version: 2022-11-28" \
81+
"$GITHUB_API_URL/user/$endpoint?per_page=$per_page&page=$page"
82+
)
83+
keys_status=$(tail -n1 <<< "$keys_response")
84+
keys_body=$(sed \$d <<< "$keys_response")
85+
86+
if [ "$keys_status" -ne 200 ]; then
87+
echo "Failed to fetch GitHub $label keys with status code $keys_status!"
88+
echo "$keys_body"
89+
return 1
90+
fi
91+
92+
match=$(jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' <<< "$keys_body")
93+
if [ "$PUBLIC_KEY" = "$match" ]; then
94+
echo "Your Coder public key is already a GitHub $label key!"
95+
return 0
96+
fi
97+
98+
count=$(jq 'length' <<< "$keys_body")
99+
if [ "$count" -lt "$per_page" ]; then
100+
break
101+
fi
102+
page=$((page + 1))
103+
done
97104

98105
echo "Your Coder public key is not a GitHub $label key yet. Adding it now..."
99106
local upload_response

0 commit comments

Comments
 (0)