From 398f96249c681760868bfd03930c54a6cf7971e6 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 6 Dec 2023 20:11:52 +0100 Subject: [PATCH] Added MySQL setup via Docker refs https://github.com/TryGhost/DevOps/issues/118 - we should standardize how we run MySQL, so there aren't massive gaps in developer experience - Docker is great for this, and it's pretty easy to set up - this adds a docker-compose.yml file with a small MySQL setup - also configures `yarn setup` to spin up the Docker container and add it to config.local.json - in the near future, we'll provide a base SQL file in .github/scripts/mysql-preload to ensure the DB is in a known state --- .github/scripts/docker-compose.yml | 19 ++++++ .github/scripts/mysql-preload/.keep | 0 .github/scripts/setup.js | 94 +++++++++++++++++++++++++++++ package.json | 3 +- 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/docker-compose.yml create mode 100644 .github/scripts/mysql-preload/.keep create mode 100644 .github/scripts/setup.js diff --git a/.github/scripts/docker-compose.yml b/.github/scripts/docker-compose.yml new file mode 100644 index 0000000000..eb2caaeed9 --- /dev/null +++ b/.github/scripts/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3.8' + +services: + mysql: + image: mysql:8.0.35 + container_name: ghost-mysql + ports: + - "3306:3306" + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: ghost + restart: always + volumes: + # Turns out you can drop .sql or .sql.gz files in here, cool! + - ./mysql-preload:/docker-entrypoint-initdb.d + healthcheck: + test: "mysql -uroot -proot ghost -e 'select 1'" + interval: 1s + retries: 120 diff --git a/.github/scripts/mysql-preload/.keep b/.github/scripts/mysql-preload/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.github/scripts/setup.js b/.github/scripts/setup.js new file mode 100644 index 0000000000..59fd37b3b5 --- /dev/null +++ b/.github/scripts/setup.js @@ -0,0 +1,94 @@ +const {spawn} = require('child_process'); +const fs = require('fs').promises; +const path = require('path'); + +const chalk = require('chalk'); + +/** + * Run a command and stream output to the console + * + * @param {string} command + * @param {string[]} args + * @param {object} options + */ +async function runAndStream(command, args, options) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + stdio: 'inherit', + ...options + }); + + child.on('close', (code) => { + if (code === 0) { + resolve(code); + } else { + reject(new Error(`'${command} ${args.join(' ')}' exited with code ${code}`)); + } + }); + + }); +} + +(async () => { + if (process.env.NODE_ENV !== 'development') { + console.log(chalk.yellow(`NODE_ENV is not development, skipping setup`)); + return; + } + + const coreFolder = path.join(__dirname, '../../ghost/core'); + const config = require('../../ghost/core/core/shared/config/loader').loadNconf({ + customConfigPath: coreFolder + }); + + const dbClient = config.get('database:client'); + + if (!dbClient.includes('mysql')) { + let mysqlSetup = false; + console.log(chalk.blue(`Attempting to setup MySQL via Docker`)); + try { + await runAndStream('yarn', ['docker:reset'], {cwd: path.join(__dirname, '../../')}); + mysqlSetup = true; + } catch (err) { + console.error(chalk.red('Failed to run MySQL Docker container'), err); + console.error(chalk.red('Hint: is Docker installed and running?')); + } + + if (mysqlSetup) { + console.log(chalk.blue(`Adding MySQL credentials to config.local.json`)); + const currentConfigPath = path.join(coreFolder, 'config.local.json'); + + let currentConfig; + try { + currentConfig = require(currentConfigPath); + } catch (err) { + currentConfig = {}; + } + + currentConfig.database = { + client: 'mysql', + connection: { + host: '127.0.0.1', + user: 'root', + password: 'root', + database: 'ghost' + } + }; + + try { + await fs.writeFile(currentConfigPath, JSON.stringify(currentConfig, null, 4)); + } catch (err) { + console.error(chalk.red('Failed to write config.local.json'), err); + console.log(chalk.yellow(`Please add the following to config.local.json:\n`), JSON.stringify(currentConfig, null, 4)); + process.exit(1); + } + + console.log(chalk.blue(`Running knex-migrator init`)); + await runAndStream('yarn', ['knex-migrator', 'init'], {cwd: coreFolder}); + + //console.log(chalk.blue(`Running data generator`)); + //await runAndStream('node', ['index.js', 'generate-data'], {cwd: coreFolder}); + } + } else { + console.log(chalk.green(`MySQL already configured, skipping setup`)); + } +})(); diff --git a/package.json b/package.json index a4a986bedc..3f8b7f6848 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "dev": "node .github/scripts/dev.js", "fix": "yarn cache clean && rm -rf node_modules && yarn", "knex-migrator": "yarn workspace ghost run knex-migrator", - "setup": "yarn && git submodule update --init", + "setup": "yarn && git submodule update --init && NODE_ENV=development node .github/scripts/setup.js", + "docker:reset": "docker-compose -f .github/scripts/docker-compose.yml down -v && docker-compose -f .github/scripts/docker-compose.yml up -d --wait", "lint": "nx run-many -t lint", "test": "nx run-many -t test", "test:unit": "nx run-many -t test:unit",