Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add two grunt commands #1939

Merged
merged 26 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2368f3e
chore: deploy commant - not completed
Silver-IT Mar 25, 2024
c46084b
chore: updated grunt deploy and serve
Silver-IT Mar 26, 2024
cbce1b0
chore: in-pgoress
Silver-IT Mar 27, 2024
caf62b4
feat: grunt deploy cli
Silver-IT Mar 28, 2024
c8664b8
chore: tiny updates for optimize
Silver-IT Mar 29, 2024
62f3e43
chore: add warning messages
Silver-IT Apr 2, 2024
40cafd4
feat: improving grunt deploy command
Silver-IT Apr 12, 2024
81b1df0
feat: copy:contracts grunt command
Silver-IT Apr 12, 2024
ae31cda
chore: removed useless option
Silver-IT Apr 12, 2024
ed874ee
chore: removed useless gitignore
Silver-IT Apr 12, 2024
6d95b80
chore: reverted useless updates
Silver-IT Apr 12, 2024
1c38dda
chore: reverted 0.2.3 version
Silver-IT Apr 12, 2024
79382cd
fix: resolved conflicts
Silver-IT Apr 16, 2024
24dce62
chore: removed TODO comment
Silver-IT Apr 16, 2024
940c6d9
feat: added the destination folder of deployment
Silver-IT Apr 16, 2024
6434fd8
making grunt serve cli
Silver-IT Apr 17, 2024
af94054
feat: grunt serve command
Silver-IT Apr 17, 2024
2d6926d
feat: grunt deploy and serve
Silver-IT Apr 18, 2024
38c4547
fix: error in backendIndex path
Silver-IT Apr 18, 2024
ed079c1
chore: small commit and cypress retry
Silver-IT Apr 18, 2024
2e40c55
fix: error in using await
Silver-IT Apr 18, 2024
86cfc11
feat: simplified making a directory for deployment
Silver-IT Apr 18, 2024
b3706b3
feat: simplified
Silver-IT May 2, 2024
2a87d61
feat: simplified
Silver-IT May 2, 2024
1faa788
feat: build task simplified
Silver-IT May 2, 2024
3130bb6
chore: removed useless flags in deploy task
Silver-IT May 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading