Skip to content

Commit

Permalink
Changeset (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
escottalexander authored Dec 11, 2024
2 parents 35b7b54 + 9f7aa03 commit e17d7aa
Show file tree
Hide file tree
Showing 18 changed files with 1,117 additions and 490 deletions.
5 changes: 5 additions & 0 deletions .changeset/gorgeous-keys-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eth-tech-tree": patch
---

Major UI overhaul. Everything should still work similarly to last release.
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
> ⚠️ Ethereum Development Tech Tree is currently under heavy construction.
# ETH Development Tech Tree
Test your skills and find some new ones by completing challenges.

There are three different types of nodes on the tree:
- [x] Challenges: A repository that poses a problem that you must solve with Solidity. You deploy your contract and submit your contract address so we can test it to ensure your solution works, allowing you to progress.
- [ ] Quizzes: Links to source material that will help you to master a topic that will be encountered in later challenges.
- [ ] Capstone Projects: These are large scale projects that stretch your knowledge about the ecosystem. A description of the project is provided but it is up to you to fulfill the description.
Test your skills and find some new ones by completing medium to hard Solidity challenges.

## Quick Start
Run the following command to use the NPM package
```bash
npx eth-tech-tree
```
The CLI visualizes several categories which contain challenges. Navigate with your arrow keys and hit enter to view options for a challenge. Follow the instructions in your CLI to complete challenges fill out your Ethereum dev tech skills.
The CLI visualizes several categories which contain challenges. Navigate with your arrow keys and hit enter to view the challenge description and see options. Follow the instructions in your CLI to complete challenges and fill out your Ethereum development tech tree.

You can also run individual commands without the tree visualization.

Set up a challenge:
```bash
npx eth-tech-tree setup CHALLENGE_NAME INSTALL_LOCATION
```

Submit a challenge:
```bash
npx eth-tech-tree submit CHALLENGE_NAME CONTRACT_ADDRESS
```

## Development
Clone and `cd` into the repo then run this CLI application with the following commands
- `yarn install`
- `yarn build`
- `yarn cli`

## TODO
- [ ] Show users how many challenges they have completed in a category
- [ ] Show users where they rank on a leaderboard
- [ ] Onchain NFT mint or attestations showing a user has completed certain challenges
- [ ] Enable Gas Efficiency CTF element
Also consider contributing new challenges here: https://github.com/BuidlGuidl/eth-tech-tree-challenges
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"license": "MIT",
"devDependencies": {
"@rollup/plugin-typescript": "11.1.0",
"@types/inquirer": "9.0.3",
"@types/ncp": "2.0.5",
"@types/node": "18.16.0",
"rollup": "3.21.0",
Expand All @@ -42,6 +41,7 @@
},
"dependencies": {
"@changesets/cli": "^2.26.2",
"@inquirer/prompts": "^7.1.0",
"@types/terminal-kit": "^2.5.6",
"ansi-escapes": "^7.0.0",
"arg": "5.0.2",
Expand All @@ -50,12 +50,11 @@
"dotenv": "^16.4.5",
"execa": "7.1.1",
"handlebars": "^4.7.7",
"inquirer": "9.2.0",
"inquirer-tree-prompt": "^1.1.2",
"listr2": "^8.2.5",
"merge-packages": "^0.1.6",
"ncp": "^2.0.0",
"pkg-install": "1.0.0",
"semver": "^7.6.3",
"terminal-kit": "^3.1.1"
},
"packageManager": "[email protected]"
Expand Down
1 change: 1 addition & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export default {
sourcemap: true,
},
plugins: [autoExternal(), typescript({ exclude: ["challenges/**"] })],
external: ["@inquirer/core"],
};
41 changes: 38 additions & 3 deletions src/actions/setup-challenge.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { execa } from "execa";
import semver, { Range } from 'semver';
import ncp from "ncp";
import path from "path";
import fs from "fs";
import { createFirstGitCommit } from "../tasks/create-first-git-commit";
import { fetchChallenges } from "../modules/api";
import { loadChallenges } from "../utils/stateManager";
import { loadChallenges } from "../utils/state-manager";
import { IChallenge } from "../types";
import { BASE_REPO, BASE_BRANCH, BASE_COMMIT } from "../config";
import { DefaultRenderer, Listr, ListrTaskWrapper, SimpleRenderer } from "listr2";
import chalk from "chalk";

type RequiredDependency = "node" | "git" | "yarn" | "foundryup";

// Sidestep for ncp issue https://github.com/AvianFlu/ncp/issues/127
const copy = (source: string, destination: string, options?: ncp.Options) => new Promise((resolve, reject) => {
ncp(source, destination, options || {}, (err) => {
Expand Down Expand Up @@ -57,6 +60,10 @@ export const setupChallenge = async (name: string, installLocation: string) => {
}

const tasks = new Listr([
{
title: 'Checking for required dependencies',
task: () => checkUserDependencies()
},
{
title: 'Setting up base repository',
task: () => setupBaseRepo(targetDir)
Expand Down Expand Up @@ -84,11 +91,39 @@ export const setupChallenge = async (name: string, installLocation: string) => {
console.log(chalk.green("Challenge setup completed successfully."));
console.log("");
console.log(chalk.cyan(`Now open this repository in your favorite code editor and look at the readme for instructions: ${targetDir}`));
} catch (error) {
console.error(chalk.red("An error occurred during challenge setup:"), error);
} catch (error: any) {
console.error(chalk.red("An error occurred during challenge setup:"), error.message);
}
}

const checkDependencyInstalled = async (name: RequiredDependency) => {
try {
await execa(name, ["--help"]);
} catch(_) {
throw new Error(`${name} is required. Please install to continue.`);
}
}

const checkDependencyVersion = async (name: RequiredDependency, requiredVersion: string | Range) => {
try {
const userVersion = (await execa(name, ["--version"])).stdout;
if (!semver.satisfies(userVersion, requiredVersion)) {
throw new Error(`${name} version requirement of ${requiredVersion} not met. Please update to continue.`);
}
} catch(_) {
throw new Error(`${name} ${requiredVersion} is required. Please install to continue.`);
}
}

export const checkUserDependencies = async () => {
await Promise.all([
checkDependencyVersion("node", ">=18.17.0"),
checkDependencyInstalled("git"),
checkDependencyInstalled("yarn"),
checkDependencyInstalled("foundryup"),
])
}

const setupBaseRepo = async (targetDir: string): Promise<void> => {
await execa("git", ["clone", "--branch", BASE_BRANCH, "--single-branch", BASE_REPO, targetDir]);
await execa("git", ["checkout", BASE_COMMIT], { cwd: targetDir });
Expand Down
21 changes: 8 additions & 13 deletions src/actions/submit-challenge.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import inquirer from "inquirer";
import { loadUserState } from "../utils/stateManager";
import { loadUserState } from "../utils/state-manager";
import { submitChallengeToServer } from "../modules/api";
import chalk from "chalk";
import { input } from "@inquirer/prompts";

export async function submitChallenge(name: string, contractAddress?: string) {
const { address: userAddress } = loadUserState();
if (!contractAddress) {
// Prompt the user for the contract address
const questions = [
{
type: "input",
name: "address",
message: "Completed challenge contract address on Sepolia:",
validate: (value: string) => /^0x[a-fA-F0-9]{40}$/.test(value),
},
];
const answers = await inquirer.prompt(questions);
const { address } = answers;
contractAddress = address;
const question = {
message: "Completed challenge contract address on Sepolia:",
validate: (value: string) => /^0x[a-fA-F0-9]{40}$/.test(value),
};
const answer = await input(question);
contractAddress = answer;
}

console.log("Submitting challenge...");
Expand Down
8 changes: 5 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { promptForMissingUserState } from "./tasks/prompt-for-missing-user-state";
import { renderIntroMessage } from "./tasks/render-intro-message";
import type { Args, IUser } from "./types";
import { startVisualization } from "./utils/tree";
import { loadUserState, saveChallenges } from "./utils/stateManager";
import { loadUserState, saveChallenges } from "./utils/state-manager";
import { fetchChallenges } from "./modules/api";
import { parseCommandArgumentsAndOptions, promptForMissingCommandArgs } from "./tasks/parse-command-arguments-and-options";
import { handleCommand } from "./tasks/handle-command";
import { TechTree } from ".";



Expand All @@ -19,7 +19,9 @@ export async function cli(args: Args) {
await renderIntroMessage();
await init(userState);
// Navigate tree
await startVisualization();
const techTree = new TechTree();

await techTree.start();
}
}

Expand Down
Loading

0 comments on commit e17d7aa

Please sign in to comment.