Skip to content

Commit

Permalink
Add two grunt commands (#1939)
Browse files Browse the repository at this point in the history
* chore: deploy commant - not completed

* chore: updated grunt deploy and serve

* chore: in-pgoress

* feat: grunt deploy cli

* chore: tiny updates for optimize

* chore: add warning messages

* feat: improving grunt deploy command

* feat: copy:contracts grunt command

* chore: removed useless option

* chore: removed useless gitignore

* chore: reverted useless updates

* chore: reverted 0.2.3 version

* chore: removed TODO comment

* feat: added the destination folder of deployment

* making grunt serve cli

* feat: grunt serve command

* feat: grunt deploy and serve

* fix: error in backendIndex path

* chore: small commit and cypress retry

* fix: error in using await

* feat: simplified making a directory for deployment

* feat: simplified

* feat: simplified

* feat: build task simplified

* chore: removed useless flags in deploy task
  • Loading branch information
Silver-IT authored May 7, 2024
1 parent 8525604 commit 40b6e48
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 58 deletions.
141 changes: 87 additions & 54 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const chalk = require('chalk')
const crypto = require('crypto')
const { exec, fork } = require('child_process')
const execP = util.promisify(exec)
const { copyFile, readFile, writeFile } = require('fs/promises')
const { readdir, cp, mkdir, access, rm, copyFile, readFile, writeFile } = require('fs/promises')
const fs = require('fs')
const path = require('path')
const { resolve } = path
Expand Down Expand Up @@ -60,20 +60,26 @@ const GI_VERSION = packageJSON.version + (NODE_ENV === 'production' ? `@${new Da
Object.assign(process.env, { CONTRACTS_VERSION, GI_VERSION })

const backendIndex = './backend/index.js'
const distAssets = 'dist/assets'
const distCSS = 'dist/assets/css'
const distDir = 'dist'
const distContracts = 'dist/contracts'
const distJS = 'dist/assets/js'
const serviceWorkerDir = 'frontend/controller/serviceworkers'
const distAssets = `${distDir}/assets`
const distCSS = `${distDir}/assets/css`
const distContracts = `${distDir}/contracts`
const distJS = `${distDir}/assets/js`
const srcDir = 'frontend'
const contractsDir = 'frontend/model/contracts'
const serviceWorkerDir = `${srcDir}/controller/serviceworkers`
const contractsDir = `${srcDir}/model/contracts`
const mainSrc = path.join(srcDir, 'main.js')
const manifestJSON = path.join(contractsDir, 'manifests.json')

const development = NODE_ENV === 'development'
const production = !development

// Make database path available to subprocess
const dbPath = process.env.DB_PATH || (production ? `./${distDir}/data` : './data')
if (!process.env.DB_PATH) {
Object.assign(process.env, { DB_PATH: dbPath })
}

module.exports = (grunt) => {
require('load-grunt-tasks')(grunt)

Expand Down Expand Up @@ -111,46 +117,35 @@ module.exports = (grunt) => {
}

async function generateManifests (dir, version) {
if (development) {
const keyFile = process.env.KEY_FILE || 'key.json'
const pubKeyFile = process.env.PUB_KEY_FILE || 'key.pub.json'
if (fs.existsSync(keyFile)) {
grunt.log.writeln(chalk.underline(`Key file ${keyFile} exists, using that.`))
} else {
grunt.log.writeln(chalk.underline(`\nRunning 'chel keygen --pubout ${pubKeyFile} --out ${keyFile}'`))
const { stdout } = await execWithErrMsg(`./node_modules/.bin/chel keygen --pubout ${pubKeyFile} --out ${keyFile}`)
console.log(stdout)
}
grunt.log.writeln(chalk.underline("\nRunning 'chel manifest'"))
// TODO: do this with JS instead of POSIX commands for Windows support
const { stdout } = await execWithErrMsg(`ls ${dir}/*-slim.js | sed -En 's/.*\\/(.*)-slim.js/\\1/p' | xargs -I {} node_modules/.bin/chel manifest -n gi.contracts/{} -v ${version} -s ${dir}/{}-slim.js ${keyFile} ${dir}/{}.js`, 'error generating manifests')
console.log(stdout)
const keyFile = process.env.KEY_FILE || 'key.json'
const pubKeyFile = process.env.PUB_KEY_FILE || 'key.pub.json'
if (fs.existsSync(keyFile)) {
grunt.log.writeln(chalk.underline(`Key file ${keyFile} exists, using that.`))
} else {
// Only run these in NODE_ENV=development so that production servers
// don't overwrite manifests.json
grunt.log.writeln(chalk.yellow("\n(Skipping) Running 'chel manifest'"))
grunt.log.writeln(chalk.underline(`\nRunning 'chel keygen --pubout ${pubKeyFile} --out ${keyFile}'`))
const { stdout } = await execWithErrMsg(`./node_modules/.bin/chel keygen --pubout ${pubKeyFile} --out ${keyFile}`)
console.log(stdout)
}
grunt.log.writeln(chalk.underline("\nRunning 'chel manifest'"))
// TODO: do this with JS instead of POSIX commands for Windows support
const { stdout } = await execWithErrMsg(`ls ${dir}/*-slim.js | sed -En 's/.*\\/(.*)-slim.js/\\1/p' | xargs -I {} node_modules/.bin/chel manifest -n gi.contracts/{} -v ${version} -s ${dir}/{}-slim.js ${keyFile} ${dir}/{}.js`, 'error generating manifests')
console.log(stdout)
}

async function deployAndUpdateMainSrc (manifestDir, dest) {
if (development) {
grunt.log.writeln(chalk.underline(`Running 'chel deploy' to ${dest}`))
const { stdout } = await execWithErrMsg(`./node_modules/.bin/chel deploy ${dest} ${manifestDir}/*.manifest.json`, 'error deploying contracts')
console.log(stdout)
const r = /contracts\/([^.]+)\.(?:x|[\d.]+)\.manifest.*\/(.*)/g
const manifests = Object.fromEntries(Array.from(stdout.replace(/\\/g, '/').matchAll(r), x => [`gi.contracts/${x[1]}`, x[2]]))
fs.writeFileSync(manifestJSON,
JSON.stringify({ manifests }, null, 2) + '\n',
'utf8')
console.log(chalk.green('manifest JSON written to:'), manifestJSON, '\n')
} else {
// Only run these in NODE_ENV=development so that production servers
// don't overwrite manifests.json
grunt.log.writeln(chalk.yellow("\n(Skipping) Running 'chel deploy'"))
}
grunt.log.writeln(chalk.underline(`Running 'chel deploy' to ${dest}`))
await access(dest).catch(async () => await mkdir(dest))
const { stdout } = await execWithErrMsg(`./node_modules/.bin/chel deploy ${dest} ${manifestDir}/*.manifest.json`, 'error deploying contracts')
console.log(stdout)
const r = /contracts\/([^.]+)\.(?:x|[\d.]+)\.manifest.*\/(.*)/g
const manifests = Object.fromEntries(Array.from(stdout.replace(/\\/g, '/').matchAll(r), x => [`gi.contracts/${x[1]}`, x[2]]))
fs.writeFileSync(manifestJSON,
JSON.stringify({ manifests }, null, 2) + '\n',
'utf8')
console.log(chalk.green('manifest JSON written to:'), manifestJSON, '\n')
}

async function genManifestsAndDeploy (dir, version, dest = './data') {
async function genManifestsAndDeploy (dir, version, dest = dbPath) {
await generateManifests(dir, version)
await deployAndUpdateMainSrc(dir, dest)
}
Expand Down Expand Up @@ -199,10 +194,10 @@ module.exports = (grunt) => {

const databaseOptionBags = {
fs: {
dest: './data/'
dest: dbPath
},
sqlite: {
dest: './data/groupincome.db'
dest: `${dbPath}/groupincome.db`
}
}

Expand Down Expand Up @@ -363,7 +358,7 @@ module.exports = (grunt) => {
clean: { dist: [`${distDir}/*`] },

copy: {
html_files: {
htmlFiles: {
src: 'frontend/index.html',
dest: `${distDir}/index.html`
},
Expand Down Expand Up @@ -394,7 +389,8 @@ module.exports = (grunt) => {
cmd: 'node node_modules/mocha/bin/mocha --require ./scripts/mocha-helper.js --exit -R spec --bail "./{test/,!(node_modules|ignored|dist|historical|test)/**/}*.test.js"',
options: { env: process.env }
},
chelDeployAll: 'find contracts -iname "*.manifest.json" | xargs -r ./node_modules/.bin/chel deploy ./data'
chelDevDeploy: `find contracts -iname "*.manifest.json" | xargs -r ./node_modules/.bin/chel deploy ${dbPath}`,
chelProdDeploy: `find ${distContracts} -iname "*.manifest.json" | xargs -r ./node_modules/.bin/chel deploy ${dbPath}`
}
})

Expand All @@ -404,6 +400,35 @@ module.exports = (grunt) => {

let child = null

grunt.registerTask('copyAndMoveContracts', async function () {
const done = this.async()
const { contractsVersion } = packageJSON

// NOTE: the latest version
await mkdir(`${distContracts}/${contractsVersion}`)
for (const dirent of await readdir(distContracts, { withFileTypes: true })) {
if (dirent.isFile()) {
const fileName = dirent.name
await copyFile(`${distContracts}/${fileName}`, `${distContracts}/${contractsVersion}/${fileName}`)
await rm(`${distContracts}/${fileName}`)
}
}

// NOTE: all previously pinned versions
const versions = (await readdir('contracts', { withFileTypes: true })).filter(dirent => {
return dirent.isDirectory() && dirent.name !== contractsVersion
}).map(dirent => dirent.name)
for (const version of versions) {
await cp(`contracts/${version}`, `${distContracts}/${version}`, { recursive: true })
}

done()
})

grunt.registerTask('chelDeploy', function () {
grunt.task.run([production ? 'exec:chelProdDeploy' : 'exec:chelDevDeploy'])
})

// Useful helper task for `grunt test`.
grunt.registerTask('backend:launch', '[internal]', function () {
const done = this.async()
Expand Down Expand Up @@ -522,9 +547,21 @@ module.exports = (grunt) => {
grunt.task.run(`_pin:${version}`)
})

grunt.registerTask('deploy', function () {
if (!production) {
console.warn(chalk.yellow('Please run with NODE_ENV=production'))
}
grunt.task.run(['checkDependencies', 'build', 'copyAndMoveContracts'])
})
grunt.registerTask('serve', function () {
if (!production) {
console.warn(chalk.yellow('Please run with NODE_ENV=production'))
}
grunt.task.run(['chelDeploy', 'backend:launch', 'keepalive'])
})

grunt.registerTask('default', ['dev'])
// TODO: add 'deploy' as per https://github.com/okTurtles/group-income/issues/10
grunt.registerTask('dev', ['exec:gitconfig', 'checkDependencies', 'exec:chelDeployAll', 'build:watch', 'backend:relaunch', 'keepalive'])
grunt.registerTask('dev', ['exec:gitconfig', 'checkDependencies', 'chelDeploy', 'build:watch', 'backend:relaunch', 'keepalive'])
grunt.registerTask('dist', ['exec:gitconfig', 'build'])

// --------------------
Expand Down Expand Up @@ -563,12 +600,8 @@ module.exports = (grunt) => {
// first we build the contracts since genManifestsAndDeploy depends on that
// and then we build the main bundle since it depends on manifests.json
await Promise.all([buildContracts.run(), buildContractsSlim.run()])
.then(() => {
return genManifestsAndDeploy(distContracts, packageJSON.contractsVersion)
})
.then(() => {
return Promise.all([buildMain.run(), buildServiceWorkers.run()])
})
.then(() => genManifestsAndDeploy(distContracts, packageJSON.contractsVersion))
.then(() => Promise.all([buildMain.run(), buildServiceWorkers.run()]))
.catch(error => {
grunt.log.error(error.message)
process.exit(1)
Expand Down Expand Up @@ -670,9 +703,9 @@ module.exports = (grunt) => {
killKeepAlive = this.async()
})

grunt.registerTask('test', ['build', 'exec:chelDeployAll', 'backend:launch', 'exec:test', 'cypress'])
grunt.registerTask('test', ['build', 'chelDeploy', 'backend:launch', 'exec:test', 'cypress'])
grunt.registerTask('test:unit', ['backend:launch', 'exec:test'])
grunt.registerTask('test:cypress', ['build', 'exec:chelDeployAll', 'backend:launch', 'cypress'])
grunt.registerTask('test:cypress', ['build', 'chelDeploy', 'backend:launch', 'cypress'])

// -------------------------------------------------------------------------
// Process event handlers
Expand Down
7 changes: 4 additions & 3 deletions backend/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ const production = process.env.NODE_ENV === 'production'
const persistence = process.env.GI_PERSIST || (production ? 'fs' : undefined)

// Default database options. Other values may be used e.g. in tests.
const dbRootPath = process.env.DB_PATH || './data'
const options = {
fs: {
dirname: './data'
dirname: dbRootPath
},
sqlite: {
dirname: './data',
dirname: dbRootPath,
filename: 'groupincome.db'
}
}
Expand Down Expand Up @@ -169,7 +170,7 @@ export default async () => {
sbp('sbp/selectors/lock', ['chelonia/db/get', 'chelonia/db/set', 'chelonia/db/delete'])
}
// TODO: Update this to only run when persistence is disabled when `chel deploy` can target SQLite.
if (persistence !== 'fs' || options.fs.dirname !== './data') {
if (persistence !== 'fs' || options.fs.dirname !== dbRootPath) {
// Remember to keep these values up-to-date.
const HASH_LENGTH = 52
const CONTRACT_MANIFEST_MAGIC = '{"head":"{\\"manifestVersion\\"'
Expand Down
9 changes: 8 additions & 1 deletion frontend/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,19 @@ async function startApp () {
sbp('okTurtles.data/set', PUBSUB_INSTANCE, sbp('chelonia/connect', {
messageHandlers: {
[NOTIFICATION_TYPE.VERSION_INFO] (msg) {
const isDevelopment = process.env.NODE_ENV === 'development'
const ourVersion = process.env.GI_VERSION
const theirVersion = msg.data.GI_VERSION

const ourContractsVersion = process.env.CONTRACTS_VERSION
const theirContractsVersion = msg.data.CONTRACTS_VERSION
if (ourVersion !== theirVersion || ourContractsVersion !== theirContractsVersion) {

const isContractVersionDiff = ourContractsVersion !== theirContractsVersion
const isGIVersionDiff = ourVersion !== theirVersion
// We only compare GI_VERSION in development mode so that the page auto-refreshes if `grunt dev` is re-run
// This check cannot be done in production mode as it would lead to an infinite page refresh bug
// when using `grunt deploy` with `grunt serve`
if (isContractVersionDiff || (isDevelopment && isGIVersionDiff)) {
sbp('okTurtles.events/emit', NOTIFICATION_TYPE.VERSION_INFO, { ...msg.data })
}
},
Expand Down

0 comments on commit 40b6e48

Please sign in to comment.