Skip to content

Commit

Permalink
feat: add dev env setup, deployment, debug etc. (#2)
Browse files Browse the repository at this point in the history
feat: add dev env setup, deployment, debug etc.
  • Loading branch information
moromis authored Mar 22, 2024
1 parent 9f1c238 commit e9d93fe
Show file tree
Hide file tree
Showing 38 changed files with 678 additions and 141 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
config.json
.*.env
.env

node_modules
Expand All @@ -19,4 +20,6 @@ template.old.yaml

coverage/

chores.json
chores.json

scheduler_role.json
58 changes: 58 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug weekly",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/modules/jobs/weekly/debug.js",
"envFile": "${workspaceFolder}/.dev.env",
},
{
"type": "node",
"request": "launch",
"name": "Debug daily",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/modules/jobs/daily/debug.js",
"envFile": "${workspaceFolder}/.dev.env",
},
{
"type": "node",
"request": "launch",
"name": "Debug monthly",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/modules/jobs/monthly/debug.js",
"envFile": "${workspaceFolder}/.dev.env",
},
{
"type": "node",
"request": "launch",
"name": "Debug complete",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/modules/debug-commands/debug-complete.js",
"envFile": "${workspaceFolder}/.dev.env",
},
{
"type": "node",
"request": "launch",
"name": "Debug assign",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/modules/debug-commands/debug-assign.js",
"envFile": "${workspaceFolder}/.dev.env",
}
]
}
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@
</a>
</p>

## NOTE
Best practice: make two AWS accounts and then configure each with `aws configure --profile <PROFILE_NAME>`, then use `AWS_PROFILE=<PROFILE_NAME>` before yarn commands, e.g. `AWS_PROFILE=cb-dev yarn setup-scheduler`

Also, specifically for generating the SAM template, use `DEV=true yarn generate-template` for your dev environment. It will use a file called `.dev.env` in the root environment if you've created one. You can place your dev AWS credentials there.

Make sure to slide the slider for your Discord bot under `Bot > Privileged Gateway Intents > SERVER MEMBERS INTENT`

## Setup (instructions still in progress)
1. Create a Discord channel for the Chore Bot, and enable developer mode so you can copy IDs from Discord
2. Fill out `.example_env` and rename to `.env`
3. Run `yarn install-deps` to install project dependencies
4. Run `yarn setup`
5. Modify `setup/tables/example_chores.json` as desired so it matches your chore list, then rename it to `chores.json`
6. Create an AWS account
7. Run `yarn setup-iam` (in-progress feature, use created `Bot` user for cli)
8. Run `yarn setup-scheduler`
9. Deploy with `yarn deploy`
10. Upload the initial chores data with `yarn upload-chores-list`
11. There will now be daily, weekly, and monthly scheduled jobs that will take care of all chore assignments etc., and slash commands will be registered in your Discord guild
7. Run `yarn setup-iam` (in-progress feature, use created `Bot` user for cli)
8. In the AWS console, navigate to iam/users/Bot, then create an access key for the user. Run `aws configure --profile default` and use the access key and secret access key.
9. Run `yarn setup-scheduler`
10. Deploy with `yarn deploy`
11. Upload the initial chores data with `yarn upload-chores-list`
12. There will now be daily, weekly, and monthly scheduled jobs that will take care of all chore assignments etc., and slash commands will be registered in your Discord guild


## Features
Expand Down
1 change: 1 addition & 0 deletions _design/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ users
displayName
numCycleChores
numAllTimeChores
inactive
* currentChore (-> chores)

chores
Expand Down
4 changes: 0 additions & 4 deletions deploy.sh

This file was deleted.

