mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 19:28:09 +00:00
fetch apps from circleci
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
|||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -313,7 +314,40 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAppsCircleci = async ({ accessToken }: { accessToken: string }) => {
|
const getAppsCircleci = async ({ accessToken }: { accessToken: string }) => {
|
||||||
return [];
|
// in place of accessToken we have to send Circle-Token i.e. Personal API token from CircleCi
|
||||||
|
let apps: any;
|
||||||
|
try {
|
||||||
|
const circleciOrganizationDetail = (
|
||||||
|
await axios.get(`${INTEGRATION_CIRCLECI_API_URL}/v2/me/collaborations`, {
|
||||||
|
headers: {
|
||||||
|
"Circle-Token": accessToken,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
|
const { slug } = circleciOrganizationDetail;
|
||||||
|
|
||||||
|
const res = (
|
||||||
|
await axios.get(
|
||||||
|
`${INTEGRATION_CIRCLECI_API_URL}/v2/pipeline/?org-slug=${slug}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Circle-Token": accessToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).data.items;
|
||||||
|
|
||||||
|
apps = res.map((a: any) => ({
|
||||||
|
name: a?.project_slug?.split("/")[2],
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
throw new Error("Failed to get Render services");
|
||||||
|
}
|
||||||
|
|
||||||
|
return apps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getApps };
|
export { getApps };
|
||||||
|
|||||||
+203
-157
@@ -1,10 +1,10 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from "@sentry/node";
|
||||||
import { Octokit } from '@octokit/rest';
|
import { Octokit } from "@octokit/rest";
|
||||||
// import * as sodium from 'libsodium-wrappers';
|
// import * as sodium from 'libsodium-wrappers';
|
||||||
import sodium from 'libsodium-wrappers';
|
import sodium from "libsodium-wrappers";
|
||||||
// const sodium = require('libsodium-wrappers');
|
// const sodium = require('libsodium-wrappers');
|
||||||
import { IIntegration, IIntegrationAuth } from '../models';
|
import { IIntegration, IIntegrationAuth } from "../models";
|
||||||
import {
|
import {
|
||||||
INTEGRATION_HEROKU,
|
INTEGRATION_HEROKU,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
@@ -12,13 +12,15 @@ import {
|
|||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_HEROKU_API_URL,
|
INTEGRATION_HEROKU_API_URL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
INTEGRATION_FLYIO_API_URL
|
INTEGRATION_FLYIO_API_URL,
|
||||||
} from '../variables';
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
import { access, appendFile } from 'fs';
|
} from "../variables";
|
||||||
|
import { access, appendFile } from "fs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to [app] in integration named [integration]
|
* Sync/push [secrets] to [app] in integration named [integration]
|
||||||
@@ -32,7 +34,7 @@ const syncSecrets = async ({
|
|||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
integrationAuth,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
integrationAuth: IIntegrationAuth;
|
integrationAuth: IIntegrationAuth;
|
||||||
@@ -45,7 +47,7 @@ const syncSecrets = async ({
|
|||||||
await syncSecretsHeroku({
|
await syncSecretsHeroku({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_VERCEL:
|
case INTEGRATION_VERCEL:
|
||||||
@@ -53,7 +55,7 @@ const syncSecrets = async ({
|
|||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
integrationAuth,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_NETLIFY:
|
case INTEGRATION_NETLIFY:
|
||||||
@@ -61,35 +63,41 @@ const syncSecrets = async ({
|
|||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
integrationAuth,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_GITHUB:
|
case INTEGRATION_GITHUB:
|
||||||
await syncSecretsGitHub({
|
await syncSecretsGitHub({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_RENDER:
|
case INTEGRATION_RENDER:
|
||||||
await syncSecretsRender({
|
await syncSecretsRender({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_FLYIO:
|
case INTEGRATION_FLYIO:
|
||||||
await syncSecretsFlyio({
|
await syncSecretsFlyio({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
// case INTEGRATION_CIRCLECI:
|
||||||
|
// await syncSecretsCircleci({
|
||||||
|
// integration,
|
||||||
|
// secrets,
|
||||||
|
// accessToken,
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to integration');
|
throw new Error("Failed to sync secrets to integration");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,7 +111,7 @@ const syncSecrets = async ({
|
|||||||
const syncSecretsHeroku = async ({
|
const syncSecretsHeroku = async ({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
secrets: any;
|
secrets: any;
|
||||||
@@ -115,9 +123,9 @@ const syncSecretsHeroku = async ({
|
|||||||
`${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
|
`${INTEGRATION_HEROKU_API_URL}/apps/${integration.app}/config-vars`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/vnd.heroku+json; version=3',
|
Accept: "application/vnd.heroku+json; version=3",
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
@@ -133,15 +141,15 @@ const syncSecretsHeroku = async ({
|
|||||||
secrets,
|
secrets,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/vnd.heroku+json; version=3',
|
Accept: "application/vnd.heroku+json; version=3",
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to Heroku');
|
throw new Error("Failed to sync secrets to Heroku");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,10 +163,10 @@ const syncSecretsVercel = async ({
|
|||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
integrationAuth,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration,
|
integration: IIntegration;
|
||||||
integrationAuth: IIntegrationAuth,
|
integrationAuth: IIntegrationAuth;
|
||||||
secrets: any;
|
secrets: any;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -174,37 +182,52 @@ const syncSecretsVercel = async ({
|
|||||||
// Get all (decrypted) secrets back from Vercel in
|
// Get all (decrypted) secrets back from Vercel in
|
||||||
// decrypted format
|
// decrypted format
|
||||||
const params: { [key: string]: string } = {
|
const params: { [key: string]: string } = {
|
||||||
decrypt: 'true',
|
decrypt: "true",
|
||||||
...( integrationAuth?.teamId ? {
|
...(integrationAuth?.teamId
|
||||||
teamId: integrationAuth.teamId
|
? {
|
||||||
} : {})
|
teamId: integrationAuth.teamId,
|
||||||
}
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
|
||||||
const res = (await Promise.all((await axios.get(
|
const res = (
|
||||||
|
await Promise.all(
|
||||||
|
(
|
||||||
|
await axios.get(
|
||||||
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`,
|
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`,
|
||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
))
|
).data.envs
|
||||||
.data
|
.filter((secret: VercelSecret) =>
|
||||||
.envs
|
secret.target.includes(integration.targetEnvironment)
|
||||||
.filter((secret: VercelSecret) => secret.target.includes(integration.targetEnvironment))
|
)
|
||||||
.map(async (secret: VercelSecret) => (await axios.get(
|
.map(
|
||||||
|
async (secret: VercelSecret) =>
|
||||||
|
(
|
||||||
|
await axios.get(
|
||||||
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
|
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
|
||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)).data)
|
).data
|
||||||
)).reduce((obj: any, secret: any) => ({
|
)
|
||||||
|
)
|
||||||
|
).reduce(
|
||||||
|
(obj: any, secret: any) => ({
|
||||||
...obj,
|
...obj,
|
||||||
[secret.key]: secret
|
[secret.key]: secret,
|
||||||
}), {});
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
const updateSecrets: VercelSecret[] = [];
|
const updateSecrets: VercelSecret[] = [];
|
||||||
const deleteSecrets: VercelSecret[] = [];
|
const deleteSecrets: VercelSecret[] = [];
|
||||||
@@ -217,8 +240,8 @@ const syncSecretsVercel = async ({
|
|||||||
newSecrets.push({
|
newSecrets.push({
|
||||||
key: key,
|
key: key,
|
||||||
value: secrets[key],
|
value: secrets[key],
|
||||||
type: 'encrypted',
|
type: "encrypted",
|
||||||
target: [integration.targetEnvironment]
|
target: [integration.targetEnvironment],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -232,8 +255,8 @@ const syncSecretsVercel = async ({
|
|||||||
id: res[key].id,
|
id: res[key].id,
|
||||||
key: key,
|
key: key,
|
||||||
value: secrets[key],
|
value: secrets[key],
|
||||||
type: 'encrypted',
|
type: "encrypted",
|
||||||
target: [integration.targetEnvironment]
|
target: [integration.targetEnvironment],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -242,7 +265,7 @@ const syncSecretsVercel = async ({
|
|||||||
id: res[key].id,
|
id: res[key].id,
|
||||||
key: key,
|
key: key,
|
||||||
value: res[key].value,
|
value: res[key].value,
|
||||||
type: 'encrypted',
|
type: "encrypted",
|
||||||
target: [integration.targetEnvironment],
|
target: [integration.targetEnvironment],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -256,8 +279,8 @@ const syncSecretsVercel = async ({
|
|||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -265,18 +288,15 @@ const syncSecretsVercel = async ({
|
|||||||
// Sync/push updated secrets
|
// Sync/push updated secrets
|
||||||
if (updateSecrets.length > 0) {
|
if (updateSecrets.length > 0) {
|
||||||
updateSecrets.forEach(async (secret: VercelSecret) => {
|
updateSecrets.forEach(async (secret: VercelSecret) => {
|
||||||
const {
|
const { id, ...updatedSecret } = secret;
|
||||||
id,
|
|
||||||
...updatedSecret
|
|
||||||
} = secret;
|
|
||||||
await axios.patch(
|
await axios.patch(
|
||||||
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
|
`${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
|
||||||
updatedSecret,
|
updatedSecret,
|
||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -290,8 +310,8 @@ const syncSecretsVercel = async ({
|
|||||||
{
|
{
|
||||||
params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -299,9 +319,9 @@ const syncSecretsVercel = async ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to Vercel');
|
throw new Error("Failed to sync secrets to Vercel");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to Netlify site with id [integration.appId]
|
* Sync/push [secrets] to Netlify site with id [integration.appId]
|
||||||
@@ -315,7 +335,7 @@ const syncSecretsNetlify = async ({
|
|||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
integrationAuth,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
integrationAuth: IIntegrationAuth;
|
integrationAuth: IIntegrationAuth;
|
||||||
@@ -323,7 +343,6 @@ const syncSecretsNetlify = async ({
|
|||||||
accessToken: string;
|
accessToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
interface NetlifyValue {
|
interface NetlifyValue {
|
||||||
id?: string;
|
id?: string;
|
||||||
context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production',
|
context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production',
|
||||||
@@ -340,24 +359,27 @@ const syncSecretsNetlify = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getParams = new URLSearchParams({
|
const getParams = new URLSearchParams({
|
||||||
context_name: 'all', // integration.context or all
|
context_name: "all", // integration.context or all
|
||||||
site_id: integration.appId
|
site_id: integration.appId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = (await axios.get(
|
const res = (
|
||||||
|
await axios.get(
|
||||||
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
|
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
|
||||||
{
|
{
|
||||||
params: getParams,
|
params: getParams,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
))
|
).data.reduce(
|
||||||
.data
|
(obj: any, secret: any) => ({
|
||||||
.reduce((obj: any, secret: any) => ({
|
|
||||||
...obj,
|
...obj,
|
||||||
[secret.key]: secret
|
[secret.key]: secret,
|
||||||
}), {});
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
const newSecrets: NetlifySecret[] = []; // createEnvVars
|
const newSecrets: NetlifySecret[] = []; // createEnvVars
|
||||||
const deleteSecrets: string[] = []; // deleteEnvVar
|
const deleteSecrets: string[] = []; // deleteEnvVar
|
||||||
@@ -370,18 +392,22 @@ const syncSecretsNetlify = async ({
|
|||||||
// case: Infisical secret does not exist in Netlify -> create secret
|
// case: Infisical secret does not exist in Netlify -> create secret
|
||||||
newSecrets.push({
|
newSecrets.push({
|
||||||
key,
|
key,
|
||||||
values: [{
|
values: [
|
||||||
|
{
|
||||||
value: secrets[key],
|
value: secrets[key],
|
||||||
context: integration.targetEnvironment
|
context: integration.targetEnvironment,
|
||||||
}]
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// case: Infisical secret exists in Netlify
|
// case: Infisical secret exists in Netlify
|
||||||
const contexts = res[key].values
|
const contexts = res[key].values.reduce(
|
||||||
.reduce((obj: any, value: NetlifyValue) => ({
|
(obj: any, value: NetlifyValue) => ({
|
||||||
...obj,
|
...obj,
|
||||||
[value.context]: value
|
[value.context]: value,
|
||||||
}), {});
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
if (integration.targetEnvironment in contexts) {
|
if (integration.targetEnvironment in contexts) {
|
||||||
// case: Netlify secret value exists in integration context
|
// case: Netlify secret value exists in integration context
|
||||||
@@ -390,10 +416,12 @@ const syncSecretsNetlify = async ({
|
|||||||
// -> update Netlify secret context and value
|
// -> update Netlify secret context and value
|
||||||
updateSecrets.push({
|
updateSecrets.push({
|
||||||
key,
|
key,
|
||||||
values: [{
|
values: [
|
||||||
|
{
|
||||||
context: integration.targetEnvironment,
|
context: integration.targetEnvironment,
|
||||||
value: secrets[key]
|
value: secrets[key],
|
||||||
}]
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -401,14 +429,16 @@ const syncSecretsNetlify = async ({
|
|||||||
// -> add the new Netlify secret context and value
|
// -> add the new Netlify secret context and value
|
||||||
updateSecrets.push({
|
updateSecrets.push({
|
||||||
key,
|
key,
|
||||||
values: [{
|
values: [
|
||||||
|
{
|
||||||
context: integration.targetEnvironment,
|
context: integration.targetEnvironment,
|
||||||
value: secrets[key]
|
value: secrets[key],
|
||||||
}]
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// identify secrets to delete
|
// identify secrets to delete
|
||||||
// TODO: revise (patch case where 1 context was deleted but others still there
|
// TODO: revise (patch case where 1 context was deleted but others still there
|
||||||
@@ -428,11 +458,13 @@ const syncSecretsNetlify = async ({
|
|||||||
// case: Netlify secret value has more than 1 context -> delete secret value context
|
// case: Netlify secret value has more than 1 context -> delete secret value context
|
||||||
deleteSecretValues.push({
|
deleteSecretValues.push({
|
||||||
key,
|
key,
|
||||||
values: [{
|
values: [
|
||||||
|
{
|
||||||
id: value.id,
|
id: value.id,
|
||||||
context: integration.targetEnvironment,
|
context: integration.targetEnvironment,
|
||||||
value: value.value
|
value: value.value,
|
||||||
}]
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,7 +473,7 @@ const syncSecretsNetlify = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const syncParams = new URLSearchParams({
|
const syncParams = new URLSearchParams({
|
||||||
site_id: integration.appId
|
site_id: integration.appId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newSecrets.length > 0) {
|
if (newSecrets.length > 0) {
|
||||||
@@ -451,8 +483,8 @@ const syncSecretsNetlify = async ({
|
|||||||
{
|
{
|
||||||
params: syncParams,
|
params: syncParams,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -463,13 +495,13 @@ const syncSecretsNetlify = async ({
|
|||||||
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
|
`${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
|
||||||
{
|
{
|
||||||
context: secret.values[0].context,
|
context: secret.values[0].context,
|
||||||
value: secret.values[0].value
|
value: secret.values[0].value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
params: syncParams,
|
params: syncParams,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -482,8 +514,8 @@ const syncSecretsNetlify = async ({
|
|||||||
{
|
{
|
||||||
params: syncParams,
|
params: syncParams,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -496,8 +528,8 @@ const syncSecretsNetlify = async ({
|
|||||||
{
|
{
|
||||||
params: syncParams,
|
params: syncParams,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -505,9 +537,9 @@ const syncSecretsNetlify = async ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to Heroku');
|
throw new Error("Failed to sync secrets to Heroku");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to GitHub repo with name [integration.app]
|
* Sync/push [secrets] to GitHub repo with name [integration.app]
|
||||||
@@ -520,14 +552,13 @@ const syncSecretsNetlify = async ({
|
|||||||
const syncSecretsGitHub = async ({
|
const syncSecretsGitHub = async ({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
secrets: any;
|
secrets: any;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
interface GitHubRepoKey {
|
interface GitHubRepoKey {
|
||||||
key_id: string;
|
key_id: string;
|
||||||
key: string;
|
key: string;
|
||||||
@@ -546,41 +577,42 @@ const syncSecretsGitHub = async ({
|
|||||||
const deleteSecrets: GitHubSecret[] = [];
|
const deleteSecrets: GitHubSecret[] = [];
|
||||||
|
|
||||||
const octokit = new Octokit({
|
const octokit = new Octokit({
|
||||||
auth: accessToken
|
auth: accessToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
// const user = (await octokit.request('GET /user', {})).data;
|
// const user = (await octokit.request('GET /user', {})).data;
|
||||||
const repoPublicKey: GitHubRepoKey = (await octokit.request(
|
const repoPublicKey: GitHubRepoKey = (
|
||||||
'GET /repos/{owner}/{repo}/actions/secrets/public-key',
|
await octokit.request(
|
||||||
|
"GET /repos/{owner}/{repo}/actions/secrets/public-key",
|
||||||
{
|
{
|
||||||
owner: integration.owner,
|
owner: integration.owner,
|
||||||
repo: integration.app
|
repo: integration.app,
|
||||||
}
|
}
|
||||||
)).data;
|
)
|
||||||
|
).data;
|
||||||
|
|
||||||
// Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
|
// Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
|
||||||
const encryptedSecrets: GitHubSecretRes = (await octokit.request(
|
const encryptedSecrets: GitHubSecretRes = (
|
||||||
'GET /repos/{owner}/{repo}/actions/secrets',
|
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", {
|
||||||
{
|
|
||||||
owner: integration.owner,
|
owner: integration.owner,
|
||||||
repo: integration.app
|
repo: integration.app,
|
||||||
}
|
})
|
||||||
))
|
).data.secrets.reduce(
|
||||||
.data
|
(obj: any, secret: any) => ({
|
||||||
.secrets
|
|
||||||
.reduce((obj: any, secret: any) => ({
|
|
||||||
...obj,
|
...obj,
|
||||||
[secret.name]: secret
|
[secret.name]: secret,
|
||||||
}), {});
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
Object.keys(encryptedSecrets).map(async (key) => {
|
Object.keys(encryptedSecrets).map(async (key) => {
|
||||||
if (!(key in secrets)) {
|
if (!(key in secrets)) {
|
||||||
await octokit.request(
|
await octokit.request(
|
||||||
'DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}',
|
"DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}",
|
||||||
{
|
{
|
||||||
owner: integration.owner,
|
owner: integration.owner,
|
||||||
repo: integration.app,
|
repo: integration.app,
|
||||||
secret_name: key
|
secret_name: key,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -606,13 +638,13 @@ const syncSecretsGitHub = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
await octokit.request(
|
await octokit.request(
|
||||||
'PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}',
|
"PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}",
|
||||||
{
|
{
|
||||||
owner: integration.owner,
|
owner: integration.owner,
|
||||||
repo: integration.app,
|
repo: integration.app,
|
||||||
secret_name: key,
|
secret_name: key,
|
||||||
encrypted_value: encryptedSecret,
|
encrypted_value: encryptedSecret,
|
||||||
key_id: repoPublicKey.key_id
|
key_id: repoPublicKey.key_id,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -620,7 +652,7 @@ const syncSecretsGitHub = async ({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to GitHub');
|
throw new Error("Failed to sync secrets to GitHub");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -634,7 +666,7 @@ const syncSecretsGitHub = async ({
|
|||||||
const syncSecretsRender = async ({
|
const syncSecretsRender = async ({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
secrets: any;
|
secrets: any;
|
||||||
@@ -645,20 +677,20 @@ const syncSecretsRender = async ({
|
|||||||
`${INTEGRATION_RENDER_API_URL}/v1/services/${integration.appId}/env-vars`,
|
`${INTEGRATION_RENDER_API_URL}/v1/services/${integration.appId}/env-vars`,
|
||||||
Object.keys(secrets).map((key) => ({
|
Object.keys(secrets).map((key) => ({
|
||||||
key,
|
key,
|
||||||
value: secrets[key]
|
value: secrets[key],
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to Render');
|
throw new Error("Failed to sync secrets to Render");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to Fly.io app
|
* Sync/push [secrets] to Fly.io app
|
||||||
@@ -670,7 +702,7 @@ const syncSecretsRender = async ({
|
|||||||
const syncSecretsFlyio = async ({
|
const syncSecretsFlyio = async ({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
secrets: any;
|
secrets: any;
|
||||||
@@ -700,19 +732,22 @@ const syncSecretsFlyio = async ({
|
|||||||
|
|
||||||
await axios({
|
await axios({
|
||||||
url: INTEGRATION_FLYIO_API_URL,
|
url: INTEGRATION_FLYIO_API_URL,
|
||||||
method: 'post',
|
method: "post",
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + accessToken
|
Authorization: "Bearer " + accessToken,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
query: SetSecrets,
|
query: SetSecrets,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
appId: integration.app,
|
appId: integration.app,
|
||||||
secrets: Object.entries(secrets).map(([key, value]) => ({ key, value }))
|
secrets: Object.entries(secrets).map(([key, value]) => ({
|
||||||
}
|
key,
|
||||||
}
|
value,
|
||||||
}
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// get secrets
|
// get secrets
|
||||||
@@ -732,20 +767,22 @@ const syncSecretsFlyio = async ({
|
|||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const getSecretsRes = (await axios({
|
const getSecretsRes = (
|
||||||
method: 'post',
|
await axios({
|
||||||
|
method: "post",
|
||||||
url: INTEGRATION_FLYIO_API_URL,
|
url: INTEGRATION_FLYIO_API_URL,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + accessToken,
|
Authorization: "Bearer " + accessToken,
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
query: GetSecrets,
|
query: GetSecrets,
|
||||||
variables: {
|
variables: {
|
||||||
appName: integration.app
|
appName: integration.app,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
})).data.data.app.secrets;
|
})
|
||||||
|
).data.data.app.secrets;
|
||||||
|
|
||||||
const deleteSecretsKeys = getSecretsRes
|
const deleteSecretsKeys = getSecretsRes
|
||||||
.filter((secret: FlyioSecret) => !(secret.name in secrets))
|
.filter((secret: FlyioSecret) => !(secret.name in secrets))
|
||||||
@@ -771,28 +808,37 @@ const syncSecretsFlyio = async ({
|
|||||||
}`;
|
}`;
|
||||||
|
|
||||||
await axios({
|
await axios({
|
||||||
method: 'post',
|
method: "post",
|
||||||
url: INTEGRATION_FLYIO_API_URL,
|
url: INTEGRATION_FLYIO_API_URL,
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + accessToken,
|
Authorization: "Bearer " + accessToken,
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
query: DeleteSecrets,
|
query: DeleteSecrets,
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
appId: integration.app,
|
appId: integration.app,
|
||||||
keys: deleteSecretsKeys
|
keys: deleteSecretsKeys,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to Fly.io');
|
throw new Error("Failed to sync secrets to Fly.io");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// const syncSecretsCircleci = async ({
|
||||||
|
// integration,
|
||||||
|
// secrets,
|
||||||
|
// accessToken,
|
||||||
|
// }: {
|
||||||
|
// integration: IIntegration;
|
||||||
|
// secrets: any;
|
||||||
|
// accessToken: string;
|
||||||
|
// }) => {};
|
||||||
|
|
||||||
export { syncSecrets };
|
export { syncSecrets };
|
||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
INTEGRATION_OPTIONS,
|
INTEGRATION_OPTIONS,
|
||||||
} from "./integration";
|
} from "./integration";
|
||||||
import { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED } from "./organization";
|
import { OWNER, ADMIN, MEMBER, INVITED, ACCEPTED } from "./organization";
|
||||||
@@ -69,6 +70,7 @@ export {
|
|||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
EVENT_PUSH_SECRETS,
|
EVENT_PUSH_SECRETS,
|
||||||
EVENT_PULL_SECRETS,
|
EVENT_PULL_SECRETS,
|
||||||
ACTION_ADD_SECRETS,
|
ACTION_ADD_SECRETS,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com";
|
|||||||
const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com";
|
const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com";
|
||||||
const INTEGRATION_RENDER_API_URL = "https://api.render.com";
|
const INTEGRATION_RENDER_API_URL = "https://api.render.com";
|
||||||
const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql";
|
const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql";
|
||||||
const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api/v2";
|
const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api";
|
||||||
|
|
||||||
const INTEGRATION_OPTIONS = [
|
const INTEGRATION_OPTIONS = [
|
||||||
{
|
{
|
||||||
@@ -164,5 +164,6 @@ export {
|
|||||||
INTEGRATION_NETLIFY_API_URL,
|
INTEGRATION_NETLIFY_API_URL,
|
||||||
INTEGRATION_RENDER_API_URL,
|
INTEGRATION_RENDER_API_URL,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
INTEGRATION_OPTIONS,
|
INTEGRATION_OPTIONS,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user