From 71fb9f8b345cc88eddd7bc334f9644bebf1313a6 Mon Sep 17 00:00:00 2001 From: Chris Raible <chris@ghost.org> Date: Tue, 29 Oct 2024 13:03:49 -0700 Subject: [PATCH] Converted dev container's onCreateCommand to JavaScript (#21457) no issue - The onCreateCommand was previously a bash script, which made it a bit more challenging to read and make changes to it. This commit converts it to JavaScript so it will be easier to make updates to it. --- .devcontainer/createLocalConfig.js | 93 --------------- .devcontainer/devcontainer.json | 2 +- .devcontainer/onCreateCommand.js | 179 +++++++++++++++++++++++++++++ .devcontainer/onCreateCommand.sh | 18 --- 4 files changed, 180 insertions(+), 112 deletions(-) delete mode 100644 .devcontainer/createLocalConfig.js create mode 100644 .devcontainer/onCreateCommand.js delete mode 100755 .devcontainer/onCreateCommand.sh diff --git a/.devcontainer/createLocalConfig.js b/.devcontainer/createLocalConfig.js deleted file mode 100644 index 5447868ddb..0000000000 --- a/.devcontainer/createLocalConfig.js +++ /dev/null @@ -1,93 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const assert = require('node:assert/strict'); - -// Reads the config.local.json file and updates it with environments variables for devcontainer setup -const configBasePath = path.join(__dirname, '..', 'ghost', 'core'); -const configFile = path.join(configBasePath, 'config.local.json'); -let originalConfig = {}; -if (fs.existsSync(configFile)) { - try { - // Backup the user's config.local.json file just in case - // This won't be used by Ghost but can be useful to switch back to local development - const backupFile = path.join(configBasePath, 'config.local-backup.json'); - fs.copyFileSync(configFile, backupFile); - - // Read the current config.local.json file into memory - const fileContent = fs.readFileSync(configFile, 'utf8'); - originalConfig = JSON.parse(fileContent); - } catch (error) { - console.error('Error reading or parsing config file:', error); - process.exit(1); - } -} else { - console.log('Config file does not exist. Creating a new one.'); -} - -let newConfig = {}; -// Change the url if we're in a codespace -if (process.env.CODESPACES === 'true') { - assert.ok(process.env.CODESPACE_NAME, 'CODESPACE_NAME is not defined'); - assert.ok(process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN, 'GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN is not defined'); - const url = `https://${process.env.CODESPACE_NAME}-2368.${process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}`; - newConfig.url = url; -} - -newConfig.database = { - client: 'mysql2', - connection: { - host: 'mysql', - user: 'root', - password: 'root', - database: 'ghost' - } -} -newConfig.adapters = { - Redis: { - host: 'redis', - port: 6379 - } -} - - -// Only update the mail settings if they aren't already set -if (!originalConfig.mail && process.env.MAILGUN_SMTP_PASS && process.env.MAILGUN_SMTP_USER && process.env.MAILGUN_FROM_ADDRESS) { - newConfig.mail = { - transport: 'SMTP', - from: process.env.MAILGUN_FROM_ADDRESS, - options: { - service: 'Mailgun', - host: 'smtp.mailgun.org', - secure: true, - port: 465, - auth: { - user: process.env.MAILGUN_SMTP_USER, - pass: process.env.MAILGUN_SMTP_PASS - } - } - } -} - -// Only update the bulk email settings if they aren't already set -if (!originalConfig.bulkEmail && process.env.MAILGUN_API_KEY && process.env.MAILGUN_DOMAIN) { - newConfig.bulkEmail = { - mailgun: { - baseUrl: 'https://api.mailgun.net/v3', - apiKey: process.env.MAILGUN_API_KEY, - domain: process.env.MAILGUN_DOMAIN, - tag: 'bulk-email' - } - } -} - -// Merge the original config with the new config -const config = {...originalConfig, ...newConfig}; - -// Write the updated config.local.json file -try { - fs.writeFileSync(configFile, JSON.stringify(config, null, 2)); - console.log('Config file updated successfully.'); -} catch (error) { - console.error('Error writing config file:', error); - process.exit(1); -} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bb4396cc18..ac0efdfc08 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,7 @@ "service": "ghost", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "shutdownAction": "stopCompose", - "onCreateCommand": ["./.devcontainer/onCreateCommand.sh"], + "onCreateCommand": ["node", "./.devcontainer/onCreateCommand.js"], "updateContentCommand": ["git", "submodule", "update", "--init", "--recursive"], "postCreateCommand": ["yarn", "knex-migrator", "init"], "remoteEnv": { diff --git a/.devcontainer/onCreateCommand.js b/.devcontainer/onCreateCommand.js new file mode 100644 index 0000000000..72e7706131 --- /dev/null +++ b/.devcontainer/onCreateCommand.js @@ -0,0 +1,179 @@ +// This script is run in the Dev Container right after it is created +// No dependencies are installed at this point so we can't use any npm packages +const fs = require('fs'); +const path = require('path'); +const assert = require('node:assert/strict'); +const { execSync } = require('child_process'); + +// Main function that runs all the setup steps +function main() { + setupLocalConfig(); + runCleanHard(); + runInstall(); + runSubmoduleUpdate(); + runTypescriptBuild(); +} + +// Basic color constants for console output +const colors = { + reset: '\x1b[0m', + bright: '\x1b[1m', + dim: '\x1b[2m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + magenta: '\x1b[35m', + cyan: '\x1b[36m', +}; + +function log(message, color = colors.reset) { + console.log(`${color}${message}${colors.reset}`); +} + +function logError(message, error) { + console.error(`${colors.red}${message}${colors.reset}`, error); +} + +// Creates config.local.json file with the correct values for the devcontainer +function setupLocalConfig() { + log('Setting up local config file...', colors.blue); + // Reads the config.local.json file and updates it with environments variables for devcontainer setup + const configBasePath = path.join(__dirname, '..', 'ghost', 'core'); + const configFile = path.join(configBasePath, 'config.local.json'); + let originalConfig = {}; + if (fs.existsSync(configFile)) { + try { + // Backup the user's config.local.json file just in case + // This won't be used by Ghost but can be useful to switch back to local development + const backupFile = path.join(configBasePath, 'config.local-backup.json'); + fs.copyFileSync(configFile, backupFile); + + // Read the current config.local.json file into memory + const fileContent = fs.readFileSync(configFile, 'utf8'); + originalConfig = JSON.parse(fileContent); + } catch (error) { + logError('Error reading or parsing config file:', error); + process.exit(1); + } + } else { + log('Config file does not exist. Creating a new one.', colors.dim); + } + + let newConfig = {}; + // Change the url if we're in a codespace + if (process.env.CODESPACES === 'true') { + assert.ok(process.env.CODESPACE_NAME, 'CODESPACE_NAME is not defined'); + assert.ok(process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN, 'GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN is not defined'); + const url = `https://${process.env.CODESPACE_NAME}-2368.${process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}`; + newConfig.url = url; + } + + newConfig.database = { + client: 'mysql2', + connection: { + host: 'mysql', + user: 'root', + password: 'root', + database: 'ghost' + } + } + newConfig.adapters = { + Redis: { + host: 'redis', + port: 6379 + } + } + + // Only update the mail settings if they aren't already set + if (!originalConfig.mail && process.env.MAILGUN_SMTP_PASS && process.env.MAILGUN_SMTP_USER && process.env.MAILGUN_FROM_ADDRESS) { + newConfig.mail = { + transport: 'SMTP', + from: process.env.MAILGUN_FROM_ADDRESS, + options: { + service: 'Mailgun', + host: 'smtp.mailgun.org', + secure: true, + port: 465, + auth: { + user: process.env.MAILGUN_SMTP_USER, + pass: process.env.MAILGUN_SMTP_PASS + } + } + } + } + + // Only update the bulk email settings if they aren't already set + if (!originalConfig.bulkEmail && process.env.MAILGUN_API_KEY && process.env.MAILGUN_DOMAIN) { + newConfig.bulkEmail = { + mailgun: { + baseUrl: 'https://api.mailgun.net/v3', + apiKey: process.env.MAILGUN_API_KEY, + domain: process.env.MAILGUN_DOMAIN, + tag: 'bulk-email' + } + } + } + + // Merge the original config with the new config + const config = {...originalConfig, ...newConfig}; + + // Write the updated config.local.json file + try { + fs.writeFileSync(configFile, JSON.stringify(config, null, 2)); + log('Config file updated successfully.', colors.dim); + } catch (error) { + logError('Error writing config file:', error); + process.exit(1); + } +} + +// Deletes node_modules and clears yarn & nx caches +function runCleanHard() { + try { + log('Cleaning up node_modules and yarn/nx caches...', colors.blue); + execSync('yarn clean:hard', { stdio: 'inherit' }); + log('Successfully ran yarn clean:hard', colors.dim); + } catch (error) { + logError('Error running yarn clean:hard:', error); + process.exit(1); + } +} + +// Installs dependencies +function runInstall() { + try { + log('Installing dependencies...', colors.blue); + execSync('yarn install --frozen-lockfile', { stdio: 'inherit' }); + log('Successfully ran yarn install', colors.dim); + } catch (error) { + logError('Error running yarn install:', error); + process.exit(1); + } +} + +// Initializes and updates git submodules +function runSubmoduleUpdate() { + try { + log('Updating git submodules...', colors.blue); + execSync('git submodule update --init --recursive', { stdio: 'inherit' }); + log('Successfully ran git submodule update', colors.dim); + } catch (error) { + logError('Error running git submodule update:', error); + process.exit(1); + } +} + +// Builds the typescript packages +function runTypescriptBuild() { + try { + log('Building typescript packages...', colors.blue); + execSync('yarn nx run-many -t build:ts', { stdio: 'inherit' }); + log('Successfully ran yarn nx run-many -t build:ts', colors.dim); + } catch (error) { + logError('Error running yarn nx run-many -t build:ts:', error); + process.exit(1); + } +} + +main(); \ No newline at end of file diff --git a/.devcontainer/onCreateCommand.sh b/.devcontainer/onCreateCommand.sh deleted file mode 100755 index a227848dc5..0000000000 --- a/.devcontainer/onCreateCommand.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e - -echo "Setting up local config file..." -node .devcontainer/createLocalConfig.js - -echo "Cleaning up any previous installs..." -yarn clean:hard - -echo "Installing dependencies..." -yarn install - -echo "Updating git submodules..." -git submodule update --init --recursive - -echo "Building typescript packages..." -yarn nx run-many -t build:ts \ No newline at end of file