Skip to content

Instantly share code, notes, and snippets.

@dgellow
Last active September 26, 2023 10:02
Show Gist options
  • Save dgellow/5f6ad9c616b7b0859941f99e42681e95 to your computer and use it in GitHub Desktop.
Save dgellow/5f6ad9c616b7b0859941f99e42681e95 to your computer and use it in GitHub Desktop.
Pulumi dynamic provider to set/update the Webhook config of a GitHub App
import * as pulumi from '@pulumi/pulumi';
import { Octokit } from '@octokit/core';
import { AppAuthentication, createAppAuth } from '@octokit/auth-app';
export interface ConfigurationResourceInputs {
appId: pulumi.Input<string>;
privateKey: pulumi.Input<string>;
clientId: pulumi.Input<string>;
clientSecret: pulumi.Input<string>;
url: pulumi.Input<string>;
secret: pulumi.Input<string>;
contentType?: pulumi.Input<string> | undefined;
}
export class Configuration extends pulumi.dynamic.Resource {
public readonly appId!: pulumi.Output<pulumi.ID>;
public readonly privateKey!: pulumi.Output<string>;
public readonly clientId!: pulumi.Output<string>;
public readonly clientSecret!: pulumi.Output<string>;
public readonly url!: pulumi.Output<string>;
public readonly secret!: pulumi.Output<string>;
public readonly contentType!: pulumi.Output<string>;
constructor(name: string, args: ConfigurationResourceInputs, opts?: pulumi.CustomResourceOptions) {
super(new ConfigurationProvider(), name, args, opts, 'github-app', 'Configuration');
}
}
interface ConfigurationInputs {
appId: string;
privateKey: string;
clientId: string;
clientSecret: string;
url: string;
secret: string;
contentType?: string | undefined;
}
export interface ConfigurationOutputs extends Omit<ConfigurationInputs, 'url' | 'secret'> {
url?: string | undefined;
secret?: string | undefined;
}
export class ConfigurationProvider implements pulumi.dynamic.ResourceProvider {
async auth({
appId,
privateKey,
clientId,
clientSecret,
}: Pick<
ConfigurationInputs,
'appId' | 'clientId' | 'clientSecret' | 'privateKey'
>): Promise<AppAuthentication> {
const auth = createAppAuth({
appId: appId,
privateKey: privateKey,
clientId: clientId,
clientSecret: clientSecret,
});
return await auth({ type: 'app' });
}
async create(inputs: ConfigurationInputs): Promise<pulumi.dynamic.CreateResult> {
const auth = await this.auth(inputs);
const octokit = new Octokit({ auth: auth.token });
try {
const { data } = await octokit.request('PATCH /app/hook/config', {
data: JSON.stringify({
url: inputs.url,
secret: inputs.secret,
insecure_ssl: '0',
content_type: inputs.contentType || 'json',
}),
});
return {
id: inputs.appId,
outs: {
appId: inputs.appId,
privateKey: inputs.privateKey,
clientId: inputs.clientId,
clientSecret: inputs.clientSecret,
url: data.url,
secret: data.secret,
contentType: data.content_type,
},
};
} catch (err) {
await pulumi.log.error(`${JSON.stringify(err, null, 2)}`);
throw new Error('Failed to set github app webhook config');
}
}
async diff(
id: pulumi.ID,
olds: ConfigurationOutputs,
news: ConfigurationInputs,
): Promise<pulumi.dynamic.DiffResult> {
let changes: boolean = false;
let replaces: string[] = [];
let keys = Object.keys(news) as Array<Extract<keyof typeof news, string>>;
for (const key of keys) {
if (JSON.stringify(olds[key]) !== JSON.stringify(news[key])) {
changes = true;
if (key === 'appId' || key === 'privateKey' || key === 'clientId' || key === 'clientSecret') {
replaces.push(key);
}
}
}
return { changes, replaces };
}
async update(
id: pulumi.ID,
_olds: ConfigurationOutputs,
news: ConfigurationInputs,
): Promise<pulumi.dynamic.UpdateResult> {
const auth = await this.auth(news);
const octokit = new Octokit({ auth: auth.token });
try {
const { data } = await octokit.request('PATCH /app/hook/config', {
data: JSON.stringify({
url: news.url,
secret: news.secret,
insecure_ssl: '0',
content_type: news.contentType || 'json',
}),
});
return {
outs: {
appId: id,
privateKey: news.privateKey,
clientId: news.clientId,
clientSecret: news.clientSecret,
url: data.url,
secret: data.secret,
contentType: data.content_type,
},
};
} catch (err) {
await pulumi.log.error(`${JSON.stringify(err, null, 2)}`);
throw new Error('Failed to set github app webhook config');
}
}
async read(id: pulumi.ID, props?: ConfigurationOutputs): Promise<pulumi.dynamic.ReadResult> {
if (!props) {
throw new Error('Props undefined');
}
const auth = await this.auth({ ...props, appId: id });
const octokit = new Octokit({ auth: auth.token });
try {
const { data } = await octokit.request('GET /app/hook/config');
return {
id,
props: {
appId: id,
privateKey: props.privateKey,
clientId: props.clientId,
clientSecret: props.clientSecret,
url: data.url,
secret: data.secret,
contentType: data.content_type,
},
};
} catch (err) {
await pulumi.log.error(`${JSON.stringify(err, null, 2)}`);
throw new Error('Failed to set github app webhook config');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment