Skip to content
This repository has been archived by the owner on Sep 19, 2024. It is now read-only.

Commit

Permalink
feat!: clean up resources
Browse files Browse the repository at this point in the history
  • Loading branch information
jgoux authored Oct 6, 2022
2 parents c6618a7 + cea96d5 commit 42067c6
Show file tree
Hide file tree
Showing 21 changed files with 337 additions and 111 deletions.
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,29 @@ This plugin gives you a new and isolated database for your preview deployments i
#### Inputs

```yaml
- name: databaseEnvVar
required: false
description: Database environment variable name
default: "DATABASE_URL"

- name: databaseCreateCommand
required: false
description: Command used to generate the instant database
description: Command used to create the preview database
default: "snaplet db create --git --latest"

- name: databaseDeleteCommand
required: false
description: Command used to delete the preview database
default: "snaplet db delete --git"

- name: databaseUrlCommand
required: false
description: Command used to get the instant database url
description: Command used to get the preview database url
default: "snaplet db url --git"

- name: databaseUrlEnvKey
required: false
description: Preview database environment variable key
default: "DATABASE_URL"

- name: reset
required: false
description: Reset the database state on each commit
description: Reset the preview database state on each commit
default: false
```
Expand All @@ -78,10 +83,15 @@ This plugin gives you a new and isolated database for your preview deployments i

#### Required Environment variables

```
NETLIFY_ACCESS_TOKEN=// API Access token found in Netlify user settings.
SNAPLET_ACCESS_TOKEN=// CLI Access token found in Snaplet UI
SNAPLET_PROJECT_ID=// Project ID found in Snaplet project settings.
```bash
# Personal Access Token with "repo" scope, found in GitHub user settings
GITHUB_ACCESS_TOKEN=
# API Access token found in Netlify user settings
NETLIFY_ACCESS_TOKEN=
# CLI Access token found in Snaplet UI
SNAPLET_ACCESS_TOKEN=
# Project ID found in Snaplet project settings
SNAPLET_PROJECT_ID=
```

## How it works
Expand Down
5 changes: 5 additions & 0 deletions jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"checkJs": true
}
}
21 changes: 13 additions & 8 deletions manifest.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
name: snaplet-netlify-plugin-preview-db

inputs:
- name: databaseEnvVar
required: false
description: Database environment variable name
default: "DATABASE_URL"

- name: databaseCreateCommand
required: false
description: Command used to generate the instant database
description: Command used to create the preview database
default: "snaplet db create --git --latest"

- name: databaseDeleteCommand
required: false
description: Command used to delete the preview database
default: "snaplet db delete --git"

- name: databaseUrlCommand
required: false
description: Command used to get the instant database url
description: Command used to get the preview database url
default: "snaplet db url --git"

- name: databaseUrlEnvKey
required: false
description: Preview database environment variable key
default: "DATABASE_URL"

- name: reset
required: false
description: Reset the database state on each commit
description: Reset the preview database state on each commit
default: false
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@snaplet/netlify-preview-database-plugin",
"version": "1.1.0",
"version": "2.0.0",
"type": "module",
"main": "src/index.js",
"license": "MIT",
Expand Down
7 changes: 0 additions & 7 deletions src/getPreviewDatabaseUrl.js

This file was deleted.

11 changes: 11 additions & 0 deletions src/github/getAssociatedPullRequests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { github } from "./github.js";

/**
* @param {{ commitRef: string }} options
*/
export async function getAssociatedPullRequests(options) {
/** @type {{ head: { ref: string }}[]} */
const associatedPullRequests = await github(`commits/${options.commitRef}/pulls`);

return associatedPullRequests;
}
26 changes: 26 additions & 0 deletions src/github/github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import fetch from "node-fetch";

const [OWNER, REPOSITORY] = new URL(process.env.REPOSITORY_URL).pathname.slice(1).split("/");

/**
* @template T
* @param {string} path
* @returns {Promise<T>}
*/
export async function github(path, options) {
const response = await fetch(`https://api.github.com/repos/${OWNER}/${REPOSITORY}/${path}`, {
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
...options
});

if (!response.ok) {
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
}

// @ts-ignore
return await response.json();
}
34 changes: 34 additions & 0 deletions src/handleDeployPreview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createPreviewDatabase } from "./snaplet/previewDatabase/createPreviewDatabase.js"
import { installSnapletCLI } from "./snaplet/installSnapletCLI.js";
import { setEnvironmentVariable } from "./netlify/environmentVariable/setEnvironmentVariable.js";

/**
* @param {{
* utils: { run: { command: (cmd: string, options: Record<string, any>) => { stdout: string } } },
* inputs: { databaseCreateCommand: string, databaseUrlCommand: string, databaseUrlEnvKey: string, reset: boolean }
* }} config
*/
export async function handleDeployPreview({
utils: { run },
inputs: {
databaseCreateCommand,
databaseUrlCommand,
databaseUrlEnvKey,
reset,
},
}) {
await installSnapletCLI({ run });

const databaseUrl = await createPreviewDatabase({ run }, {
databaseCreateCommand,
databaseUrlCommand,
reset,
});

await setEnvironmentVariable({
siteId: process.env.SITE_ID,
branch: process.env.HEAD,
key: databaseUrlEnvKey,
value: databaseUrl,
});
};
34 changes: 34 additions & 0 deletions src/handleDeployProduction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { installSnapletCLI } from "./snaplet/installSnapletCLI.js";
import { getAssociatedPullRequests } from "./github/getAssociatedPullRequests.js";
import { deletePreviewDatabase } from "./snaplet/previewDatabase/deletePreviewDatabase.js";
import { deleteEnvironmentVariable } from "./netlify/environmentVariable/deleteEnvironmentVariable.js";

/**
* @param {{
* utils: { run: { command: (cmd: string, options: Record<string, any>) => { stdout: string } } },
* inputs: { databaseDeleteCommand: string, databaseUrlCommand: string, databaseUrlEnvKey: string }
* }} config
*/
export async function handleDeployProduction({
utils: { run },
inputs: {
databaseDeleteCommand,
databaseUrlCommand,
databaseUrlEnvKey,
},
}) {
const associatedPullRequests = await getAssociatedPullRequests({ commitRef: process.env.COMMIT_REF });

if (associatedPullRequests.length > 0) {
await installSnapletCLI({ run });

await Promise.all(associatedPullRequests.map(async (pullRequest) => {
const branch = pullRequest.head.ref;

await Promise.all([
deleteEnvironmentVariable({ branch, key: databaseUrlEnvKey, siteId: process.env.SITE_ID }),
deletePreviewDatabase({ run }, { branch, databaseDeleteCommand, databaseUrlCommand }),
]);
}));
}
}
52 changes: 23 additions & 29 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
import { createPreviewDatabase } from "./createPreviewDatabase.js"
import { installSnapletCLI } from "./installSnapletCLI.js";
import { setEnvironmentVariable } from "./setEnvironmentVariable.js";
import { handleDeployPreview } from "./handleDeployPreview.js";
import { handleDeployProduction } from "./handleDeployProduction.js";

export async function onPreBuild({
utils: { run },
constants,
netlifyConfig,
inputs: {
databaseEnvVar,
databaseCreateCommand,
databaseUrlCommand,
reset,
},
}) {
if (process.env.CONTEXT === "deploy-preview") {
await installSnapletCLI({ run });

const databaseUrl = await createPreviewDatabase({ run }, {
databaseCreateCommand,
databaseUrlCommand,
reset,
});

await setEnvironmentVariable({
siteId: constants.SITE_ID,
branch: netlifyConfig.build.environment.BRANCH,
key: databaseEnvVar,
value: databaseUrl,
});
/**
* @param {{
* utils: { run: { command: (cmd: string, options: Record<string, any>) => { stdout: string } } },
* inputs: { databaseCreateCommand: string, databaseDeleteCommand: string, databaseUrlCommand: string, databaseUrlEnvKey: string, reset: boolean }
* }} config
*/
export async function onPreBuild(config) {
switch (process.env.CONTEXT) {
case "deploy-preview":
await handleDeployPreview(config);
break;
case "production":
try {
await handleDeployProduction(config);
} catch (e) {
// We don't want the production build to fail because of resources clean up
console.error(`Failed to clean up resources for this commit`);
console.error(e);
}
break;
default:
}
};
27 changes: 27 additions & 0 deletions src/netlify/environmentVariable/deleteEnvironmentVariable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getAccountId } from "./getAccountId.js";
import { netlify } from "../netlify.js";
import { getEnvironmentVariable } from "./getEnvironmentVariable.js";

/**
* @param {{ siteId: string, branch: string, key: string }} options
*/
export async function deleteEnvironmentVariable(options) {
const { siteId, branch, key } = options;

const accountId = await getAccountId({ siteId });

const environmentVariable = await getEnvironmentVariable({ accountId, siteId, key });

const environmentVariableValue = environmentVariable?.values?.find(v => v.context === "branch" && v.context_parameter === branch);

if (environmentVariableValue) {
console.log(`Deleting environment variable ${key} for branch ${branch}...`);
await netlify(
`accounts/${accountId}/env/${key}/value/${environmentVariableValue.id}?site_id=${siteId}`,
{
method: "DELETE",
}
);
console.log(`Environment variable ${key} for branch ${branch} deleted.`);
}
}
15 changes: 15 additions & 0 deletions src/netlify/environmentVariable/getAccountId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { netlify } from "../netlify.js";

/**
* @param {{ siteId: string }} options
*/
export async function getAccountId(options) {
const { siteId } = options;

/** @type [{ account_name: string, account_slug: string }, { id: string, name: string, slug: string }[]] */
const [{ account_name, account_slug }, accounts] = await Promise.all([netlify(`sites/${siteId}`), netlify("accounts")]);

const account = accounts.find(account => account.name === account_name && account.slug === account_slug);

return account.id;
}
18 changes: 18 additions & 0 deletions src/netlify/environmentVariable/getEnvironmentVariable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { netlify } from "../netlify.js";

/**
* @param {{ accountId: string, siteId: string, key: string }} options
* @returns {Promise<{ values: { id: string, context: string, context_parameter?: string }[]} | null>}
*/
export async function getEnvironmentVariable(options) {
const { accountId, siteId, key } = options;

try {
/** @type {{ values: { id: string, context: string, context_parameter?: string }[]}} */
const environmentVariable = await netlify(`accounts/${accountId}/env/${key}?site_id=${siteId}`);

return environmentVariable;
} catch (_) {
return null;
}
}
18 changes: 18 additions & 0 deletions src/netlify/environmentVariable/isEnvironmentVariableDeployed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getEnvironmentVariable } from "./getEnvironmentVariable.js";

/**
* @param {{ accountId: string, siteId: string, key: string, branch: string }} options
*/
export async function isEnvironmentVariableDeployed(options) {
const { accountId, siteId, key, branch } = options;

let environmentVariableIsDeployed = false;

const environmentVariable = await getEnvironmentVariable({ accountId, siteId, key });

if (environmentVariable) {
environmentVariableIsDeployed = environmentVariable.values.some(v => v.context === "branch" && v.context_parameter === branch);
}

return environmentVariableIsDeployed;
}
Loading

0 comments on commit 42067c6

Please sign in to comment.