Skip to content

Commit 0cceb42

Browse files
authored
Remove git credential workflow and allow download w/o PAT (#6303)
This changes makes it possible to build Positron without specifying a Github Personal Access Token by allowing the scripts that install Ark, PET, and Kallichore to run without credentials. It also removes the `git credential` workflow since it is not possible to use it non-interactively; if you want to supply a token, it's now necessary to do so via an environment variable or Git configuration value. Addresses #6083
1 parent ef8e77c commit 0cceb42

File tree

3 files changed

+25
-290
lines changed

3 files changed

+25
-290
lines changed

extensions/positron-python/scripts/install-pet.ts

+7-101
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,10 @@ async function executeCommand(command: string, stdin?: string): Promise<{ stdout
104104
* Downloads the specified version of Python Environment Tool and replaces the local directory.
105105
*
106106
* @param version The version of Python Environment Tool to download.
107-
* @param githubPat A Github Personal Access Token with the appropriate rights
107+
* @param githubPat An optional Github Personal Access Token with the appropriate rights
108108
* to download the release.
109-
* @param gitCredential Whether the PAT originated from the `git credential` command.
110109
*/
111-
async function downloadAndReplacePet(version: string, githubPat: string, gitCredential: boolean): Promise<void> {
110+
async function downloadAndReplacePet(version: string, githubPat: string | undefined): Promise<void> {
112111
try {
113112
const headers: Record<string, string> = {
114113
Accept: 'application/vnd.github.v3.raw', // eslint-disable-line
@@ -128,59 +127,6 @@ async function downloadAndReplacePet(version: string, githubPat: string, gitCred
128127

129128
const response = (await httpsGetAsync(requestOptions as any)) as any;
130129

131-
// Special handling for PATs originating from `git credential`.
132-
if (gitCredential && response.statusCode === 200) {
133-
// If the PAT hasn't been approved yet, do so now. This stores the credential in
134-
// the system credential store (or whatever `git credential` uses on the system).
135-
// Without this step, the user will be prompted for a username and password the
136-
// next time they try to download PET.
137-
const { stdout, stderr } = await executeCommand(
138-
'git credential approve',
139-
`protocol=https\n` +
140-
`host=github.com\n` +
141-
`path=/repos/posit-dev/positron-pet-builds/releases\n` +
142-
`username=\n` +
143-
`password=${githubPat}\n`,
144-
);
145-
console.log(stdout);
146-
if (stderr) {
147-
console.warn(
148-
`Unable to approve PAT. You may be prompted for a username and ` +
149-
`password the next time you download Python Environment Tools.`,
150-
);
151-
console.error(stderr);
152-
}
153-
} else if (gitCredential && response.statusCode > 400 && response.statusCode < 500) {
154-
// This handles the case wherein we got an invalid PAT from `git credential`. In this
155-
// case we need to clean up the PAT from the credential store, so that we don't
156-
// continue to use it.
157-
const { stdout, stderr } = await executeCommand(
158-
'git credential reject',
159-
`protocol=https\n` +
160-
`host=github.com\n` +
161-
`path=/repos/posit-dev/positron-pet-builds/releases\n` +
162-
`username=\n` +
163-
`password=${githubPat}\n`,
164-
);
165-
console.log(stdout);
166-
if (stderr) {
167-
console.error(stderr);
168-
throw new Error(
169-
`The stored PAT returned by 'git credential' is invalid, but\n` +
170-
`could not be removed. Please manually remove the PAT from 'git credential'\n` +
171-
`for the host 'github.com'`,
172-
);
173-
}
174-
throw new Error(
175-
`The PAT returned by 'git credential' is invalid. Python Environment Tool cannot be\n` +
176-
`downloaded.\n\n` +
177-
`Check to be sure that your Personal Access Token:\n` +
178-
'- Has the `repo` scope\n' +
179-
'- Is not expired\n' +
180-
'- Has been authorized for the "posit-dev" organization on Github (Configure SSO)\n',
181-
);
182-
}
183-
184130
let responseBody = '';
185131

186132
response.on('data', (chunk: any) => {
@@ -300,17 +246,16 @@ async function main() {
300246
return;
301247
}
302248

303-
// We need a Github Personal Access Token (PAT) to download PET. Because this is sensitive
304-
// information, there are a lot of ways to set it. We try the following in order:
249+
// We can optionally use a Github Personal Access Token (PAT) to download
250+
// PET. Because this is sensitive information, there are a lot of ways to
251+
// set it. We try the following in order:
305252

306253
// (1) The GITHUB_PAT environment variable.
307254
// (2) The POSITRON_GITHUB_PAT environment variable.
308255
// (3) The git config setting 'credential.https://api.github.com.token'.
309-
// (4) The git credential store.
310256

311257
// (1) Get the GITHUB_PAT from the environment.
312258
let githubPat = process.env.GITHUB_PAT;
313-
let gitCredential = false;
314259
if (githubPat) {
315260
console.log('Using Github PAT from environment variable GITHUB_PAT.');
316261
} else {
@@ -331,50 +276,11 @@ async function main() {
331276
console.log(`Using Github PAT from git config setting 'credential.https://api.github.com.token'.`);
332277
}
333278
} catch (error) {
334-
// We don't care if this fails; we'll try `git credential` next.
279+
// We don't care if this fails; we'll try without a PAT.
335280
}
336281
}
337282

338-
// (4) If no GITHUB_PAT is set, try to get it from git credential.
339-
if (!githubPat) {
340-
// Explain to the user what's about to happen.
341-
console.log(
342-
`Attempting to retrieve a Github Personal Access Token from git in order\n` +
343-
`to download Python Environment Tool ${packageJsonVersion}. If you are prompted for a username and\n` +
344-
`password, enter your Github username and a Personal Access Token with the\n` +
345-
`'repo' scope. You can read about how to create a Personal Access Token here: \n` +
346-
`\n` +
347-
`https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens\n` +
348-
`\n` +
349-
`If you don't want to set up a Personal Access Token now, just press Enter twice to set \n` +
350-
`a blank value for the password. Python Environment Tool will not be downloaded, but you will still be\n` +
351-
`able to run Positron with Python support.\n` +
352-
`\n` +
353-
`You can set a PAT later by running yarn again and supplying the PAT at this prompt,\n` +
354-
`or by running 'git config credential.https://api.github.com.token YOUR_GITHUB_PAT'\n`,
355-
);
356-
const { stdout } = await executeCommand(
357-
'git credential fill',
358-
`protocol=https\n host=github.com\n path=/repos/posit-dev/pet/releases\n`,
359-
);
360-
361-
gitCredential = true;
362-
// Extract the `password = ` line from the output.
363-
const passwordLine = stdout.split('\n').find((line: string) => line.startsWith('password='));
364-
if (passwordLine) {
365-
[, githubPat] = passwordLine.split('=');
366-
console.log(`Using Github PAT returned from 'git credential'.`);
367-
}
368-
}
369-
370-
if (!githubPat) {
371-
throw new Error(
372-
`No Github PAT was found. Unable to download PET ${packageJsonVersion}.\n` +
373-
`You can still run Positron with Python Support.`,
374-
);
375-
}
376-
377-
await downloadAndReplacePet(packageJsonVersion, githubPat, gitCredential);
283+
await downloadAndReplacePet(packageJsonVersion, githubPat);
378284
}
379285

380286
main().catch((error) => {

extensions/positron-r/scripts/install-kernel.ts

+8-93
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*---------------------------------------------------------------------------------------------
2-
* Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved.
2+
* Copyright (C) 2023-2025 Posit Software, PBC. All rights reserved.
33
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
44
*--------------------------------------------------------------------------------------------*/
55

@@ -88,13 +88,11 @@ async function executeCommand(command: string, stdin?: string):
8888
* Downloads the specified version of Ark and replaces the local binary.
8989
*
9090
* @param version The version of Ark to download.
91-
* @param githubPat A Github Personal Access Token with the appropriate rights
91+
* @param githubPat An optional Github Personal Access Token with the appropriate rights
9292
* to download the release.
93-
* @param gitCredential Whether the PAT originated from the `git credential` command.
9493
*/
9594
async function downloadAndReplaceArk(version: string,
96-
githubPat: string,
97-
gitCredential: boolean): Promise<void> {
95+
githubPat: string | undefined): Promise<void> {
9896

9997
try {
10098
const headers: Record<string, string> = {
@@ -115,51 +113,6 @@ async function downloadAndReplaceArk(version: string,
115113

116114
const response = await httpsGetAsync(requestOptions as any) as any;
117115

118-
// Special handling for PATs originating from `git credential`.
119-
if (gitCredential && response.statusCode === 200) {
120-
// If the PAT hasn't been approved yet, do so now. This stores the credential in
121-
// the system credential store (or whatever `git credential` uses on the system).
122-
// Without this step, the user will be prompted for a username and password the
123-
// next time they try to download Ark.
124-
const { stdout, stderr } =
125-
await executeCommand('git credential approve',
126-
`protocol=https\n` +
127-
`host=github.com\n` +
128-
`path=/repos/posit-dev/ark/releases\n` +
129-
`username=\n` +
130-
`password=${githubPat}\n`);
131-
console.log(stdout);
132-
if (stderr) {
133-
console.warn(`Unable to approve PAT. You may be prompted for a username and ` +
134-
`password the next time you download Ark.`);
135-
console.error(stderr);
136-
}
137-
} else if (gitCredential && response.statusCode > 400 && response.statusCode < 500) {
138-
// This handles the case wherein we got an invalid PAT from `git credential`. In this
139-
// case we need to clean up the PAT from the credential store, so that we don't
140-
// continue to use it.
141-
const { stdout, stderr } =
142-
await executeCommand('git credential reject',
143-
`protocol=https\n` +
144-
`host=github.com\n` +
145-
`path=/repos/posit-dev/ark/releases\n` +
146-
`username=\n` +
147-
`password=${githubPat}\n`);
148-
console.log(stdout);
149-
if (stderr) {
150-
console.error(stderr);
151-
throw new Error(`The stored PAT returned by 'git credential' is invalid, but\n` +
152-
`could not be removed. Please manually remove the PAT from 'git credential'\n` +
153-
`for the host 'github.com'`);
154-
}
155-
throw new Error(`The PAT returned by 'git credential' is invalid. Ark cannot be\n` +
156-
`downloaded.\n\n` +
157-
`Check to be sure that your Personal Access Token:\n` +
158-
'- Has the `repo` scope\n' +
159-
'- Is not expired\n' +
160-
'- Has been authorized for the "posit-dev" organization on Github (Configure SSO)\n');
161-
}
162-
163116
let responseBody = '';
164117

165118
response.on('data', (chunk: any) => {
@@ -290,17 +243,16 @@ async function main() {
290243
return;
291244
}
292245

293-
// We need a Github Personal Access Token (PAT) to download Ark. Because this is sensitive
294-
// information, there are a lot of ways to set it. We try the following in order:
246+
// We can optionally use a Github Personal Access Token (PAT) to download
247+
// Ark. Because this is sensitive information, there are a lot of ways to
248+
// set it. We try the following in order:
295249

296250
// (1) The GITHUB_PAT environment variable.
297251
// (2) The POSITRON_GITHUB_PAT environment variable.
298252
// (3) The git config setting 'credential.https://api.github.com.token'.
299-
// (4) The git credential store.
300253

301254
// (1) Get the GITHUB_PAT from the environment.
302255
let githubPat = process.env.GITHUB_PAT;
303-
let gitCredential = false;
304256
if (githubPat) {
305257
console.log('Using Github PAT from environment variable GITHUB_PAT.');
306258
} else {
@@ -323,48 +275,11 @@ async function main() {
323275
`'credential.https://api.github.com.token'.`);
324276
}
325277
} catch (error) {
326-
// We don't care if this fails; we'll try `git credential` next.
278+
// We don't care if this fails; we'll without a PAT.
327279
}
328280
}
329281

330-
// (4) If no GITHUB_PAT is set, try to get it from git credential.
331-
if (!githubPat) {
332-
// Explain to the user what's about to happen.
333-
console.log(`Attempting to retrieve a Github Personal Access Token from git in order\n` +
334-
`to download Ark ${packageJsonVersion}. If you are prompted for a username and\n` +
335-
`password, enter your Github username and a Personal Access Token with the\n` +
336-
`'repo' scope. You can read about how to create a Personal Access Token here: \n` +
337-
`\n` +
338-
`https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens\n` +
339-
`\n` +
340-
`If you don't want to set up a Personal Access Token now, just press Enter twice to set \n` +
341-
`a blank value for the password. Ark will not be downloaded, but you will still be\n` +
342-
`able to run Positron without R support.\n` +
343-
`\n` +
344-
`You can set a PAT later by running yarn again and supplying the PAT at this prompt,\n` +
345-
`or by running 'git config credential.https://api.github.com.token YOUR_GITHUB_PAT'\n`);
346-
const { stdout, stderr } =
347-
await executeCommand('git credential fill',
348-
`protocol=https\n` +
349-
`host=github.com\n` +
350-
`path=/repos/posit-dev/ark/releases\n`);
351-
352-
gitCredential = true;
353-
// Extract the `password = ` line from the output.
354-
const passwordLine = stdout.split('\n').find(
355-
(line: string) => line.startsWith('password='));
356-
if (passwordLine) {
357-
githubPat = passwordLine.split('=')[1];
358-
console.log(`Using Github PAT returned from 'git credential'.`);
359-
}
360-
}
361-
362-
if (!githubPat) {
363-
throw new Error(`No Github PAT was found. Unable to download Ark ${packageJsonVersion}.\n` +
364-
`You can still run Positron without R support.`);
365-
}
366-
367-
await downloadAndReplaceArk(packageJsonVersion, githubPat, gitCredential);
282+
await downloadAndReplaceArk(packageJsonVersion, githubPat);
368283
}
369284

370285
main().catch((error) => {

0 commit comments

Comments
 (0)