13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"author": "moromis",
"version": "0.0.1",
"private": true,
"license": "./LICENSE.md",
"dependencies": {
"eslint-plugin-require-path-exists": "^1.1.9"
},
Expand All @@ -16,16 +17,24 @@
"recursive-install": "^1.4.0"
},
"scripts": {
"deploy": "./deploy.sh",
"deploy": "./scripts/deploy.sh",
"dev:deploy": "export AWS_PROFILE=cb-dev && export DEV=true && ./scripts/deploy.sh",
"upload-chores-list": "cd setup/tables && node upload_chores.js",
"dev:upload-chores-list": "export AWS_PROFILE=cb-dev && cd setup/tables && node upload_chores.js",
"register-commands": "cd register_commands && node register.js",
"dev:register-commands": "export AWS_PROFILE=cb-dev && export DEV=true && cd register_commands && node register.js",
"destroy": "cd scripts && ./destroy.sh",
"dev:destroy": "export AWS_PROFILE=cb-dev && cd scripts && ./destroy.sh",
"test": "jest --passWithNoTests src",
"test:coverage": "yarn test --config jest.config.json --no-cache",
"test:badge": "yarn test:coverage && yarn make-coverage-badge --output-path ./cov-badge.svg",
"generate-template": "cd setup/generate_template && yarn && node generate_template.js",
"setup-scheduler": "cd setup/scheduler && ./setup_scheduler.sh",
"dev:setup-scheduler": "export AWS_PROFILE=cb-dev && export DEV=true && cd setup/scheduler && ./setup_scheduler.sh",
"setup-iam": "cd setup/iam_user && ./setup_iam_user_and_policy.sh",
"dev:setup-iam": "export AWS_PROFILE=cb-dev && cd setup/iam_user && ./setup_iam_user_and_policy.sh",
"setup": "yarn generate-template",
"install-deps": "yarn && node recursive-install-yarn.js --rootDir=src"
"dev:setup": "export AWS_PROFILE=cb-dev && yarn generate-template",
"install-deps": "yarn && node scripts/recursive-install-yarn.js --rootDir=src"
}
}
6 changes: 5 additions & 1 deletion register_commands/register.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const dotenv = require("dotenv");
const register = require("./misc/send.js").register;
dotenv.config();
dotenv.config({
path: !!process.env.DEV === true ? "../.dev.env" : "../.env",
});

console.log("registering commands using AWS account ", process.env.AWS_ID);

if (!process.env.APP_ID || !process.env.BOT_TOKEN) {
throw "You must define APP_ID and BOT_TOKEN in .env file or in a command line run (e.g. APP_ID=1234 BOT_TOKEN=ABCD node register_commands/register.js)";
Expand Down
2 changes: 1 addition & 1 deletion samconfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ version = 0.1
stack_name = "sam-app"

[default.build.parameters]
cached = true
cached = false
parallel = true

[default.deploy.parameters]
Expand Down
8 changes: 8 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
echo $DEV
echo $AWS_PROFILE
rm -rf .aws-sam
yarn
yarn generate-template
yarn register-commands
sam build
sam deploy
5 changes: 5 additions & 0 deletions scripts/destroy.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
echo $AWS_PROFILE

# from https://stackoverflow.com/questions/1885525/how-do-i-prompt-a-user-for-confirmation-in-bash-script
read -p "This will delete EVERYTHING and you will have to re-deploy. All your data will be GONE. Are you sure? (y/n)" -n 1 -r
echo # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
cd ..
sam delete
echo "Done with sam delete"
aws cloudformation delete-stack --stack-name sam-app
echo "Done with cloudformation delete-stack"
fi
File renamed without changes.
9 changes: 7 additions & 2 deletions setup/generate_template/generate_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ const fs = require("fs");
const {
create_template_yaml_string,
} = require("./create_template_yaml_string");
dotenv.config({ path: "../../.env" });
dotenv.config({
path: !!process.env.DEV === true ? "../../.dev.env" : "../../.env",
});

console.log("generating template using AWS account ", process.env.AWS_ID);

if (
!process.env.PUBLIC_KEY ||
!process.env.CHANNEL_ID ||
!process.env.BOT_TOKEN ||
!process.env.GUILD_ID
!process.env.GUILD_ID ||
!process.env.AWS_ID
) {
throw "You must define all variables in the .env file (see readme for more details)";
}
Expand Down
30 changes: 30 additions & 0 deletions setup/scheduler/create_scheduler_role.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const dotenv = require("dotenv");
const path = require("path");
const fs = require("fs");
const { create_template_string } = require("./scheduler_role_template");
dotenv.config({
path: !!process.env.DEV === true ? "../../.dev.env" : "../../.env",
});

console.log(
"generating scheduler role json using AWS account ",
process.env.AWS_ID
);

if (!process.env.AWS_ID) {
throw "You must define all variables in the .env file (see readme for more details)";
}

const main = () => {
const template = create_template_string();

fs.writeFileSync(
path.join(__dirname, "scheduler_role.json"),
template,
"utf-8"
);
console.log("File scheduler_role.json generated successfully.");
return;
};

main();
10 changes: 10 additions & 0 deletions setup/scheduler/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "scheduler",
"version": "1.0.0",
"main": "index.js",
"author": "Kevin Ulrich",
"license": "MIT",
"dependencies": {
"dotenv": "^16.4.5"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
exports.create_template_string = () => `{
"Version": "2012-10-17",
"Statement": [
{
Expand All @@ -9,10 +9,10 @@
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "ACCOUNT_ID",
"aws:SourceArn": "COPY_FROM_SCHEDULER_GROUP_PAGE"
"aws:SourceAccount": "${process.env.AWS_ID}",
"aws:SourceArn": "arn:aws:scheduler:us-east-2:${process.env.AWS_ID}:schedule-group/default"
}
}
}
]
}
}`;
5 changes: 5 additions & 0 deletions setup/scheduler/setup_scheduler.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
user_id=$(aws sts get-caller-identity --query 'Account' --output text)
echo $DEV
echo $AWS_PROFILE
echo $user_id

yarn
node create_scheduler_role.js
aws iam create-role --role-name SchedulerExecutionRole --assume-role-policy-document file://scheduler_role.json
aws iam create-policy --policy-name SchedulerPolicy --policy-document file://scheduler_policy.json
aws iam attach-role-policy --policy-arn arn:aws:iam::${user_id}:policy/SchedulerPolicy --role-name SchedulerExecutionRole
8 changes: 8 additions & 0 deletions setup/scheduler/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


dotenv@^16.4.5:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
8 changes: 6 additions & 2 deletions src/helpers/getChoreMessage.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
exports.getChoreMessage = (chore) =>
`**Chore**: ${chore.displayName}\n**Description**: ${chore.description}`;
exports.getChoreMessage = (chore) => {
if (!chore || !chore.displayName || !chore.description) {
return "Chore not found";
}
return `**Chore**: ${chore.displayName}\n**Description**: ${chore.description}\n**Reviewer**: <@${chore.reviewer}>`;
};
4 changes: 2 additions & 2 deletions src/helpers/getChoreMessage.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { getChoreMessage } = require("./getChoreMessage");

describe("getChoreMessage", () => {
test("calling chore message with no params should throw an error", () => {
expect(() => getChoreMessage()).toThrow();
test("calling chore message with no params should let you know the chore wasn't found", () => {
expect(getChoreMessage()).toBe("Chore not found");
});

test("calling chore message with a chore object should return a string", () => {
Expand Down
20 changes: 10 additions & 10 deletions src/modules/commands/assign.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {
removeRandomFromList,
} = require("../../helpers/removeRandomFromList.js");
const { getChoreMessage } = require("../../helpers/getChoreMessage.js");
const { addNewUsers } = require("../../services/addNewUsers.js");
const { updateUsers } = require("../../services/updateUsers.js");
const { Client, GatewayIntentBits } = require("discord.js");

const globalHandler = require("../handler.js").globalHandler;
Expand All @@ -27,7 +27,7 @@ const _action = async (body) => {
await client.login(process.env.BOT_TOKEN);

const userId = body.member.user.id;
const allUsers = await addNewUsers(client);
const allUsers = await updateUsers(client); // TODO: maybe we should actually only update users in weekly and maybe monthly?
const user = allUsers.find((u) => u.id === userId);
if (user.hasOwnProperty("currentChore")) {
// IMPORTANT: destroy the discord.js client, otherwise the application hangs
Expand All @@ -38,7 +38,7 @@ const _action = async (body) => {
}

let newChore;
const choresToPickFrom = await services.getTodoChores();
let choresToPickFrom = await services.getTodoChores();
if (choresToPickFrom.length === 0) {
// TODO: big deal here! celebrate all chores being done!
await services.unassignCompletedChores();
Expand All @@ -65,24 +65,24 @@ const _action = async (body) => {

const reviewer = _.cloneDeep(removeRandomFromList(potentialReviewers));
if (newChore) {
await db.put(TABLES.CHORES, {
newChore = {
...newChore,
user: _.cloneDeep(user),
reviewer,
user: user.id,
reviewer: reviewer.id,
status: CHORE_STATES.ASSIGNED,
});
};
await db.put(TABLES.CHORES, newChore);
await db.put(TABLES.USERS, {
...user,
inactive: false,
currentChore: newChore.id,
});
}

let response;
if (newChore) {
response = {
content: `<@${userId}> Your chore is\n${getChoreMessage(
newChore
)}\nYour reviewer is <@${reviewer.id}>`,
content: `<@${userId}> Your new chore is\n${getChoreMessage(newChore)}`,
};
} else {
response = {
Expand Down
Loading

0 comments on commit e9d93fe

Please sign in to comment.