-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithub-cli-auth.ts
More file actions
146 lines (128 loc) · 4.5 KB
/
Copy pathgithub-cli-auth.ts
File metadata and controls
146 lines (128 loc) · 4.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import {
CreatePlan,
DestroyPlan,
ModifyPlan,
Resource,
ResourceSettings,
SpawnStatus,
getPty,
z,
} from '@codifycli/plugin-core';
import { OS } from '@codifycli/schemas';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { exampleGithubCliAuthBasic, exampleGithubCliAuthEnterprise } from './examples.js';
export const schema = z
.object({
token: z
.string()
.optional()
.describe('GitHub personal access token (classic or fine-grained) used for authentication. Omit to use interactive browser-based login'),
hostname: z
.string()
.optional()
.describe('GitHub hostname (default: github.com). Set this for GitHub Enterprise Server instances'),
})
.meta({ $comment: 'https://cli.github.com/manual/gh_auth' })
.describe('GitHub CLI authentication — log in and out of GitHub accounts');
export type GithubCliAuthConfig = z.infer<typeof schema>;
const defaultConfig: Partial<GithubCliAuthConfig> = {
hostname: 'github.com',
token: undefined,
};
export class GithubCliAuthResource extends Resource<GithubCliAuthConfig> {
getSettings(): ResourceSettings<GithubCliAuthConfig> {
return {
id: 'github-cli-auth',
defaultConfig,
exampleConfigs: {
example1: exampleGithubCliAuthBasic,
example2: exampleGithubCliAuthEnterprise,
},
isSensitive: true,
operatingSystems: [OS.Darwin, OS.Linux],
schema,
dependencies: ['github-cli'],
parameterSettings: {
token: { canModify: true, isSensitive: true },
hostname: { default: 'github.com' },
},
importAndDestroy: {
requiredParameters: [],
defaultRefreshValues: {
hostname: 'github.com',
},
},
allowMultiple: {
identifyingParameters: ['hostname'],
findAllParameters: async () => {
const $ = getPty();
const { data, status } = await $.spawnSafe('gh auth status');
if (status === SpawnStatus.ERROR || !data.trim()) return [];
const hostnames: string[] = [];
for (const line of data.split('\n')) {
if (line.length > 0 && !line.startsWith(' ') && !line.startsWith('\t')) {
const hostname = line.trim();
if (hostname) hostnames.push(hostname);
}
}
return hostnames.map((h) => ({ hostname: h }));
},
},
};
}
async refresh(params: Partial<GithubCliAuthConfig>): Promise<Partial<GithubCliAuthConfig> | null> {
const $ = getPty();
const hostname = params.hostname ?? 'github.com';
const { status } = await $.spawnSafe(`gh auth status --hostname "${hostname}"`);
if (status === SpawnStatus.ERROR) return null;
const { data: tokenData, status: tokenStatus } = await $.spawnSafe(
`gh auth token --hostname "${hostname}"`
);
if (tokenStatus === SpawnStatus.ERROR) return { hostname };
return {
hostname,
token: tokenData.trim(),
};
}
async create(plan: CreatePlan<GithubCliAuthConfig>): Promise<void> {
const { token, hostname = 'github.com' } = plan.desiredConfig;
await this.login(token, hostname);
}
async modify(
_pc: unknown,
plan: ModifyPlan<GithubCliAuthConfig>
): Promise<void> {
const { token, hostname = 'github.com' } = plan.desiredConfig;
await this.login(token, hostname);
}
async destroy(plan: DestroyPlan<GithubCliAuthConfig>): Promise<void> {
const $ = getPty();
const hostname = plan.currentConfig.hostname ?? 'github.com';
const { data: statusData } = await $.spawnSafe(`gh auth status --hostname "${hostname}"`);
const userMatch = statusData.match(/Logged in to \S+ account (\S+)/);
const username = userMatch?.[1];
if (username) {
await $.spawnSafe(`gh auth logout --hostname "${hostname}" --user "${username}"`);
} else {
await $.spawnSafe(`gh auth logout --hostname "${hostname}"`);
}
}
private async login(token: string | undefined, hostname: string): Promise<void> {
const $ = getPty();
if (!token) {
await $.spawn(`gh auth login --hostname "${hostname}" --web`, { interactive: true, stdin: true });
return;
}
const tmpFile = path.join(os.tmpdir(), `.gh-token-${Date.now()}`);
await fs.writeFile(tmpFile, token.trim(), { mode: 0o600 });
try {
await $.spawn(`gh auth login --with-token --hostname "${hostname}" < "${tmpFile}"`, {
interactive: true,
});
} finally {
await fs.unlink(tmpFile).catch(() => {});
}
}
}