diff --git a/.dockerignore b/.dockerignore index 6bac71e2..5f99e0dc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,11 @@ -data/* -!data/scores.json +**/.git +**/.github +README.md +data +built +doc node_modules -frontend/lib +Makefile + +# Vim swap files +**/.*.sw[po] diff --git a/.editorconfig b/.editorconfig index 32e74e79..c96f6b74 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,3 +9,7 @@ trim_trailing_whitespace = true [*.{js,jsx,css,js.default}] indent_size = 2 indent_style = space + +[Makefile] +# Makefiles require tabs for indentation +indent_style = tab diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e213acab..098e156a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,55 +13,28 @@ on: - '**.md' jobs: - test: - name: Run tests - - strategy: - fail-fast: false - matrix: - node_version: - - 14 - - 16 + verify: + name: Verify runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - - - name: Setup - uses: actions/setup-node@v2.5.1 with: - node-version: ${{matrix.node_version}} + fetch-depth: 0 - # See https://github.com/npm/cli/issues/2610 - - name: Workaround for Node 14 - if: matrix.node_version == '14' - run: sed -i 's/git+ssh/git+https/g' package-lock.json + - name: Build + run: make docker DOCKER_CACHE_OFF=1 - - name: Install - run: npm ci + - name: Lint + run: make docker-lint - name: Test - run: npm test - - - docker: - name: Test docker image - - runs-on: ubuntu-latest - - continue-on-error: true - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Build - run: docker build --tag dr4ft-app . + run: make docker-test - - name: Run - run: docker run -dp 1337:1337 dr4ft-app + - name: Run docker image + run: make docker-run - name: Show info run: | @@ -70,24 +43,5 @@ jobs: docker ps -a echo docker images - - - lint: - name: Run ESLint - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup - uses: actions/setup-node@v2.5.1 - with: - node-version: 16 - - - name: Install - run: npm ci --ignore-scripts - - - name: Run ESLint - run: npm run lint + echo + make docker-logs FOLLOW= diff --git a/Dockerfile b/Dockerfile index 0fabc240..ab07fa37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,42 @@ FROM node:lts-alpine -ENV NPM_CONFIG_LOGLEVEL warn -# Install "git" -RUN apk update \ - && apk add alpine-sdk +ARG VERSION_INFO=noVersion + +ENV NPM_CONFIG_LOGLEVEL warn +ENV PORT=1337 # Set working dir as /app WORKDIR /app -# Add sources to /app -COPY . . -RUN adduser -S dr4ftuser -RUN chown dr4ftuser -R . -USER dr4ftuser - # Install the dependencies -RUN npm ci +COPY --chown=node package.json . +COPY --chown=node package-lock.json . +RUN npm ci --ignore-scripts + +# Update card database +COPY --chown=node backend/core backend/core +COPY --chown=node config/ config/ +COPY --chown=node scripts/ scripts/ +RUN npm run download_allsets \ + && npm run download_booster_rules \ + && chown node -R data/ + +# Add sources to /app +COPY --chown=node LICENSE . +COPY --chown=node .gitignore . +COPY --chown=node .babelrc . +COPY --chown=node .eslintrc.js . +COPY --chown=node .mocharc.yaml . +COPY --chown=node webpack.prod.js . +COPY --chown=node webpack.common.js . +COPY --chown=node webpack.dev.js . +COPY --chown=node app.json . +COPY --chown=node app.js . +COPY --chown=node backend/ backend/ +COPY --chown=node frontend/ frontend/ -# Publish the port 1337 -EXPOSE 1337 +RUN npm run webpack +ENV VERSION_INFO=$VERSION_INFO # Run the server ENTRYPOINT [ "npm", "start" ] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..617ffaf0 --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +VERSION_INFO ?= $$(git describe --tags) +IMAGE ?= dr4ft-app +CONTAINER ?= $(IMAGE)-container +PORT ?= 1337 + + +# Show makefile help by default +.DEFAULT_GOAL = help + + +CACHE_OFF=0 +ifeq ($(CACHE_OFF), 1) + NO_CACHE_FLAG = --no-cache +else + NO_CACHE_FLAG = +endif +.PHONY: docker +docker: ## Build docker image + @echo "Building with version info $(VERSION_INFO)" + docker build $(NO_CACHE_FLAG) \ + --build-arg VERSION_INFO=$(VERSION_INFO) \ + -t $(IMAGE) . + + +.PHONY: docker-run +docker-run: docker-stop docker ## Run app in docker container + docker run -d \ + --name $(CONTAINER) \ + --env "PORT=$(PORT)" \ + -p $(PORT):$(PORT) \ + $(IMAGE) + @echo "##########################################" + @echo "Dr4ft now running at http://localhost:$(PORT)" + @echo "##########################################" + + +.PHONY: docker-stop +docker-stop: ## Stop running docker container + docker stop $(CONTAINER) > /dev/null 2>&1 || true + docker container rm $(CONTAINER) > /dev/null 2>&1 || true + + +.PHONY: docker-logs +FOLLOW := -f +docker-logs: ## Show logs from running docker container + docker logs $(FOLLOW) $(CONTAINER) + + +.PHONY: docker-test +docker-test: docker ## Run tests in docker container + docker run --rm \ + --entrypoint npm \ + $(IMAGE) run test:js + + +.PHONY: docker-lint +docker-lint: docker ## Lint code in docker container + docker run --rm \ + --entrypoint npm \ + $(IMAGE) run lint + + +.PHONY: docker-clean +docker-clean: docker-stop ## Remove any built docker images + docker rmi -f $$(docker images -q $(IMAGE)) > /dev/null 2>&1 || true + + +# Output help string for each target. +# With thanks to: https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +.PHONY: help +help: ## Show this help + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) diff --git a/app.js b/app.js index af0eebbe..b56de839 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,7 @@ const bodyParser = require("body-parser"); const {spawn} = require("child_process"); const {app: config, version} = require("./config"); -const logger = require("./backend/logger"); +const logger = require("./backend/core/logger"); const router = require("./backend/router"); const apiRouter = require("./backend/api/"); require("./backend/data-watch"); diff --git a/backend/api/sets.js b/backend/api/sets.js index 9c77dfec..7650fe02 100644 --- a/backend/api/sets.js +++ b/backend/api/sets.js @@ -1,12 +1,11 @@ const fs = require("fs"); const express = require("express"); const setsRouter = express.Router(); -const { getSets, saveSetAndCards } = require("../data"); -const doSet = require("../import/doSet"); -const logger = require("../logger"); -const parser = require("../import/xml/parser"); +const { getDataDir, getSets, saveSetAndCards } = require("../core/data"); +const doSet = require("../core/import/doSet"); +const logger = require("../core/logger"); +const parser = require("../core/import/xml/parser"); const path = require("path"); -const {getDataDir} = require("../data"); const customDataDir = path.join(getDataDir(), "custom"); if (!fs.existsSync(customDataDir)) { diff --git a/backend/boosterGenerator.js b/backend/boosterGenerator.js index 7ed72806..6fd19542 100644 --- a/backend/boosterGenerator.js +++ b/backend/boosterGenerator.js @@ -1,5 +1,5 @@ -const {getCardByUuid, getSet, getBoosterRules} = require("./data"); -const logger = require("./logger"); +const {getCardByUuid, getSet, getBoosterRules} = require("./core/data"); +const logger = require("./core/logger"); const weighted = require("weighted"); const {sample, sampleSize, random, concat} = require("lodash"); diff --git a/backend/data.js b/backend/core/data.js similarity index 100% rename from backend/data.js rename to backend/core/data.js diff --git a/backend/data.spec.js b/backend/core/data.spec.js similarity index 100% rename from backend/data.spec.js rename to backend/core/data.spec.js diff --git a/backend/import/doSet.js b/backend/core/import/doSet.js similarity index 100% rename from backend/import/doSet.js rename to backend/core/import/doSet.js diff --git a/backend/import/keyCards.js b/backend/core/import/keyCards.js similarity index 100% rename from backend/import/keyCards.js rename to backend/core/import/keyCards.js diff --git a/backend/import/keyCards.spec.js b/backend/core/import/keyCards.spec.js similarity index 100% rename from backend/import/keyCards.spec.js rename to backend/core/import/keyCards.spec.js diff --git a/backend/import/toBoosterCard.js b/backend/core/import/toBoosterCard.js similarity index 100% rename from backend/import/toBoosterCard.js rename to backend/core/import/toBoosterCard.js diff --git a/backend/import/toBoosterCard.spec.js b/backend/core/import/toBoosterCard.spec.js similarity index 100% rename from backend/import/toBoosterCard.spec.js rename to backend/core/import/toBoosterCard.spec.js diff --git a/backend/import/xml/parser.js b/backend/core/import/xml/parser.js similarity index 100% rename from backend/import/xml/parser.js rename to backend/core/import/xml/parser.js diff --git a/backend/import/xml/parser.spec.js b/backend/core/import/xml/parser.spec.js similarity index 100% rename from backend/import/xml/parser.spec.js rename to backend/core/import/xml/parser.spec.js diff --git a/backend/logger.js b/backend/core/logger.js similarity index 91% rename from backend/logger.js rename to backend/core/logger.js index cb94af8e..3da7be9d 100644 --- a/backend/logger.js +++ b/backend/core/logger.js @@ -1,6 +1,6 @@ const { createLogger, format, transports } = require("winston"); const { combine, timestamp, printf } = format; -const { logger: config } = require("../config"); +const { logger: config } = require("../../config"); const logger = createLogger({ level: config.level, diff --git a/backend/mtgjson.js b/backend/core/mtgjson.js similarity index 93% rename from backend/mtgjson.js rename to backend/core/mtgjson.js index 60c0fc6a..677515f4 100644 --- a/backend/mtgjson.js +++ b/backend/core/mtgjson.js @@ -1,6 +1,6 @@ const fs = require("fs"); const path = require("path"); -const {getDataDir} = require("../backend/data"); +const {getDataDir} = require("./data"); const VERSION_FILE = path.join(getDataDir(), "version.json"); const logger = require("./logger"); const semver = require("semver"); diff --git a/backend/data-watch.js b/backend/data-watch.js index c0c32f25..57a93ec1 100644 --- a/backend/data-watch.js +++ b/backend/data-watch.js @@ -1,6 +1,6 @@ const fs = require("fs"); -const {reloadData} = require("./data"); -const logger = require("./logger"); +const {reloadData} = require("./core/data"); +const logger = require("./core/logger"); /** * Add a watch on fs to get updated even from external process diff --git a/backend/game.js b/backend/game.js index 62135eee..b4efa171 100644 --- a/backend/game.js +++ b/backend/game.js @@ -8,9 +8,9 @@ const Human = require("./player/human"); const Pool = require("./pool"); const Room = require("./room"); const Rooms = require("./rooms"); -const logger = require("./logger"); +const logger = require("./core/logger"); const Sock = require("./sock"); -const {saveDraftStats, getDataDir} = require("./data"); +const {saveDraftStats, getDataDir} = require("./core/data"); module.exports = class Game extends Room { constructor({ hostId, title, seats, type, sets, cube, isPrivate, modernOnly, totalChaos, chaosPacksNumber, picksPerPack }) { diff --git a/backend/player/bot.js b/backend/player/bot.js index fe89f747..057c3df4 100644 --- a/backend/player/bot.js +++ b/backend/player/bot.js @@ -1,7 +1,7 @@ const {sample, pull, times} = require("lodash"); const Player = require("./index"); -const logger = require("../logger"); +const logger = require("../core/logger"); module.exports = class Bot extends Player { constructor(picksPerPack, burnsPerPack, gameId) { diff --git a/backend/player/human.js b/backend/player/human.js index ca30a026..8ac4355f 100644 --- a/backend/player/human.js +++ b/backend/player/human.js @@ -3,7 +3,7 @@ const {pull, find, pullAllWith, remove, times, sample, chain} = require("lodash" const Player = require("./index"); const util = require("../util"); const hash = require("../hash"); -const logger = require("../logger"); +const logger = require("../core/logger"); module.exports = class Human extends Player { constructor(sock, picksPerPack, burnsPerPack, gameId) { diff --git a/backend/pool.js b/backend/pool.js index 304b8859..fb585ab9 100644 --- a/backend/pool.js +++ b/backend/pool.js @@ -1,6 +1,6 @@ const {sample, shuffle, random, range, times, constant, pull} = require("lodash"); const boosterGenerator = require("./boosterGenerator"); -const { getCardByUuid, getCardByName, getRandomSet, getExpansionOrCoreModernSets: getModernList, getExansionOrCoreSets: getSetsList } = require("./data"); +const { getCardByUuid, getCardByName, getRandomSet, getExpansionOrCoreModernSets: getModernList, getExansionOrCoreSets: getSetsList } = require("./core/data"); const draftId = require("uuid").v1; /** diff --git a/backend/pool.spec.js b/backend/pool.spec.js index 8af099b2..acd4f4b9 100644 --- a/backend/pool.spec.js +++ b/backend/pool.spec.js @@ -3,7 +3,7 @@ const assert = require("assert"); const Pool = require("./pool"); const {range, times, constant} = require("lodash"); -const {getPlayableSets} = require("./data"); +const {getPlayableSets} = require("./core/data"); const assertPackIsCorrect = (got) => { const cardIds = new Set(); diff --git a/backend/router.js b/backend/router.js index 2bcd5742..57070199 100644 --- a/backend/router.js +++ b/backend/router.js @@ -3,7 +3,7 @@ const Game = require("./game"); const Rooms = require("./rooms"); const Sock = require("./sock"); const util = require("./util"); -const logger = require("./logger"); +const logger = require("./core/logger"); function create(opts) { try { diff --git a/backend/sock.js b/backend/sock.js index a4be5620..df3b5ece 100644 --- a/backend/sock.js +++ b/backend/sock.js @@ -1,7 +1,7 @@ const { EventEmitter } = require("events"); const { app: { DEFAULT_USERNAME } } = require("../config"); -const { getPlayableSets, getLatestReleasedSet, getBoosterRulesVersion } = require("./data"); -const { getVersion } = require("./mtgjson"); +const { getPlayableSets, getLatestReleasedSet, getBoosterRulesVersion } = require("./core/data"); +const { getVersion } = require("./core/mtgjson"); // All sockets currently connected to the server. let allSocks = []; diff --git a/backend/util.js b/backend/util.js index 75cdcad7..ff7c0e4c 100644 --- a/backend/util.js +++ b/backend/util.js @@ -1,6 +1,6 @@ const assert = require("assert"); const {countBy} = require("lodash"); -const { getSet, getCardByName } = require("./data"); +const { getSet, getCardByName } = require("./core/data"); const BASICS = [ "Forest", "Island", diff --git a/config/version.js b/config/version.js index 89bafd54..711894ee 100644 --- a/config/version.js +++ b/config/version.js @@ -1,16 +1,23 @@ /* eslint no-console: "off" */ "use strict"; -const execSync = require("child_process").execSync; +const getVersionInfo = () => { + if (process.env.VERSION_INFO) { + return process.env.VERSION_INFO; + } -let versionInfo = "noVersion"; -try { - versionInfo = execSync("git describe --tags") - .toString().trim(); -} catch(err) { - console.log(err); -} + try { + const execSync = require("child_process").execSync; + return execSync("git describe --tags") + .toString() + .trim(); + } catch(err) { + console.log(err); + return "noVersion"; + } +}; +const versionInfo = getVersionInfo(); const versionInfoParts = versionInfo.split("-"); const VERSION = diff --git a/package.json b/package.json index 98bbd6d4..fe5f03b2 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,11 @@ "lint": "eslint --ignore-path .gitignore .", "test": "npm run postinstall && npm run test:js", "test:js": "mocha \"./{,!(node_modules|built)/**/}*.spec.*js\" --exit --require @babel/register", - "postinstall": " npm run download_allsets && npm run download_booster_rules && webpack --config webpack.prod.js", + "postinstall": "npm run download_allsets && npm run download_booster_rules && webpack --config webpack.prod.js", "update_database": "node scripts/update_database ", "download_allsets": "node scripts/download_allsets", - "download_booster_rules": "node scripts/download_booster_rules" + "download_booster_rules": "node scripts/download_booster_rules", + "webpack": "webpack --config webpack.prod.js" }, "dependencies": { "@babel/cli": "^7.17.6", diff --git a/scripts/download_allsets.js b/scripts/download_allsets.js index 6800f29d..205aa713 100644 --- a/scripts/download_allsets.js +++ b/scripts/download_allsets.js @@ -4,9 +4,9 @@ const https = require("https"); const unzip = require("unzipper"); const semver = require("semver"); const updateDatabase = require("./update_database"); -const logger = require("../backend/logger"); -const { refresh: refreshVersion } = require("../backend/mtgjson"); -const {getDataDir} = require("../backend/data"); +const logger = require("../backend/core/logger"); +const { refresh: refreshVersion } = require("../backend/core/mtgjson"); +const {getDataDir} = require("../backend/core/data"); const mtgJsonURL = "https://www.mtgjson.com/api/v5/AllSetFiles.zip"; const versionURL = "https://www.mtgjson.com/api/v5/Meta.json"; diff --git a/scripts/download_booster_rules.js b/scripts/download_booster_rules.js index f4614466..2e79a8ac 100644 --- a/scripts/download_booster_rules.js +++ b/scripts/download_booster_rules.js @@ -1,6 +1,6 @@ const axios = require("axios"); -const logger = require("../backend/logger"); -const {getBoosterRulesVersion, getCardByUuid, getSet, saveBoosterRules} = require("../backend/data"); +const logger = require("../backend/core/logger"); +const {getBoosterRulesVersion, getCardByUuid, getSet, saveBoosterRules} = require("../backend/core/data"); const URL = "https://raw.githubusercontent.com/taw/magic-sealed-data/master/sealed_basic_data.json"; const REPO_URL = "https://api.github.com/repos/taw/magic-sealed-data/git/refs/heads/master"; diff --git a/scripts/update_database.js b/scripts/update_database.js index 5b385d40..0d765985 100644 --- a/scripts/update_database.js +++ b/scripts/update_database.js @@ -1,8 +1,8 @@ const fs = require("fs"); const path = require("path"); -const logger = require("../backend/logger"); -const { saveSetsAndCards, getDataDir } = require("../backend/data"); -const doSet = require("../backend/import/doSet"); +const logger = require("../backend/core/logger"); +const { saveSetsAndCards, getDataDir } = require("../backend/core/data"); +const doSet = require("../backend/core/import/doSet"); const updateDatabase = () => { let allCards = {};