From 35b8294fb272881cf146365955479b0875c7f6f1 Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Mon, 26 Feb 2024 11:44:23 -0500 Subject: [PATCH 01/12] fix: alert deprecation --- src/submitQuest.js | 9 +++++++++ src/utils/messages.js | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/submitQuest.js b/src/submitQuest.js index 411e254..d4f879a 100644 --- a/src/submitQuest.js +++ b/src/submitQuest.js @@ -8,6 +8,7 @@ import { SUBMISSION_ERROR_BANNER, SUBMISSION_FAILED_BANNER, UNCOMMITTED_FILES_MESSAGE, + BREAKING_CHANGE_MESSAGE } from "./utils/messages.js"; import { getToken } from "./utils/token.js"; import { @@ -45,6 +46,14 @@ export async function submitQuest(isSetUpstream, isListening, environment) { process.exit(0); } + const remoteMajorVersion = quest.info.version.match(/^[0-9]+(?=\.)/g); + const localMajorVersion = quest.localVersion().match(/^[0-9]+(?=\.)/g); + + if (remoteMajorVersion > localMajorVersion) { + console.log(BREAKING_CHANGE_MESSAGE); + process.exit(1); + } + const statusSummary = await git.status(); if (statusSummary.files.length) { console.log(UNCOMMITTED_FILES_MESSAGE); diff --git a/src/utils/messages.js b/src/utils/messages.js index 6b7d36d..939351e 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -73,6 +73,12 @@ export const UPDATE_REMINDER_MESSAGE = chalk.yellow( "Run `quest update` to download update.\n" ); +export const BREAKING_CHANGE_MESSAGE = chalk.yellow( + "Deprecated version of quest detected in local repo. New version introduces breaking changes.\n" + + "Run `quest update` to download breaking update.\n") + + chalk.bgBlackBright("Any of your existing work will be overwritten\n"); + + export function NoUpstreamBranchMessage(branchName) { return ( chalk.red(`Current branch : ${branchName} doesn't exist in remote\n`) + From 97192532bc3a63c58064968d11d952029e4bbbdc Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Fri, 1 Mar 2024 15:51:56 -0500 Subject: [PATCH 02/12] download quest dir --- src/findQuest.js | 2 +- src/quest/baseQuest.js | 15 +++++++++++++++ src/quest/cairoQuest.js | 5 ++--- src/quest/huffQuest.js | 5 ++--- src/quest/index.js | 10 +++++----- src/quest/noirQuest.js | 5 ++--- src/quest/solQuest.js | 6 +++--- src/utils/downloader.js | 2 +- src/utils/messages.js | 2 +- 9 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/findQuest.js b/src/findQuest.js index 33e49f7..aaffccc 100644 --- a/src/findQuest.js +++ b/src/findQuest.js @@ -30,7 +30,7 @@ export async function findQuest(questName) { process.exit(1); } - const quest = getQuest(questName); + const quest = await getQuest(questName); if (quest == null) { // Quest not found console.log(QUEST_NOT_FOUND_MESSAGE); diff --git a/src/quest/baseQuest.js b/src/quest/baseQuest.js index e6c52cd..267badf 100644 --- a/src/quest/baseQuest.js +++ b/src/quest/baseQuest.js @@ -2,6 +2,8 @@ import chalk from 'chalk'; import path from 'path'; import { mainPath } from '../utils/navigation.js'; +import { QuestDownloader } from '../utils/downloader.js'; + export class BaseQuest { constructor(language, campaignName, questInfo, runTests) { @@ -47,4 +49,17 @@ export class BaseQuest { } + static async getDirectory(language) { + var downPath = ""; + if (language === "solidity") { + downPath = `campaigns/directory.json`; + } else { + downPath = `campaigns/${language}-directory.json`; + } + const qdown = new QuestDownloader(); + await qdown.downloadFile("Nodeguardians", "ng-questplay", downPath); + + return path.join(process.cwd(), downPath); + } + } diff --git a/src/quest/cairoQuest.js b/src/quest/cairoQuest.js index 756b4ed..4695bbd 100644 --- a/src/quest/cairoQuest.js +++ b/src/quest/cairoQuest.js @@ -4,13 +4,12 @@ import toml from 'toml'; import { BaseQuest } from './baseQuest.js'; import { checkScarbVersion } from '../utils/versions.js'; -import { mainPath } from '../utils/navigation.js'; import { spawnSync } from 'child_process'; export class CairoQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/cairo-directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("cairo"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/quest/huffQuest.js b/src/quest/huffQuest.js index 97650b8..7c5a6be 100644 --- a/src/quest/huffQuest.js +++ b/src/quest/huffQuest.js @@ -2,14 +2,13 @@ import fs from 'fs'; import path from 'path'; import { BaseQuest } from './baseQuest.js'; -import { mainPath } from '../utils/navigation.js'; import { checkForgeVersion, checkHuffCVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; export class HuffQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/huff-directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("huff"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/quest/index.js b/src/quest/index.js index bc7b1fb..3f5763c 100644 --- a/src/quest/index.js +++ b/src/quest/index.js @@ -6,21 +6,21 @@ import { NoirQuest } from "./noirQuest.js"; import { campaignPath } from "../utils/navigation.js"; import { HuffQuest } from "./huffQuest.js"; -export function getQuest(questName) { +export async function getQuest(questName) { // 1. Find in Solidity directory - const solidityQuest = SolidityQuest.find(questName); + const solidityQuest = await SolidityQuest.find(questName); if (solidityQuest != null) return solidityQuest; // 2. Find in Cairo directory - const cairoQuest = CairoQuest.find(questName); + const cairoQuest = await CairoQuest.find(questName); if (cairoQuest != null) return cairoQuest; // 3. Find in Noir directory - const noirQuest = NoirQuest.find(questName); + const noirQuest = await NoirQuest.find(questName); if (noirQuest != null) return noirQuest; // 3. Find in Huff directory - const huffQuest = HuffQuest.find(questName); + const huffQuest = await HuffQuest.find(questName); if (huffQuest != null) return huffQuest; return null; diff --git a/src/quest/noirQuest.js b/src/quest/noirQuest.js index 3b63927..3c61be2 100644 --- a/src/quest/noirQuest.js +++ b/src/quest/noirQuest.js @@ -2,7 +2,6 @@ import fs from 'fs'; import path from 'path'; import { BaseQuest } from './baseQuest.js'; -import { mainPath } from '../utils/navigation.js'; import { checkNargoVersion } from '../utils/versions.js'; import { execSync } from 'child_process'; @@ -12,8 +11,8 @@ const MOCHA_TIMEOUT = 10000 export class NoirQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/noir-directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("noir"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/quest/solQuest.js b/src/quest/solQuest.js index 37dd5c5..237d1a1 100644 --- a/src/quest/solQuest.js +++ b/src/quest/solQuest.js @@ -3,7 +3,7 @@ import fs from 'fs'; import path from 'path'; import { BaseQuest } from './baseQuest.js'; -import { mainPath, readSettings } from '../utils/navigation.js'; +import { readSettings } from '../utils/navigation.js'; import { checkForgeVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; @@ -11,8 +11,8 @@ let hre; export class SolidityQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("solidity"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/utils/downloader.js b/src/utils/downloader.js index d4cd65a..dcaa9ef 100644 --- a/src/utils/downloader.js +++ b/src/utils/downloader.js @@ -88,7 +88,7 @@ export class QuestDownloader { async downloadFile(owner, repo, filePath, options = {}) { const rootPath = options.rootPath == undefined - ? cwd() + ? process.cwd() : options.rootPath; const file = await this._octokit.repos.getContent({ diff --git a/src/utils/messages.js b/src/utils/messages.js index 939351e..74ca58c 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -32,7 +32,7 @@ export const WRONG_DIRECTORY_MESSAGE = chalk.red( export const QUEST_NOT_FOUND_MESSAGE = chalk.yellow( "\nQuest not found. Did you enter the correct name?\n" + - "Check the name again or run `quest update` to fetch the latest directory of released quests.\n" + "Check the name again to be sure.\n" ); // TODO: Add link to FAQ page From 445bda4bd7e3a1fd2739a2363feb1a8d3037e65f Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Mon, 4 Mar 2024 15:02:30 -0500 Subject: [PATCH 03/12] change breaking change message --- src/utils/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/messages.js b/src/utils/messages.js index 74ca58c..b6af6ce 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -75,7 +75,7 @@ export const UPDATE_REMINDER_MESSAGE = chalk.yellow( export const BREAKING_CHANGE_MESSAGE = chalk.yellow( "Deprecated version of quest detected in local repo. New version introduces breaking changes.\n" + - "Run `quest update` to download breaking update.\n") + + "Run `quest find {QUEST-NAME}` to download breaking update.\n") + chalk.bgBlackBright("Any of your existing work will be overwritten\n"); From fa9a0b6666b66d517d2390af4863ca422e87d49e Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Mon, 4 Mar 2024 19:09:24 -0500 Subject: [PATCH 04/12] updated downloader to get files temporarily --- campaigns/cairo-directory.json | 100 ----------- campaigns/directory.json | 299 --------------------------------- campaigns/huff-directory.json | 31 ---- campaigns/noir-directory.json | 25 --- src/quest/baseQuest.js | 16 -- src/quest/cairoQuest.js | 19 ++- src/quest/huffQuest.js | 19 ++- src/quest/noirQuest.js | 17 +- src/quest/solQuest.js | 23 ++- src/utils/downloader.js | 7 +- 10 files changed, 55 insertions(+), 501 deletions(-) delete mode 100644 campaigns/cairo-directory.json delete mode 100644 campaigns/directory.json delete mode 100644 campaigns/huff-directory.json delete mode 100644 campaigns/noir-directory.json diff --git a/campaigns/cairo-directory.json b/campaigns/cairo-directory.json deleted file mode 100644 index 924839c..0000000 --- a/campaigns/cairo-directory.json +++ /dev/null @@ -1,100 +0,0 @@ -[ - { - "name": "cairo-thinking", - "quests":[ - { - "name": "sparring-sorcerers", - "version": "1.3.0", - "type": "build", - "parts": 3 - }, - { - "name": "puzzling-pyramids", - "version": "1.3.0", - "type": "build", - "parts": 2 - }, - { - "name": "racing-riverboats", - "version": "1.3.0", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "standalone-quests", - "quests": [ - { - "name": "brainfuck", - "version": "1.3.0", - "type": "build", - "parts": 2 - }, - { - "name": "vanity-address", - "version": "1.2.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "unsafe-math", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "calldata-spoofing", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "starknet-storage-layout", - "version": "1.1.0", - "type": "ctf", - "parts": 4 - } - ] - }, - { - "name": "bad-accounts", - "quests": [ - { - "name": "stealing-souls", - "version": "1.2.0", - "type": "ctf", - "parts": 2 - }, - { - "name": "bendy-signatures", - "version": "1.2.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "orderless-hashing", - "version": "1.2.0", - "type": "ctf", - "parts": 1 - } - ] - }, - { - "name": "starting-cairo", - "quests": [ - { - "name": "build-tutorial-cairo", - "version": "1.3.0", - "type": "build", - "parts": 4 - }, - { - "name": "ctf-tutorial-cairo", - "version": "1.3.0", - "type": "ctf", - "parts": 1 - } - ] - } -] \ No newline at end of file diff --git a/campaigns/directory.json b/campaigns/directory.json deleted file mode 100644 index 7bd3742..0000000 --- a/campaigns/directory.json +++ /dev/null @@ -1,299 +0,0 @@ -[ - { - "name": "standalone-quests", - "quests":[ - { - "name": "low-level-calls", - "version": "1.1.1", - "type": "ctf", - "parts": 2 - }, - { - "name": "merkle-proofs", - "version": "2.0.1", - "type": "build", - "parts": 2 - }, - { - "name": "unordered-key-set", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "unexpected-ether", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "bypassing-extcodesize", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "clone-factories", - "version": "3.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "using-signatures", - "version": "2.0.1", - "type": "build", - "parts": 2 - }, - { - "name": "multicall", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "delegate-call-detection", - "version": "2.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "price-oracle-attack", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "hardhat-forking", - "version": "2.0.0", - "type": "build", - "parts": 3 - }, - { - "name": "stealth-addresses", - "version": "2.0.0", - "type": "build", - "parts": 3 - }, - { - "name": "elliptic-curve", - "version": "1.0.2", - "type": "build", - "parts": 3 - }, - { - "name": "emergency-governance", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "flashloan-counterexploit", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "self-hosted-evm", - "version": "1.0.0", - "type": "build", - "parts": 7 - }, - { - "name": "interactive-fraud-proofs", - "version": "1.0.0", - "type": "ctf", - "parts": 2 - }, - { - "name": "finding-ouroboros", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "quadratic-public-goods", - "version": "1.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "static-call-detection", - "version": "1.0.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "state-channels", - "version": "1.0.0", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "understanding-storage", - "quests": [ - { - "name": "storage-layout", - "version": "1.1.1", - "type": "ctf", - "parts": 3 - }, - { - "name": "storage-efficiency", - "version": "2.0.1", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "randomness", - "quests": [ - { - "name": "breaking-rng", - "version": "1.1.2", - "type": "ctf", - "parts": 2 - }, - { - "name": "chainlink-vrf", - "version": "1.1.2", - "type": "ctf", - "parts": 2 - } - ] - }, - { - "name": "proxy-contracts", - "quests": [ - { - "name": "basic-proxies", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "proxy-security", - "version": "2.0.0", - "type": "ctf", - "parts": 3 - }, - { - "name": "openzeppelin-upgrades", - "version": "1.0.2", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "diamonds", - "quests": [ - { - "name": "stateful-libraries", - "version": "2.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "building-diamonds", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "using-diamonds", - "version": "1.1.1", - "type": "ctf", - "parts": 4 - } - ] - }, - { - "name": "learning-assembly", - "quests": [ - { - "name": "yul-basics", - "version": "2.0.0", - "type": "build", - "parts": 7 - }, - { - "name": "bits-and-bytes", - "version": "2.0.0", - "type": "build", - "parts": 4 - }, - { - "name": "calldata-manipulation", - "version": "2.0.0", - "type": "build", - "parts": 3 - }, - { - "name": "memory-manipulation", - "version": "2.0.1", - "type": "build", - "parts": 4 - } - ] - }, - { - "name": "gas-optimization", - "quests": [ - { - "name": "solidity-optimization", - "version": "2.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "assembly-optimization", - "version": "2.0.0", - "type": "build", - "parts": 1 - } - ] - }, - { - "name": "tutorial", - "quests": [ - { - "name": "build-tutorial", - "version": "2.0.0", - "type": "build", - "parts": 4 - }, - { - "name": "ctf-tutorial", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - } - ] - }, - { - "name": "get-rekt", - "quests": [ - { - "name": "cream-rekt", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "poly-rekt", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "wintermute-rekt", - "version": "2.0.0", - "type": "ctf", - "parts": 1 - } - ] - } -] \ No newline at end of file diff --git a/campaigns/huff-directory.json b/campaigns/huff-directory.json deleted file mode 100644 index 5da672b..0000000 --- a/campaigns/huff-directory.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "name": "unveiling-huff", - "quests": [ - { - "name": "huff-fundamentals", - "version": "0.1.0", - "type": "build", - "parts": 2 - }, - { - "name": "decoding-arrays", - "version": "0.1.0", - "type": "build", - "parts": 2 - }, - { - "name": "optimized-dispatchers", - "version": "0.1.0", - "type": "build", - "parts": 2 - }, - { - "name": "the-final-duel", - "version": "0.1.0", - "type": "build", - "parts": 2 - } - ] - } -] \ No newline at end of file diff --git a/campaigns/noir-directory.json b/campaigns/noir-directory.json deleted file mode 100644 index 9ee64e6..0000000 --- a/campaigns/noir-directory.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "name": "discovering-noir", - "quests": [ - { - "name": "hello-noir", - "version": "1.1.0", - "type": "build", - "parts": 3 - }, - { - "name": "zk-dungeon", - "version": "1.1.0", - "type": "build", - "parts": 3 - }, - { - "name": "daggers-and-decoys", - "version": "1.1.0", - "type": "build", - "parts": 3 - } - ] - } -] \ No newline at end of file diff --git a/src/quest/baseQuest.js b/src/quest/baseQuest.js index 267badf..5535f4b 100644 --- a/src/quest/baseQuest.js +++ b/src/quest/baseQuest.js @@ -2,8 +2,6 @@ import chalk from 'chalk'; import path from 'path'; import { mainPath } from '../utils/navigation.js'; -import { QuestDownloader } from '../utils/downloader.js'; - export class BaseQuest { constructor(language, campaignName, questInfo, runTests) { @@ -48,18 +46,4 @@ export class BaseQuest { } } - - static async getDirectory(language) { - var downPath = ""; - if (language === "solidity") { - downPath = `campaigns/directory.json`; - } else { - downPath = `campaigns/${language}-directory.json`; - } - const qdown = new QuestDownloader(); - await qdown.downloadFile("Nodeguardians", "ng-questplay", downPath); - - return path.join(process.cwd(), downPath); - } - } diff --git a/src/quest/cairoQuest.js b/src/quest/cairoQuest.js index 4695bbd..ad7541e 100644 --- a/src/quest/cairoQuest.js +++ b/src/quest/cairoQuest.js @@ -5,18 +5,23 @@ import toml from 'toml'; import { BaseQuest } from './baseQuest.js'; import { checkScarbVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; export class CairoQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("cairo"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-cairo-quests-public", + "campaigns/directory.json", + {tempFile: true})); for (const campaign of campaigns) { for (const quest of campaign.quests) { - if (quest.name != questName) continue; - - return new CairoQuest(campaign.name, quest); + if (quest.name != questName) continue; + + return new CairoQuest(campaign.name, quest); } } @@ -50,7 +55,7 @@ async function runCairoTests(partIndex) { "test", "-f", `test_${partIndex}` ]; - spawnSync("scarb", cairoTestParams, {stdio: "inherit"}); + spawnSync("scarb", cairoTestParams, { stdio: "inherit" }); console.log(); - + } \ No newline at end of file diff --git a/src/quest/huffQuest.js b/src/quest/huffQuest.js index 7c5a6be..28fc92c 100644 --- a/src/quest/huffQuest.js +++ b/src/quest/huffQuest.js @@ -4,17 +4,22 @@ import path from 'path'; import { BaseQuest } from './baseQuest.js'; import { checkForgeVersion, checkHuffCVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; export class HuffQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("huff"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-huff-quests-public", + `campaigns/huff-directory.json`, + {tempFile: true})); for (const campaign of campaigns) { for (const quest of campaign.quests) { if (quest.name != questName) continue; - + return new HuffQuest(campaign.name, quest); } } @@ -42,9 +47,9 @@ export class HuffQuest extends BaseQuest { } } - + async function runFoundryTests(partIndex) { - + console.log(); if (!checkForgeVersion()) process.exit(1); if (!checkHuffCVersion()) process.exit(1); @@ -55,7 +60,7 @@ async function runFoundryTests(partIndex) { `*\.${partIndex}\.t\.sol` ]; - spawnSync("forge", forgeParams, {stdio: "inherit"}); + spawnSync("forge", forgeParams, { stdio: "inherit" }); console.log(); - + } \ No newline at end of file diff --git a/src/quest/noirQuest.js b/src/quest/noirQuest.js index 3c61be2..251d8ed 100644 --- a/src/quest/noirQuest.js +++ b/src/quest/noirQuest.js @@ -4,6 +4,7 @@ import path from 'path'; import { BaseQuest } from './baseQuest.js'; import { checkNargoVersion } from '../utils/versions.js'; import { execSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; let hre; @@ -12,14 +13,18 @@ const MOCHA_TIMEOUT = 10000 export class NoirQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("noir"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-noir-quests-public", + `campaigns/noir-directory.json`, + {tempFile: true})); for (const campaign of campaigns) { for (const quest of campaign.quests) { - if (quest.name != questName) continue; + if (quest.name != questName) continue; - return new NoirQuest(campaign.name, quest); + return new NoirQuest(campaign.name, quest); } } @@ -66,10 +71,10 @@ async function runJSTests(partIndex) { } else { - const mochaCommand + const mochaCommand = `npx mocha test --timeout ${MOCHA_TIMEOUT} --recursive --grep "(Part ${partIndex})"`; try { - execSync(mochaCommand, {stdio: 'inherit'}); + execSync(mochaCommand, { stdio: 'inherit' }); } catch (error) { } } diff --git a/src/quest/solQuest.js b/src/quest/solQuest.js index 237d1a1..2c47030 100644 --- a/src/quest/solQuest.js +++ b/src/quest/solQuest.js @@ -6,20 +6,25 @@ import { BaseQuest } from './baseQuest.js'; import { readSettings } from '../utils/navigation.js'; import { checkForgeVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; let hre; export class SolidityQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("solidity"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-quests-public", + "campaigns/directory.json", + { tempFile: true })); for (const campaign of campaigns) { for (const quest of campaign.quests) { - if (quest.name != questName) continue; - - return new SolidityQuest(campaign.name, quest); + if (quest.name != questName) continue; + + return new SolidityQuest(campaign.name, quest); } } @@ -61,9 +66,9 @@ async function runHardhatTests(partIndex) { await hre.default.run("test", { grep: `Part ${partIndex}` }); } - + async function runFoundryTests(partIndex) { - + console.log(); if (!checkForgeVersion()) process.exit(1); @@ -73,7 +78,7 @@ async function runFoundryTests(partIndex) { `*\.${partIndex}\.t\.sol` ]; - spawnSync("forge", forgeParams, {stdio: "inherit"}); + spawnSync("forge", forgeParams, { stdio: "inherit" }); console.log(); - + } \ No newline at end of file diff --git a/src/utils/downloader.js b/src/utils/downloader.js index dcaa9ef..ea9483a 100644 --- a/src/utils/downloader.js +++ b/src/utils/downloader.js @@ -99,7 +99,12 @@ export class QuestDownloader { }); const decodedContent = Buffer.from(file.data.content, file.data.encoding); - fs.writeFileSync(path.join(rootPath, filePath), decodedContent); + + if(!options.tempFile) { + fs.writeFileSync(path.join(rootPath, filePath), decodedContent); + } + + return decodedContent; } async installSubpackage() { From d7d7e79a42d1901b1389a27e81d19ac1a6b630db Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Mon, 26 Feb 2024 11:44:23 -0500 Subject: [PATCH 05/12] fix: alert deprecation --- src/submitQuest.js | 9 +++++++++ src/utils/messages.js | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/submitQuest.js b/src/submitQuest.js index 411e254..d4f879a 100644 --- a/src/submitQuest.js +++ b/src/submitQuest.js @@ -8,6 +8,7 @@ import { SUBMISSION_ERROR_BANNER, SUBMISSION_FAILED_BANNER, UNCOMMITTED_FILES_MESSAGE, + BREAKING_CHANGE_MESSAGE } from "./utils/messages.js"; import { getToken } from "./utils/token.js"; import { @@ -45,6 +46,14 @@ export async function submitQuest(isSetUpstream, isListening, environment) { process.exit(0); } + const remoteMajorVersion = quest.info.version.match(/^[0-9]+(?=\.)/g); + const localMajorVersion = quest.localVersion().match(/^[0-9]+(?=\.)/g); + + if (remoteMajorVersion > localMajorVersion) { + console.log(BREAKING_CHANGE_MESSAGE); + process.exit(1); + } + const statusSummary = await git.status(); if (statusSummary.files.length) { console.log(UNCOMMITTED_FILES_MESSAGE); diff --git a/src/utils/messages.js b/src/utils/messages.js index c7ac173..1ad2155 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -68,6 +68,12 @@ export const UPDATE_REMINDER_MESSAGE = chalk.yellow( "Run `quest update` to download update.\n" ); +export const BREAKING_CHANGE_MESSAGE = chalk.yellow( + "Deprecated version of quest detected in local repo. New version introduces breaking changes.\n" + + "Run `quest update` to download breaking update.\n") + + chalk.bgBlackBright("Any of your existing work will be overwritten\n"); + + export function NoUpstreamBranchMessage(branchName) { return ( chalk.red(`Current branch : ${branchName} doesn't exist in remote\n`) + From b01de6330325e4725dc81ddaae60888e71c0af3e Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Fri, 1 Mar 2024 15:51:56 -0500 Subject: [PATCH 06/12] download quest dir --- src/findQuest.js | 2 +- src/quest/baseQuest.js | 15 +++++++++++++++ src/quest/cairoQuest.js | 5 ++--- src/quest/huffQuest.js | 5 ++--- src/quest/index.js | 10 +++++----- src/quest/noirQuest.js | 5 ++--- src/quest/solQuest.js | 6 +++--- src/utils/downloader.js | 2 +- src/utils/messages.js | 2 +- 9 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/findQuest.js b/src/findQuest.js index 33e49f7..aaffccc 100644 --- a/src/findQuest.js +++ b/src/findQuest.js @@ -30,7 +30,7 @@ export async function findQuest(questName) { process.exit(1); } - const quest = getQuest(questName); + const quest = await getQuest(questName); if (quest == null) { // Quest not found console.log(QUEST_NOT_FOUND_MESSAGE); diff --git a/src/quest/baseQuest.js b/src/quest/baseQuest.js index e6c52cd..267badf 100644 --- a/src/quest/baseQuest.js +++ b/src/quest/baseQuest.js @@ -2,6 +2,8 @@ import chalk from 'chalk'; import path from 'path'; import { mainPath } from '../utils/navigation.js'; +import { QuestDownloader } from '../utils/downloader.js'; + export class BaseQuest { constructor(language, campaignName, questInfo, runTests) { @@ -47,4 +49,17 @@ export class BaseQuest { } + static async getDirectory(language) { + var downPath = ""; + if (language === "solidity") { + downPath = `campaigns/directory.json`; + } else { + downPath = `campaigns/${language}-directory.json`; + } + const qdown = new QuestDownloader(); + await qdown.downloadFile("Nodeguardians", "ng-questplay", downPath); + + return path.join(process.cwd(), downPath); + } + } diff --git a/src/quest/cairoQuest.js b/src/quest/cairoQuest.js index 756b4ed..4695bbd 100644 --- a/src/quest/cairoQuest.js +++ b/src/quest/cairoQuest.js @@ -4,13 +4,12 @@ import toml from 'toml'; import { BaseQuest } from './baseQuest.js'; import { checkScarbVersion } from '../utils/versions.js'; -import { mainPath } from '../utils/navigation.js'; import { spawnSync } from 'child_process'; export class CairoQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/cairo-directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("cairo"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/quest/huffQuest.js b/src/quest/huffQuest.js index 97650b8..7c5a6be 100644 --- a/src/quest/huffQuest.js +++ b/src/quest/huffQuest.js @@ -2,14 +2,13 @@ import fs from 'fs'; import path from 'path'; import { BaseQuest } from './baseQuest.js'; -import { mainPath } from '../utils/navigation.js'; import { checkForgeVersion, checkHuffCVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; export class HuffQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/huff-directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("huff"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/quest/index.js b/src/quest/index.js index bc7b1fb..3f5763c 100644 --- a/src/quest/index.js +++ b/src/quest/index.js @@ -6,21 +6,21 @@ import { NoirQuest } from "./noirQuest.js"; import { campaignPath } from "../utils/navigation.js"; import { HuffQuest } from "./huffQuest.js"; -export function getQuest(questName) { +export async function getQuest(questName) { // 1. Find in Solidity directory - const solidityQuest = SolidityQuest.find(questName); + const solidityQuest = await SolidityQuest.find(questName); if (solidityQuest != null) return solidityQuest; // 2. Find in Cairo directory - const cairoQuest = CairoQuest.find(questName); + const cairoQuest = await CairoQuest.find(questName); if (cairoQuest != null) return cairoQuest; // 3. Find in Noir directory - const noirQuest = NoirQuest.find(questName); + const noirQuest = await NoirQuest.find(questName); if (noirQuest != null) return noirQuest; // 3. Find in Huff directory - const huffQuest = HuffQuest.find(questName); + const huffQuest = await HuffQuest.find(questName); if (huffQuest != null) return huffQuest; return null; diff --git a/src/quest/noirQuest.js b/src/quest/noirQuest.js index 3b63927..3c61be2 100644 --- a/src/quest/noirQuest.js +++ b/src/quest/noirQuest.js @@ -2,7 +2,6 @@ import fs from 'fs'; import path from 'path'; import { BaseQuest } from './baseQuest.js'; -import { mainPath } from '../utils/navigation.js'; import { checkNargoVersion } from '../utils/versions.js'; import { execSync } from 'child_process'; @@ -12,8 +11,8 @@ const MOCHA_TIMEOUT = 10000 export class NoirQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/noir-directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("noir"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/quest/solQuest.js b/src/quest/solQuest.js index 37dd5c5..237d1a1 100644 --- a/src/quest/solQuest.js +++ b/src/quest/solQuest.js @@ -3,7 +3,7 @@ import fs from 'fs'; import path from 'path'; import { BaseQuest } from './baseQuest.js'; -import { mainPath, readSettings } from '../utils/navigation.js'; +import { readSettings } from '../utils/navigation.js'; import { checkForgeVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; @@ -11,8 +11,8 @@ let hre; export class SolidityQuest extends BaseQuest { - static find(questName) { - const directoryPath = path.join(mainPath(), "campaigns/directory.json"); + static async find(questName) { + const directoryPath = await BaseQuest.getDirectory("solidity"); const campaigns = JSON.parse(fs.readFileSync(directoryPath)); for (const campaign of campaigns) { diff --git a/src/utils/downloader.js b/src/utils/downloader.js index d4cd65a..dcaa9ef 100644 --- a/src/utils/downloader.js +++ b/src/utils/downloader.js @@ -88,7 +88,7 @@ export class QuestDownloader { async downloadFile(owner, repo, filePath, options = {}) { const rootPath = options.rootPath == undefined - ? cwd() + ? process.cwd() : options.rootPath; const file = await this._octokit.repos.getContent({ diff --git a/src/utils/messages.js b/src/utils/messages.js index 1ad2155..9a7a4dd 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -32,7 +32,7 @@ export const WRONG_DIRECTORY_MESSAGE = chalk.red( export const QUEST_NOT_FOUND_MESSAGE = chalk.yellow( "\nQuest not found. Did you enter the correct name?\n" + - "Check the name again or run `quest update` to fetch the latest directory of released quests.\n" + "Check the name again to be sure.\n" ); export const BAD_TOKEN_MESSAGE = chalk.red( From 02a0f738cf4411f6beaf6fc97bdecaa27f05b7ca Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Mon, 4 Mar 2024 15:02:30 -0500 Subject: [PATCH 07/12] change breaking change message --- src/utils/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/messages.js b/src/utils/messages.js index 9a7a4dd..454ad1f 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -70,7 +70,7 @@ export const UPDATE_REMINDER_MESSAGE = chalk.yellow( export const BREAKING_CHANGE_MESSAGE = chalk.yellow( "Deprecated version of quest detected in local repo. New version introduces breaking changes.\n" + - "Run `quest update` to download breaking update.\n") + + "Run `quest find {QUEST-NAME}` to download breaking update.\n") + chalk.bgBlackBright("Any of your existing work will be overwritten\n"); From 24286edd1ad60f70228dd5ed3c09824de6bc4d69 Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Tue, 5 Mar 2024 17:15:19 -0500 Subject: [PATCH 08/12] rebase complete --- campaigns/cairo-directory.json | 100 ----------- campaigns/directory.json | 317 --------------------------------- campaigns/huff-directory.json | 31 ---- campaigns/noir-directory.json | 25 --- src/quest/baseQuest.js | 16 -- src/quest/cairoQuest.js | 19 +- src/quest/huffQuest.js | 19 +- src/quest/noirQuest.js | 17 +- src/quest/solQuest.js | 23 ++- src/utils/downloader.js | 7 +- 10 files changed, 55 insertions(+), 519 deletions(-) delete mode 100644 campaigns/cairo-directory.json delete mode 100644 campaigns/directory.json delete mode 100644 campaigns/huff-directory.json delete mode 100644 campaigns/noir-directory.json diff --git a/campaigns/cairo-directory.json b/campaigns/cairo-directory.json deleted file mode 100644 index 924839c..0000000 --- a/campaigns/cairo-directory.json +++ /dev/null @@ -1,100 +0,0 @@ -[ - { - "name": "cairo-thinking", - "quests":[ - { - "name": "sparring-sorcerers", - "version": "1.3.0", - "type": "build", - "parts": 3 - }, - { - "name": "puzzling-pyramids", - "version": "1.3.0", - "type": "build", - "parts": 2 - }, - { - "name": "racing-riverboats", - "version": "1.3.0", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "standalone-quests", - "quests": [ - { - "name": "brainfuck", - "version": "1.3.0", - "type": "build", - "parts": 2 - }, - { - "name": "vanity-address", - "version": "1.2.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "unsafe-math", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "calldata-spoofing", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "starknet-storage-layout", - "version": "1.1.0", - "type": "ctf", - "parts": 4 - } - ] - }, - { - "name": "bad-accounts", - "quests": [ - { - "name": "stealing-souls", - "version": "1.2.0", - "type": "ctf", - "parts": 2 - }, - { - "name": "bendy-signatures", - "version": "1.2.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "orderless-hashing", - "version": "1.2.0", - "type": "ctf", - "parts": 1 - } - ] - }, - { - "name": "starting-cairo", - "quests": [ - { - "name": "build-tutorial-cairo", - "version": "1.3.0", - "type": "build", - "parts": 4 - }, - { - "name": "ctf-tutorial-cairo", - "version": "1.3.0", - "type": "ctf", - "parts": 1 - } - ] - } -] \ No newline at end of file diff --git a/campaigns/directory.json b/campaigns/directory.json deleted file mode 100644 index bc2c943..0000000 --- a/campaigns/directory.json +++ /dev/null @@ -1,317 +0,0 @@ -[ - { - "name": "standalone-quests", - "quests":[ - { - "name": "low-level-calls", - "version": "1.1.1", - "type": "ctf", - "parts": 2 - }, - { - "name": "merkle-proofs", - "version": "2.0.1", - "type": "build", - "parts": 2 - }, - { - "name": "unordered-key-set", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "unexpected-ether", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "bypassing-extcodesize", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "clone-factories", - "version": "3.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "using-signatures", - "version": "2.0.1", - "type": "build", - "parts": 2 - }, - { - "name": "multicall", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "delegate-call-detection", - "version": "2.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "price-oracle-attack", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "hardhat-forking", - "version": "2.0.0", - "type": "build", - "parts": 3 - }, - { - "name": "stealth-addresses", - "version": "2.0.0", - "type": "build", - "parts": 3 - }, - { - "name": "elliptic-curve", - "version": "1.0.2", - "type": "build", - "parts": 3 - }, - { - "name": "emergency-governance", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "flashloan-counterexploit", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "self-hosted-evm", - "version": "1.0.0", - "type": "build", - "parts": 7 - }, - { - "name": "interactive-fraud-proofs", - "version": "1.0.0", - "type": "ctf", - "parts": 2 - }, - { - "name": "finding-ouroboros", - "version": "1.1.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "quadratic-public-goods", - "version": "1.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "static-call-detection", - "version": "1.0.0", - "type": "ctf", - "parts": 1 - }, - { - "name": "state-channels", - "version": "1.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "wtf-imps", - "version": "1.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "setting-up", - "version": "1.0.0", - "type": "build", - "parts": 4 - }, - { - "name": "vanity-theft", - "version": "1.0.0", - "type": "ctf", - "parts": 1 - } - ] - }, - { - "name": "understanding-storage", - "quests": [ - { - "name": "storage-layout", - "version": "1.1.1", - "type": "ctf", - "parts": 3 - }, - { - "name": "storage-efficiency", - "version": "2.0.1", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "randomness", - "quests": [ - { - "name": "breaking-rng", - "version": "1.1.2", - "type": "ctf", - "parts": 2 - }, - { - "name": "chainlink-vrf", - "version": "1.1.2", - "type": "ctf", - "parts": 2 - } - ] - }, - { - "name": "proxy-contracts", - "quests": [ - { - "name": "basic-proxies", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "proxy-security", - "version": "2.0.0", - "type": "ctf", - "parts": 3 - }, - { - "name": "openzeppelin-upgrades", - "version": "1.0.2", - "type": "build", - "parts": 2 - } - ] - }, - { - "name": "diamonds", - "quests": [ - { - "name": "stateful-libraries", - "version": "2.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "building-diamonds", - "version": "2.0.0", - "type": "build", - "parts": 2 - }, - { - "name": "using-diamonds", - "version": "1.1.1", - "type": "ctf", - "parts": 4 - } - ] - }, - { - "name": "learning-assembly", - "quests": [ - { - "name": "yul-basics", - "version": "2.0.0", - "type": "build", - "parts": 7 - }, - { - "name": "bits-and-bytes", - "version": "2.0.0", - "type": "build", - "parts": 4 - }, - { - "name": "calldata-manipulation", - "version": "2.0.0", - "type": "build", - "parts": 3 - }, - { - "name": "memory-manipulation", - "version": "2.0.1", - "type": "build", - "parts": 4 - } - ] - }, - { - "name": "gas-optimization", - "quests": [ - { - "name": "solidity-optimization", - "version": "2.0.0", - "type": "build", - "parts": 1 - }, - { - "name": "assembly-optimization", - "version": "2.0.0", - "type": "build", - "parts": 1 - } - ] - }, - { - "name": "get-rekt", - "quests": [ - { - "name": "cream-rekt", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "poly-rekt", - "version": "1.1.1", - "type": "ctf", - "parts": 1 - }, - { - "name": "wintermute-rekt", - "version": "2.0.0", - "type": "ctf", - "parts": 1 - } - ] - }, - { - "name": "token-standards", - "quests": [ - { - "name": "building-erc-20", - "version": "1.0.0", - "type": "build", - "parts": 5 - }, - { - "name": "building-erc-721", - "version": "1.0.0", - "type": "build", - "parts": 4 - } - ] - } -] \ No newline at end of file diff --git a/campaigns/huff-directory.json b/campaigns/huff-directory.json deleted file mode 100644 index 5da672b..0000000 --- a/campaigns/huff-directory.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "name": "unveiling-huff", - "quests": [ - { - "name": "huff-fundamentals", - "version": "0.1.0", - "type": "build", - "parts": 2 - }, - { - "name": "decoding-arrays", - "version": "0.1.0", - "type": "build", - "parts": 2 - }, - { - "name": "optimized-dispatchers", - "version": "0.1.0", - "type": "build", - "parts": 2 - }, - { - "name": "the-final-duel", - "version": "0.1.0", - "type": "build", - "parts": 2 - } - ] - } -] \ No newline at end of file diff --git a/campaigns/noir-directory.json b/campaigns/noir-directory.json deleted file mode 100644 index 9ee64e6..0000000 --- a/campaigns/noir-directory.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "name": "discovering-noir", - "quests": [ - { - "name": "hello-noir", - "version": "1.1.0", - "type": "build", - "parts": 3 - }, - { - "name": "zk-dungeon", - "version": "1.1.0", - "type": "build", - "parts": 3 - }, - { - "name": "daggers-and-decoys", - "version": "1.1.0", - "type": "build", - "parts": 3 - } - ] - } -] \ No newline at end of file diff --git a/src/quest/baseQuest.js b/src/quest/baseQuest.js index 267badf..5535f4b 100644 --- a/src/quest/baseQuest.js +++ b/src/quest/baseQuest.js @@ -2,8 +2,6 @@ import chalk from 'chalk'; import path from 'path'; import { mainPath } from '../utils/navigation.js'; -import { QuestDownloader } from '../utils/downloader.js'; - export class BaseQuest { constructor(language, campaignName, questInfo, runTests) { @@ -48,18 +46,4 @@ export class BaseQuest { } } - - static async getDirectory(language) { - var downPath = ""; - if (language === "solidity") { - downPath = `campaigns/directory.json`; - } else { - downPath = `campaigns/${language}-directory.json`; - } - const qdown = new QuestDownloader(); - await qdown.downloadFile("Nodeguardians", "ng-questplay", downPath); - - return path.join(process.cwd(), downPath); - } - } diff --git a/src/quest/cairoQuest.js b/src/quest/cairoQuest.js index 4695bbd..ad7541e 100644 --- a/src/quest/cairoQuest.js +++ b/src/quest/cairoQuest.js @@ -5,18 +5,23 @@ import toml from 'toml'; import { BaseQuest } from './baseQuest.js'; import { checkScarbVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; export class CairoQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("cairo"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-cairo-quests-public", + "campaigns/directory.json", + {tempFile: true})); for (const campaign of campaigns) { for (const quest of campaign.quests) { - if (quest.name != questName) continue; - - return new CairoQuest(campaign.name, quest); + if (quest.name != questName) continue; + + return new CairoQuest(campaign.name, quest); } } @@ -50,7 +55,7 @@ async function runCairoTests(partIndex) { "test", "-f", `test_${partIndex}` ]; - spawnSync("scarb", cairoTestParams, {stdio: "inherit"}); + spawnSync("scarb", cairoTestParams, { stdio: "inherit" }); console.log(); - + } \ No newline at end of file diff --git a/src/quest/huffQuest.js b/src/quest/huffQuest.js index 7c5a6be..28fc92c 100644 --- a/src/quest/huffQuest.js +++ b/src/quest/huffQuest.js @@ -4,17 +4,22 @@ import path from 'path'; import { BaseQuest } from './baseQuest.js'; import { checkForgeVersion, checkHuffCVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; export class HuffQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("huff"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-huff-quests-public", + `campaigns/huff-directory.json`, + {tempFile: true})); for (const campaign of campaigns) { for (const quest of campaign.quests) { if (quest.name != questName) continue; - + return new HuffQuest(campaign.name, quest); } } @@ -42,9 +47,9 @@ export class HuffQuest extends BaseQuest { } } - + async function runFoundryTests(partIndex) { - + console.log(); if (!checkForgeVersion()) process.exit(1); if (!checkHuffCVersion()) process.exit(1); @@ -55,7 +60,7 @@ async function runFoundryTests(partIndex) { `*\.${partIndex}\.t\.sol` ]; - spawnSync("forge", forgeParams, {stdio: "inherit"}); + spawnSync("forge", forgeParams, { stdio: "inherit" }); console.log(); - + } \ No newline at end of file diff --git a/src/quest/noirQuest.js b/src/quest/noirQuest.js index 3c61be2..251d8ed 100644 --- a/src/quest/noirQuest.js +++ b/src/quest/noirQuest.js @@ -4,6 +4,7 @@ import path from 'path'; import { BaseQuest } from './baseQuest.js'; import { checkNargoVersion } from '../utils/versions.js'; import { execSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; let hre; @@ -12,14 +13,18 @@ const MOCHA_TIMEOUT = 10000 export class NoirQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("noir"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-noir-quests-public", + `campaigns/noir-directory.json`, + {tempFile: true})); for (const campaign of campaigns) { for (const quest of campaign.quests) { - if (quest.name != questName) continue; + if (quest.name != questName) continue; - return new NoirQuest(campaign.name, quest); + return new NoirQuest(campaign.name, quest); } } @@ -66,10 +71,10 @@ async function runJSTests(partIndex) { } else { - const mochaCommand + const mochaCommand = `npx mocha test --timeout ${MOCHA_TIMEOUT} --recursive --grep "(Part ${partIndex})"`; try { - execSync(mochaCommand, {stdio: 'inherit'}); + execSync(mochaCommand, { stdio: 'inherit' }); } catch (error) { } } diff --git a/src/quest/solQuest.js b/src/quest/solQuest.js index 237d1a1..2c47030 100644 --- a/src/quest/solQuest.js +++ b/src/quest/solQuest.js @@ -6,20 +6,25 @@ import { BaseQuest } from './baseQuest.js'; import { readSettings } from '../utils/navigation.js'; import { checkForgeVersion } from '../utils/versions.js'; import { spawnSync } from 'child_process'; +import { QuestDownloader } from '../utils/downloader.js'; let hre; export class SolidityQuest extends BaseQuest { static async find(questName) { - const directoryPath = await BaseQuest.getDirectory("solidity"); - const campaigns = JSON.parse(fs.readFileSync(directoryPath)); + const qdown = new QuestDownloader(); + const campaigns = JSON.parse(await qdown.downloadFile( + "Nodeguardians", + "ng-quests-public", + "campaigns/directory.json", + { tempFile: true })); for (const campaign of campaigns) { for (const quest of campaign.quests) { - if (quest.name != questName) continue; - - return new SolidityQuest(campaign.name, quest); + if (quest.name != questName) continue; + + return new SolidityQuest(campaign.name, quest); } } @@ -61,9 +66,9 @@ async function runHardhatTests(partIndex) { await hre.default.run("test", { grep: `Part ${partIndex}` }); } - + async function runFoundryTests(partIndex) { - + console.log(); if (!checkForgeVersion()) process.exit(1); @@ -73,7 +78,7 @@ async function runFoundryTests(partIndex) { `*\.${partIndex}\.t\.sol` ]; - spawnSync("forge", forgeParams, {stdio: "inherit"}); + spawnSync("forge", forgeParams, { stdio: "inherit" }); console.log(); - + } \ No newline at end of file diff --git a/src/utils/downloader.js b/src/utils/downloader.js index dcaa9ef..ea9483a 100644 --- a/src/utils/downloader.js +++ b/src/utils/downloader.js @@ -99,7 +99,12 @@ export class QuestDownloader { }); const decodedContent = Buffer.from(file.data.content, file.data.encoding); - fs.writeFileSync(path.join(rootPath, filePath), decodedContent); + + if(!options.tempFile) { + fs.writeFileSync(path.join(rootPath, filePath), decodedContent); + } + + return decodedContent; } async installSubpackage() { From de6598a49c1d4222dffd8c818f50e182c8afdfea Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Tue, 5 Mar 2024 14:31:04 -0500 Subject: [PATCH 09/12] custom deprecation message --- src/submitQuest.js | 4 ++-- src/utils/messages.js | 36 +++++++++++++++++++----------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/submitQuest.js b/src/submitQuest.js index d4f879a..c378053 100644 --- a/src/submitQuest.js +++ b/src/submitQuest.js @@ -8,7 +8,7 @@ import { SUBMISSION_ERROR_BANNER, SUBMISSION_FAILED_BANNER, UNCOMMITTED_FILES_MESSAGE, - BREAKING_CHANGE_MESSAGE + BreakingChangeMessage } from "./utils/messages.js"; import { getToken } from "./utils/token.js"; import { @@ -50,7 +50,7 @@ export async function submitQuest(isSetUpstream, isListening, environment) { const localMajorVersion = quest.localVersion().match(/^[0-9]+(?=\.)/g); if (remoteMajorVersion > localMajorVersion) { - console.log(BREAKING_CHANGE_MESSAGE); + console.log(BreakingChangeMessage(quest.info.name)); process.exit(1); } diff --git a/src/utils/messages.js b/src/utils/messages.js index 454ad1f..6c19cc0 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -49,30 +49,32 @@ export const QUEST_ALREADY_EXISTS_MESSAGE = chalk.yellow( export const UNCOMMITTED_FILES_MESSAGE = chalk.yellow( "Uncommitted files detected. Questplay is unable to push to remote.\n" + - "Try committing or stashing all changes with git first.\n" + "Try committing or stashing all changes with git first.\n" ); export const UNCOMMITTED_FILES_BEFORE_UPDATE_MESSAGE = chalk.yellow( "Questplay is unable to update when there are still uncommitted files in the repository.\n" + - "Try committing or stashing all changes with git first.\n" + "Try committing or stashing all changes with git first.\n" ); export const UNCOMMITTED_FILES_BEFORE_DOWNLOAD_MESSAGE = chalk.yellow( "Questplay is unable to download a quest when there are still uncommitted files in the repository.\n" + - "Try committing or stashing all changes with git first.\n" + "Try committing or stashing all changes with git first.\n" ); export const UPDATE_REMINDER_MESSAGE = chalk.yellow( chalk.bold("\nIMPORTANT:"), "New version of Questplay available.\n" + - "Run `quest update` to download update.\n" + "Run `quest update` to download update.\n" ); -export const BREAKING_CHANGE_MESSAGE = chalk.yellow( - "Deprecated version of quest detected in local repo. New version introduces breaking changes.\n" + - "Run `quest find {QUEST-NAME}` to download breaking update.\n") + - chalk.bgBlackBright("Any of your existing work will be overwritten\n"); - +export function BreakingChangeMessage(questName) { + return ( + chalk.yellow("Deprecated version of quest detected in local repo. New version introduces breaking changes.\n" + + `Run "quest find ${questName}" to download breaking update.\n`) + + chalk.bgBlackBright("Any of your existing work will be overwritten\n") + ); +} export function NoUpstreamBranchMessage(branchName) { return ( @@ -155,10 +157,10 @@ export const FOUNDRY_NOT_SUPPORTED_MESSAGE = chalk.yellow( ); export function INSTALL_SCARB_MESSAGE(remoteVersion) { - return chalk.yellow( - `Cairo quests require the installation of Scarb ${remoteVersion}. To install Scarb, check out`, - chalk.underline("https://docs.swmansion.com/scarb/\n") - ); + return chalk.yellow( + `Cairo quests require the installation of Scarb ${remoteVersion}. To install Scarb, check out`, + chalk.underline("https://docs.swmansion.com/scarb/\n") + ); } export function MISMATCH_SCARB_MESSAGE(remoteVersion) { @@ -166,13 +168,13 @@ export function MISMATCH_SCARB_MESSAGE(remoteVersion) { } export function UPDATE_CAIRO_MESSAGE(localVersion, remoteVersion) { - let changes = `${chalk.bgRed(chalk.strikethrough(localVersion))} ${chalk.bgGreenBright(remoteVersion)}`; - return chalk.yellow(`We have migrated to cairo ${remoteVersion}! Migrate your quest by making the following changes to Scarb.toml:\n`) + let changes = `${chalk.bgRed(chalk.strikethrough(localVersion))} ${chalk.bgGreenBright(remoteVersion)}`; + return chalk.yellow(`We have migrated to cairo ${remoteVersion}! Migrate your quest by making the following changes to Scarb.toml:\n`) + chalk.grey(`\n cairo-version = ${changes}`) + chalk.grey(`\n starknet-version = ${changes} [if applicable]\n`) } -export const UPDATE_SCARB_VERSION_FAIL = +export const UPDATE_SCARB_VERSION_FAIL = chalk.yellow( "WARNING: Failed to detect Scarb version.\n", "Consider reporting this on Discord!\n", @@ -210,5 +212,5 @@ export const SUBMISSION_ERROR_BANNER = chalk.yellow( export const SUBMISSION_FAILED_BANNER = chalk.red( "\nERROR: We couldn't validate your submission due to the following error\n", "Please report this error on Discord: " + - chalk.underline("https://discord.gg/EyGQEEzmjx\n") + chalk.underline("https://discord.gg/EyGQEEzmjx\n") ); From 9537e5d8d895bfe7457e0d25b11e8a523ce4c697 Mon Sep 17 00:00:00 2001 From: kashepavadan Date: Wed, 6 Mar 2024 09:42:43 -0500 Subject: [PATCH 10/12] fixed merge issue and added semver --- src/submitQuest.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/submitQuest.js b/src/submitQuest.js index 15a6c51..23998bc 100644 --- a/src/submitQuest.js +++ b/src/submitQuest.js @@ -8,11 +8,7 @@ import { SUBMISSION_ERROR_BANNER, SUBMISSION_FAILED_BANNER, UNCOMMITTED_FILES_MESSAGE, -<<<<<<< HEAD BreakingChangeMessage -======= - BREAKING_CHANGE_MESSAGE ->>>>>>> fa9a0b6666b66d517d2390af4863ca422e87d49e } from "./utils/messages.js"; import { getToken } from "./utils/token.js"; import { @@ -28,6 +24,7 @@ import { stopSpinner, succeedSpinner, } from "./utils/spinner.js"; +import semver from "semver"; const git = simpleGit(); @@ -50,10 +47,9 @@ export async function submitQuest(isSetUpstream, isListening, environment) { process.exit(0); } - const remoteMajorVersion = quest.info.version.match(/^[0-9]+(?=\.)/g); - const localMajorVersion = quest.localVersion().match(/^[0-9]+(?=\.)/g); + const diff = semver.diff(quest.info.version, quest.localVersion()); - if (remoteMajorVersion > localMajorVersion) { + if (diff == "major" || diff == "premajor") { console.log(BreakingChangeMessage(quest.info.name)); process.exit(1); } From abf0c00ee229a5ec83df5dc2b0e6472b8f1eb23d Mon Sep 17 00:00:00 2001 From: Bryan Wee Date: Thu, 7 Mar 2024 12:42:17 +0800 Subject: [PATCH 11/12] fix: enable non-solidity quest installations --- package.json | 2 +- src/findQuest.js | 5 +---- src/submitQuest.js | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9f58623..49718a6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "ng-questplay", - "version": "1.8.3", + "version": "1.9.0", "description": "", "type": "module", "scripts": { diff --git a/src/findQuest.js b/src/findQuest.js index aaffccc..ae0d828 100644 --- a/src/findQuest.js +++ b/src/findQuest.js @@ -120,10 +120,7 @@ async function queryAndPullQuest(quest) { // (3) Install Quest console.log(chalk.green("\nInstalling quest...")); - if (quest.lang == "solidity") { - // TODO: Refactor this to be multi-protocol friendly - await authDownloader.installSubpackage(); - } + await authDownloader.installSubpackage(); // (4) Commit new changes (Skip if dev mode) try { diff --git a/src/submitQuest.js b/src/submitQuest.js index 23998bc..f7468e6 100644 --- a/src/submitQuest.js +++ b/src/submitQuest.js @@ -1,6 +1,5 @@ import chalk from "chalk"; import { navigateToQuestDirectory } from "./utils/navigation.js"; -import { ProgressBar } from "./utils/progressbar.js"; import { simpleGit } from "simple-git"; import { currentWorkingQuest } from "./quest/index.js"; import { From 4f3197057ab3fbfdca0730befe379e6a1e497521 Mon Sep 17 00:00:00 2001 From: Bryan Wee Date: Thu, 7 Mar 2024 14:37:04 +0800 Subject: [PATCH 12/12] fix: avoid skipping progress bar animation --- src/utils/downloader.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/downloader.js b/src/utils/downloader.js index ea9483a..07dfd15 100644 --- a/src/utils/downloader.js +++ b/src/utils/downloader.js @@ -18,7 +18,7 @@ export class QuestDownloader { async downloadQuest(owner, repo, directoryPath, options = {}) { const zipFilePath = `${directoryPath}.zip`; - this.progressBar.start(); + const startAnimation = this.progressBar.start(); try { const file = await this._octokit.repos.getContent({ @@ -36,10 +36,12 @@ export class QuestDownloader { await this.unzip(decodedContent); } catch (err) { + await startAnimation; // Wait for initial animation to finish this.progressBar.fail("Download failed"); throw err; } + await startAnimation; // Wait for initial animation to finish await this.progressBar.stop("Download finished"); }