Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generate keypair and add to .env if one doesn't exist #5

Merged
merged 2 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 43 additions & 14 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { before, describe, test } from "node:test";
import { getKeypairFromEnvironment, getKeypairFromFile } from "./index";
import {
getKeypairFromEnvironment,
getKeypairFromFile,
addKeypairToEnvironment,
} from "./index";

import { Keypair } from "@solana/web3.js";
import assert from "node:assert/strict";
import base58 from "bs58";
import dotenv from "dotenv";
import { exec as execForOldPeople } from "child_process";
import { promisify } from "util";
import { unlinkSync } from "node:fs";
import { writeFile } from "node:fs/promises";
import { writeFile, unlink } from "node:fs/promises";
import dotenv from "dotenv";

const exec = promisify(execForOldPeople);

Expand Down Expand Up @@ -72,31 +75,57 @@ describe("getKeypairFromEnvironment", () => {
await getKeypairFromEnvironment(TEST_ENV_VAR_BASE58);
});

test("throws a nice error if the env var doesn't exist", async () => {
assert.rejects(async () => getKeypairFromEnvironment("MISSING_ENV_VAR"), {
message: `Please set 'MISSING_ENV_VAR' in environment.`,
});
});

test("throws a nice error if the value of the env var isn't valid", async () => {
assert.rejects(
async () => getKeypairFromEnvironment("TEST_ENV_VAR_WITH_BAD_VALUE"),
{
message: `Invalid secret key in environment variable 'TEST_ENV_VAR_WITH_BAD_VALUE'!`,
},
);
});
});

describe("addKeypairToEnvironment", () => {
let TEST_ENV_VAR_ARRAY_OF_NUMBERS = "TEST_ENV_VAR_ARRAY_OF_NUMBERS";

before(async () => {
const randomKeypair = Keypair.generate();

process.env[TEST_ENV_VAR_ARRAY_OF_NUMBERS] = JSON.stringify(
Object.values(randomKeypair.secretKey),
);
});

test("generates new keypair and writes to env if variable doesn't exist", async () => {
// Returns a new keypair and writes it to the .env file
const newKeypair = await getKeypairFromEnvironment("MISSING_ENV_VAR");
// Generates new keypair and writes it to the .env file
addKeypairToEnvironment("TEMP_KEYPAIR");
// Load the .env file
dotenv.config();
// Get the secret from the .env file
const secretKeyString = process.env["MISSING_ENV_VAR"];
const secretKeyString = process.env["TEMP_KEYPAIR"];

if (!secretKeyString) {
throw new Error("MISSING_ENV_VAR not found in environment");
throw new Error("TEMP_KEYPAIR not found in environment");
}
const decodedSecretKey = Uint8Array.from(JSON.parse(secretKeyString));
const envKeypair = Keypair.fromSecretKey(decodedSecretKey);

assert.deepStrictEqual(newKeypair.secretKey, envKeypair.secretKey);
assert.ok(envKeypair.secretKey);

// delete the .env file
unlinkSync(".env");
unlink(".env");
});

test("throws a nice error if the value of the env var isn't valid", async () => {
test("throws a nice error if the env var already exists", async () => {
assert.rejects(
async () => getKeypairFromEnvironment("TEST_ENV_VAR_WITH_BAD_VALUE"),
async () => addKeypairToEnvironment(TEST_ENV_VAR_ARRAY_OF_NUMBERS),
{
message: `Invalid secret key in environment variable 'TEST_ENV_VAR_WITH_BAD_VALUE'!`,
message: `'TEST_ENV_VAR_ARRAY_OF_NUMBERS' already exists in environment.`,
},
);
});
Expand Down
24 changes: 16 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Keypair } from "@solana/web3.js";
import base58 from "bs58";
import fs from "fs";
import path from "path";
import { readFile } from "fs/promises";
import { appendFileSync } from "node:fs";
const log = console.log;

// Default value from Solana CLI
Expand Down Expand Up @@ -46,13 +46,7 @@ export const getKeypairFromFile = async (filepath?: string) => {
export const getKeypairFromEnvironment = (variableName: string) => {
const secretKeyString = process.env[variableName];
if (!secretKeyString) {
// Generate a new keypair if one doesn't exist and add it to a `.env` file
const keypair = Keypair.generate();
fs.appendFileSync(
".env",
`\n${variableName}=${JSON.stringify(Array.from(keypair.secretKey))}`,
);
return keypair;
throw new Error(`Please set '${variableName}' in environment.`);
}

// Try the shorter base58 format first
Expand All @@ -79,3 +73,17 @@ export const getKeypairFromEnvironment = (variableName: string) => {
}
return Keypair.fromSecretKey(decodedSecretKey);
};

export const addKeypairToEnvironment = (variableName: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect something that adds a keypair to be addKeypairToEnvironment(keyPair: Keypair, name: string) - I think we might have discussed this on Slack.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separately, as your comment already notes, this adds a keypair to the .env file, rather than the environment itself. The function name should reflect that, ie addKeypairToEnvFile()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah it looks like you already updated this, just didn't reply! Woo I can merge in now.

const secretKeyString = process.env[variableName];
if (!secretKeyString) {
// Generate a new keypair if one doesn't exist and add it to a `.env` file
const keypair = Keypair.generate();
appendFileSync(
".env",
`\n${variableName}=${JSON.stringify(Array.from(keypair.secretKey))}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super important, but this (JSON.stringify(Array.from(keypair.secretKey))) is useful elsewhere so worth pulling out and exporting as keypairToJSON() or similar.

);
} else {
throw new Error(`'${variableName}' already exists in environment.`);
}
};