From 4dc604c5489b39edd4bc78f05b981a52ee85d8b7 Mon Sep 17 00:00:00 2001 From: David White Date: Tue, 23 Jul 2024 08:55:01 +0100 Subject: [PATCH 01/17] Add git package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 8c31af38cc..6a2bc9e5a0 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "path": "~0.12.7", "path-to-regexp": "~6.2.2", "qs": "~6.11.2", + "simple-git": "~3.25.0", "socket.io": "~4.7.2", "splitargs": "~0.0.7", "store": "~2.0.12", From 0bb819c7c292dfc058d570eb0cab7bcbf2a7a816 Mon Sep 17 00:00:00 2001 From: David White Date: Tue, 23 Jul 2024 14:57:19 +0100 Subject: [PATCH 02/17] First commit --- ZelBack/config/default.js | 3 + ZelBack/src/services/dbHelper.js | 10 +- ZelBack/src/services/utils/fluxRepository.js | 67 ++++++++++ apiServer.js | 132 ++++++++++++++++++- 4 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 ZelBack/src/services/utils/fluxRepository.js diff --git a/ZelBack/config/default.js b/ZelBack/config/default.js index c14075bd5a..d5016467c8 100644 --- a/ZelBack/config/default.js +++ b/ZelBack/config/default.js @@ -83,6 +83,9 @@ module.exports = { fluxTeamFluxID: '1hjy4bCYBJr4mny4zCE85J94RXa8W6q37', fluxSupportTeamFluxID: '16iJqiVbHptCx87q6XQwNpKdgEZnFtKcyP', deterministicNodesStart: 558000, + preprodProbability: 0.07, + fluxRepoUrl: 'https://github.com/RunOnFlux/flux.git', + preprodBranchName: 'preprod', fluxapps: { // in flux main chain per month (blocksLasting) price: [ diff --git a/ZelBack/src/services/dbHelper.js b/ZelBack/src/services/dbHelper.js index 643147f44d..4fa9086af6 100644 --- a/ZelBack/src/services/dbHelper.js +++ b/ZelBack/src/services/dbHelper.js @@ -23,7 +23,7 @@ function databaseConnection() { * * @param {string} [url] * - * @returns {object} mongodb.MongoClient + * @returns {object} MongoClient */ async function connectMongoDb(url) { const connectUrl = url || mongoUrl; @@ -32,17 +32,17 @@ async function connectMongoDb(url) { useUnifiedTopology: true, maxPoolSize: 100, }; - const db = await MongoClient.connect(connectUrl, mongoSettings); - return db; + const client = await MongoClient.connect(connectUrl, mongoSettings); + return client; } /** * Initiates default db connection. - * @returns true + * @returns mongodb.MongoClient */ async function initiateDB() { if (!openDBConnection) openDBConnection = await connectMongoDb(); - return true; + return openDBConnection; } /** diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js new file mode 100644 index 0000000000..ed26cef2d3 --- /dev/null +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -0,0 +1,67 @@ +const path = require('node:path'); +const os = require('node:os'); +const { simpleGit, CleanOptions, ResetMode } = require('simple-git'); + +class FluxRepository { + defaultRepoDir = path.join(os.homedir(), 'zelflux'); + + constructor(options = {}) { + this.repoPath = options.repoDir || this.defaultRepoDir; + + const gitOptions = { + baseDir: this.repoPath, + binary: 'git', + maxConcurrentProcesses: 6, + trimmed: true, + }; + + this.git = simpleGit(gitOptions); + } + + async remotes() { + return this.git.getRemotes(true).catch(() => []); + } + + async currentBranch() { + const branches = await this.git.branch().catch(() => null); + if (!branches) return null; + + const { current, detached } = branches; + + return detached ? null : current; + } + + async switchBranch(branch, options = {}) { + const forceClean = options.forceClean || false; + const reset = options.reset ?? true; + const remote = options.remote || 'origin'; + + try { + if (forceClean) { + await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); + } + + if (reset) { + await this.git.reset(ResetMode.HARD); + } + + await this.git.fetch(remote, branch); + + await this.git.checkout(branch); + } catch { + return false; + } + return true; + } +} + +async function main() { + const repo = new FluxRepository(); + await repo.switchBranch('noexist'); +} + +if (require.main === module) { + main(); +} + +module.exports = { FluxRepository }; diff --git a/apiServer.js b/apiServer.js index bb9f267c7c..68750450ac 100644 --- a/apiServer.js +++ b/apiServer.js @@ -1,4 +1,8 @@ -global.userconfig = require('./config/userconfig'); +/** + * @import { MongoClient, Collection } from "mongodb" + */ + +globalThis.userconfig = require('./config/userconfig'); if (typeof AbortController === 'undefined') { // polyfill for nodeJS 14.18.1 - without having to use experimental features @@ -23,10 +27,12 @@ const fluxServer = require('./ZelBack/src/lib/fluxServer'); const log = require('./ZelBack/src/lib/log'); +const dbHelper = require('./ZelBack/src/services/dbHelper'); const serviceManager = require('./ZelBack/src/services/serviceManager'); const serviceHelper = require('./ZelBack/src/services/serviceHelper'); const upnpService = require('./ZelBack/src/services/upnpService'); const requestHistoryStore = require('./ZelBack/src/services/utils/requestHistory'); +const fluxRepository = require('./ZelBack/src/services/utils/fluxRepository'); const apiPort = userconfig.initial.apiport || config.server.apiport; const apiPortHttps = +apiPort + 1; @@ -40,6 +46,126 @@ let axiosDefaultsSet = false; */ let cacheable = null; +/** + * @returns {boolean} + */ +function isPreProdNode() { + const chance = Math.random(); + return chance <= config.preprodProbability; +} + +/** + * @param {Collection} col + * @returns {Promise} + */ +async function setPreProdNode(col) { + const preprodNode = isPreProdNode(); + + await col.updateOne( + { key: 'isPreProd' }, + { $set: { key: 'isPreProd', value: preprodNode } }, + { upsert: true }, + ); + + return preprodNode; +} + +/** + * @param {Collection} col + * @returns {Promise} + */ +async function getPreProdNode(col) { + const result = await col.findOne( + { key: 'isPreProd' }, + { projection: { value: 1 } }, + ); + + if (!result) return null; + + const { value: isPreprod, _id: id } = result; + + const timestamp = new Date(id.getTimestamp()); + + timestamp.setDate(timestamp.getDate() + 30); + + if (timestamp < new Date()) return null; + + return isPreprod; +} + +/** + * @param {MongoClient} client + * @returns {Promise} + */ +async function getPreProdState(client) { + console.log('getting preprod state'); + + const db = client.db('zelfluxlocal'); + const col = db.collection('state'); + col.createIndex({ key: 1 }, { unique: true }); + + const preprodNode = await getPreProdNode(col) ?? await setPreProdNode(col); + + return preprodNode; +} + +/** + * @param {MongoClient} client + * @param {string} repoDir + * @returns {Promise} + */ +async function setProductionBranch(client, repoDir) { + const { userconfig: { initial: { development } } } = userconfig; + const sleep = (ms) => new Promise((r) => { setTimeout(r, ms); }); + + if (development) return; + + const preprodNode = await getPreProdState(client); + + const { preprodBranchName, fluxRepoUrl } = config; + + const targetBranch = preprodNode ? preprodBranchName : 'master'; + + const repo = new fluxRepository.FluxRepository({ repoDir }); + const remotes = await repo.remotes(); + const branch = await repo.currentBranch(); + + const origin = remotes.find( + (r) => r.refs.fetch === fluxRepoUrl, + ); + + // if we don't find the origin, something is fishy. Maybe git:// scheme, maybe a + // different origin. Either way, we let it go and continue on whatever branch is set. + if (!origin) return; + + if (branch === targetBranch) return; + + await repo.switchBranch(targetBranch, { remote: origin.name, forceClean: true }); + + // nodemon should kill this process as we've changed files. + + await sleep(10_000); + + // We're still here. Maybe no backend files changed. Lets trigger a restart. + // We're just updating the file access / modified times - which nodemon sees + // as files changed. + + const time = new Date(); + const testFile = path.join(this.repoPath, 'ZelBack/config/default.js'); + await fs.utimes(testFile, time, time).catch(() => { }); + + await sleep(10_000); + + // Without knowing for sure what the supervisor is, this just feels too risky. + // We just let it go, and continue running on our current branch. + + // We're still here. Doesn't seem like nodemon is running. Lets just fork + // ourselves then. + + // fork(process.argv[1], { detached: true }).unref(); + // process.exit(); +} + function getrequestHistory() { return requestHistory; } @@ -227,6 +353,10 @@ async function initiate() { process.exit(1); }); + const dbClient = await dbHelper.initiateDB().catch(() => null); + + if (dbClient) await setProductionBranch(dbClient); + await createDnsCache(); await loadUpnpIfRequired(); From b789c965e3557bc14557a6d9aacd330d1bfa55f4 Mon Sep 17 00:00:00 2001 From: David White Date: Tue, 23 Jul 2024 19:39:20 +0100 Subject: [PATCH 03/17] Fix destructuring error --- apiServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiServer.js b/apiServer.js index 68750450ac..c96f107e27 100644 --- a/apiServer.js +++ b/apiServer.js @@ -115,7 +115,7 @@ async function getPreProdState(client) { * @returns {Promise} */ async function setProductionBranch(client, repoDir) { - const { userconfig: { initial: { development } } } = userconfig; + const { initial: { development } } = userconfig; const sleep = (ms) => new Promise((r) => { setTimeout(r, ms); }); if (development) return; From cb67eb509d9844aa2ac8bb9e4aecb3197080ddb0 Mon Sep 17 00:00:00 2001 From: David White Date: Tue, 23 Jul 2024 20:08:39 +0100 Subject: [PATCH 04/17] Group config vars --- ZelBack/config/default.js | 9 ++-- ZelBack/src/services/utils/fluxRepository.js | 2 +- apiServer.js | 48 +++++++++++--------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/ZelBack/config/default.js b/ZelBack/config/default.js index d5016467c8..63baaa3751 100644 --- a/ZelBack/config/default.js +++ b/ZelBack/config/default.js @@ -83,9 +83,12 @@ module.exports = { fluxTeamFluxID: '1hjy4bCYBJr4mny4zCE85J94RXa8W6q37', fluxSupportTeamFluxID: '16iJqiVbHptCx87q6XQwNpKdgEZnFtKcyP', deterministicNodesStart: 558000, - preprodProbability: 0.07, - fluxRepoUrl: 'https://github.com/RunOnFlux/flux.git', - preprodBranchName: 'preprod', + preProd: { + probability: 0.07, + remote: 'https://github.com/RunOnFlux/flux.git', + branch: 'preprod', + daysToNextEval: 30, + }, fluxapps: { // in flux main chain per month (blocksLasting) price: [ diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index ed26cef2d3..63883c87e5 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -33,7 +33,7 @@ class FluxRepository { async switchBranch(branch, options = {}) { const forceClean = options.forceClean || false; - const reset = options.reset ?? true; + const reset = options.reset || false; const remote = options.remote || 'origin'; try { diff --git a/apiServer.js b/apiServer.js index c96f107e27..cb3de95a46 100644 --- a/apiServer.js +++ b/apiServer.js @@ -51,7 +51,7 @@ let cacheable = null; */ function isPreProdNode() { const chance = Math.random(); - return chance <= config.preprodProbability; + return chance <= config.preProd.probability; } /** @@ -86,7 +86,7 @@ async function getPreProdNode(col) { const timestamp = new Date(id.getTimestamp()); - timestamp.setDate(timestamp.getDate() + 30); + timestamp.setDate(timestamp.getDate() + config.preProd.daysToNextEval); if (timestamp < new Date()) return null; @@ -98,8 +98,6 @@ async function getPreProdNode(col) { * @returns {Promise} */ async function getPreProdState(client) { - console.log('getting preprod state'); - const db = client.db('zelfluxlocal'); const col = db.collection('state'); col.createIndex({ key: 1 }, { unique: true }); @@ -115,49 +113,54 @@ async function getPreProdState(client) { * @returns {Promise} */ async function setProductionBranch(client, repoDir) { - const { initial: { development } } = userconfig; - const sleep = (ms) => new Promise((r) => { setTimeout(r, ms); }); + const { initial: { development, disablePreProd } } = userconfig; + // Develop nodes take priority over preProd nodes. + if (development || disablePreProd) return; - if (development) return; + const sleep = (ms) => new Promise((r) => { setTimeout(r, ms); }); const preprodNode = await getPreProdState(client); - const { preprodBranchName, fluxRepoUrl } = config; + const { preProd: { branch, remote } } = config; - const targetBranch = preprodNode ? preprodBranchName : 'master'; + const targetBranch = preprodNode ? branch : 'master'; const repo = new fluxRepository.FluxRepository({ repoDir }); const remotes = await repo.remotes(); - const branch = await repo.currentBranch(); + const currentBranch = await repo.currentBranch(); const origin = remotes.find( - (r) => r.refs.fetch === fluxRepoUrl, + (r) => r.refs.fetch === remote, ); // if we don't find the origin, something is fishy. Maybe git:// scheme, maybe a // different origin. Either way, we let it go and continue on whatever branch is set. if (!origin) return; - if (branch === targetBranch) return; + if (currentBranch === targetBranch) return; - await repo.switchBranch(targetBranch, { remote: origin.name, forceClean: true }); + await repo.switchBranch(targetBranch, { + remote: origin.name, + forceClean: true, + reset: true, + }); - // nodemon should kill this process as we've changed files. + // nodemon should kill this process within 5 seconds as we've changed files. await sleep(10_000); - // We're still here. Maybe no backend files changed. Lets trigger a restart. - // We're just updating the file access / modified times - which nodemon sees - // as files changed. + // We're still here. Maybe no backend files changed with the branch switch. + // Lets trigger a restart. We're just updating the file access / modified + // times - which nodemon sees as files changed. const time = new Date(); - const testFile = path.join(this.repoPath, 'ZelBack/config/default.js'); + const testFile = path.join(repoDir, 'ZelBack/config/default.js'); await fs.utimes(testFile, time, time).catch(() => { }); await sleep(10_000); - // Without knowing for sure what the supervisor is, this just feels too risky. - // We just let it go, and continue running on our current branch. + // Without knowing for sure what the supervisor is, forking the current process + // just feels too risky. We just let it go, and continue running on our current branch. // We're still here. Doesn't seem like nodemon is running. Lets just fork // ourselves then. @@ -353,9 +356,11 @@ async function initiate() { process.exit(1); }); + const appRoot = process.cwd(); + const dbClient = await dbHelper.initiateDB().catch(() => null); - if (dbClient) await setProductionBranch(dbClient); + if (dbClient) await setProductionBranch(dbClient, appRoot); await createDnsCache(); @@ -365,7 +370,6 @@ async function initiate() { configReload(); }, 2 * 1000); - const appRoot = process.cwd(); // ToDo: move this to async const certExists = fs.existsSync(path.join(appRoot, 'certs/v1.key')); From e1b41f39d131511f50b396a086ba539c3545e763 Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 07:17:35 +0100 Subject: [PATCH 05/17] Update docstrings --- apiServer.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apiServer.js b/apiServer.js index cb3de95a46..c0a73e2df5 100644 --- a/apiServer.js +++ b/apiServer.js @@ -47,6 +47,7 @@ let axiosDefaultsSet = false; let cacheable = null; /** + * A fairly random way of determining if a node is on the preprod branch or master * @returns {boolean} */ function isPreProdNode() { @@ -55,6 +56,8 @@ function isPreProdNode() { } /** + * Throws the dice to see if this node is a preprod node, and stores it in the + * local mongo state database * @param {Collection} col * @returns {Promise} */ @@ -71,6 +74,8 @@ async function setPreProdNode(col) { } /** + * Checks the local mongo state database to see if this node has thrown the dice + * to see if it is a preprod node within the last daysToNextEval time period. * @param {Collection} col * @returns {Promise} */ @@ -94,6 +99,7 @@ async function getPreProdNode(col) { } /** + * Determines if this is a preprod node or production node. * @param {MongoClient} client * @returns {Promise} */ @@ -108,6 +114,9 @@ async function getPreProdState(client) { } /** + * Chooses either preprod or production branches. Except if the node is on deveop, + * then nothing happens. If the branch is changed, fluxOS is restarted by Nodemon, + * if it is running. * @param {MongoClient} client * @param {string} repoDir * @returns {Promise} From ae0809b169520e4b3ba35846e5e4160cfcf2d32d Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 07:23:02 +0100 Subject: [PATCH 06/17] Update some dbHelper typing --- ZelBack/src/services/dbHelper.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ZelBack/src/services/dbHelper.js b/ZelBack/src/services/dbHelper.js index 4fa9086af6..f5c3dd03f0 100644 --- a/ZelBack/src/services/dbHelper.js +++ b/ZelBack/src/services/dbHelper.js @@ -2,6 +2,10 @@ * @module Helper module used for all interactions with database */ +/** + * @import { MongoClient } from "mongodb" + */ + const mongodb = require('mongodb'); const config = require('config'); @@ -23,7 +27,7 @@ function databaseConnection() { * * @param {string} [url] * - * @returns {object} MongoClient + * @returns {MongoClient} */ async function connectMongoDb(url) { const connectUrl = url || mongoUrl; @@ -38,7 +42,7 @@ async function connectMongoDb(url) { /** * Initiates default db connection. - * @returns mongodb.MongoClient + * @returns {MongoClient} */ async function initiateDB() { if (!openDBConnection) openDBConnection = await connectMongoDb(); @@ -47,6 +51,7 @@ async function initiateDB() { /** * Closes DB connection if exists. + * @returns {Promise} */ async function closeDbConnection() { if (openDBConnection) { @@ -63,7 +68,7 @@ async function closeDbConnection() { * @param {string} distinct - field name * @param {object} [query] * - * @returns array + * @returns {Proimise} */ async function distinctDatabase(database, collection, distinct, query) { const results = await database.collection(collection).distinct(distinct, query); @@ -78,7 +83,7 @@ async function distinctDatabase(database, collection, distinct, query) { * @param {object} query * @param {object} [projection] * - * @returns array + * @returns {Promise} */ async function findInDatabase(database, collection, query, projection) { const results = await database.collection(collection).find(query, projection).toArray(); From 24719c216405c57f156359a48c3f540c3976b093 Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 08:53:57 +0100 Subject: [PATCH 07/17] Add logging --- apiServer.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/apiServer.js b/apiServer.js index c0a73e2df5..29f8a540be 100644 --- a/apiServer.js +++ b/apiServer.js @@ -113,6 +113,21 @@ async function getPreProdState(client) { return preprodNode; } +/** + * Determines if this is a preprod node or production node. + * @param {MongoClient} client + * @returns {Promise} + */ +async function getPreProdState(client) { + const db = client.db('zelfluxlocal'); + const col = db.collection('state'); + col.createIndex({ key: 1 }, { unique: true }); + + const preprodNode = await getPreProdNode(col) ?? await setPreProdNode(col); + + return preprodNode; +} + /** * Chooses either preprod or production branches. Except if the node is on deveop, * then nothing happens. If the branch is changed, fluxOS is restarted by Nodemon, @@ -130,6 +145,9 @@ async function setProductionBranch(client, repoDir) { const preprodNode = await getPreProdState(client); + const logText = preprodNode ? 'pre-production' : 'production'; + log.info(`Fluxnode running in ${logText} mode`); + const { preProd: { branch, remote } } = config; const targetBranch = preprodNode ? branch : 'master'; @@ -138,16 +156,23 @@ async function setProductionBranch(client, repoDir) { const remotes = await repo.remotes(); const currentBranch = await repo.currentBranch(); + log.info(`Fluxnode on branch: ${currentBranch}`); + const origin = remotes.find( (r) => r.refs.fetch === remote, ); // if we don't find the origin, something is fishy. Maybe git:// scheme, maybe a // different origin. Either way, we let it go and continue on whatever branch is set. - if (!origin) return; + if (!origin) { + log.warn(`Unable to find remote ref: ${remote} in remotes... skipping preprod setup`); + return; + } if (currentBranch === targetBranch) return; + log.info(`Switching from branch: ${currentBranch} to: ${targetBranch}`); + await repo.switchBranch(targetBranch, { remote: origin.name, forceClean: true, From a9c180bb22ee7f75800ef1d0d7bc70021be9dc70 Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 09:16:31 +0100 Subject: [PATCH 08/17] log switch branch error --- ZelBack/src/services/utils/fluxRepository.js | 21 ++++++++------------ apiServer.js | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index 63883c87e5..b63ab09072 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -36,22 +36,17 @@ class FluxRepository { const reset = options.reset || false; const remote = options.remote || 'origin'; - try { - if (forceClean) { - await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); - } + if (forceClean) { + await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); + } - if (reset) { - await this.git.reset(ResetMode.HARD); - } + if (reset) { + await this.git.reset(ResetMode.HARD); + } - await this.git.fetch(remote, branch); + await this.git.fetch(remote, branch); - await this.git.checkout(branch); - } catch { - return false; - } - return true; + await this.git.checkout(branch); } } diff --git a/apiServer.js b/apiServer.js index 29f8a540be..22b6941c4b 100644 --- a/apiServer.js +++ b/apiServer.js @@ -177,7 +177,7 @@ async function setProductionBranch(client, repoDir) { remote: origin.name, forceClean: true, reset: true, - }); + }).catch((err) => log.info(err)); // nodemon should kill this process within 5 seconds as we've changed files. From 52cfe26eb5cf9e3c30b422942d8db8a08b013890 Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 09:33:10 +0100 Subject: [PATCH 09/17] Remove duplicate function --- apiServer.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/apiServer.js b/apiServer.js index 22b6941c4b..66c90db71a 100644 --- a/apiServer.js +++ b/apiServer.js @@ -113,21 +113,6 @@ async function getPreProdState(client) { return preprodNode; } -/** - * Determines if this is a preprod node or production node. - * @param {MongoClient} client - * @returns {Promise} - */ -async function getPreProdState(client) { - const db = client.db('zelfluxlocal'); - const col = db.collection('state'); - col.createIndex({ key: 1 }, { unique: true }); - - const preprodNode = await getPreProdNode(col) ?? await setPreProdNode(col); - - return preprodNode; -} - /** * Chooses either preprod or production branches. Except if the node is on deveop, * then nothing happens. If the branch is changed, fluxOS is restarted by Nodemon, From 7d13bb2c3571c5322f1f8b11197234f1b1ec39bd Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 10:13:24 +0100 Subject: [PATCH 10/17] Add preprod info to flux/info --- ZelBack/src/services/fluxService.js | 33 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/ZelBack/src/services/fluxService.js b/ZelBack/src/services/fluxService.js index 73680b467a..3190c3a78f 100644 --- a/ZelBack/src/services/fluxService.js +++ b/ZelBack/src/services/fluxService.js @@ -24,6 +24,7 @@ const fluxNetworkHelper = require('./fluxNetworkHelper'); const geolocationService = require('./geolocationService'); const syncthingService = require('./syncthingService'); const dockerService = require('./dockerService'); +const fluxRepository = require('./utils/fluxRepository'); // for streamChain endpoint const zlib = require('node:zlib'); @@ -32,6 +33,8 @@ const tar = require('tar-fs'); // const stream = require('node:stream/promises'); const stream = require('node:stream'); +const fluxRepo = new fluxRepository.FluxRepository({ repoDir: process.cwd() }); + /** * Stream chain lock, so only one request at a time */ @@ -103,7 +106,6 @@ async function getCurrentCommitId(req, res) { * @returns {Promise} Message. */ async function getCurrentBranch(req, res) { - // ToDo: Fix - this breaks if head in detached state (or something similar) if (req) { const authorized = await verificationHelper.verifyPrivilege('adminandfluxteam', req); if (authorized !== true) { @@ -112,23 +114,23 @@ async function getCurrentBranch(req, res) { } } - const { stdout: commitId, error } = await serviceHelper.runCommand('git', { - logError: false, params: ['rev-parse', '--abbrev-ref', 'HEAD'], - }); - - if (error) { - const errMsg = messageHelper.createErrorMessage( - `Error getting current branch of Flux: ${error.message}`, - error.name, - error.code, - ); - return res ? res.json(errMsg) : errMsg; - } + // null branch is detached HEAD, or error + const branch = await fluxRepo.currentBranch(); - const successMsg = messageHelper.createSuccessMessage(commitId.trim()); + const successMsg = messageHelper.createSuccessMessage(branch); return res ? res.json(successMsg) : successMsg; } +/** + * If this node is on the preprod branch + * @returns {Promise} + */ +async function isPreProdNode() { + const currentBranch = await getCurrentBranch(); + const { preProd: { branch: preProdBranch } } = config; + return currentBranch === preProdBranch; +} + /** * Check out branch if it exists locally * @param {string} branch The branch to checkout @@ -1076,6 +1078,8 @@ async function getFluxInfo(req, res) { if (nodeJsVersionsRes.status === 'error') { throw nodeJsVersionsRes.data; } + const preProdNode = await isPreProdNode(); + info.flux.preProdNode = preProdNode; info.flux.nodeJsVersion = nodeJsVersionsRes.data.node; const syncthingVersion = await syncthingService.systemVersion(); if (syncthingVersion.status === 'error') { @@ -1741,6 +1745,7 @@ module.exports = { getRouterIP, hardUpdateFlux, installFluxWatchTower, + isPreProdNode, isStaticIPapi, lockStreamLock, rebuildHome, From 23b985662c8288bd0078f108dc9114da66efb43b Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 10:41:58 +0100 Subject: [PATCH 11/17] Get the repo directly --- ZelBack/src/services/fluxService.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ZelBack/src/services/fluxService.js b/ZelBack/src/services/fluxService.js index 3190c3a78f..7723fd48f6 100644 --- a/ZelBack/src/services/fluxService.js +++ b/ZelBack/src/services/fluxService.js @@ -126,8 +126,9 @@ async function getCurrentBranch(req, res) { * @returns {Promise} */ async function isPreProdNode() { - const currentBranch = await getCurrentBranch(); + const currentBranch = await fluxRepo.currentBranch(); const { preProd: { branch: preProdBranch } } = config; + return currentBranch === preProdBranch; } @@ -1078,8 +1079,6 @@ async function getFluxInfo(req, res) { if (nodeJsVersionsRes.status === 'error') { throw nodeJsVersionsRes.data; } - const preProdNode = await isPreProdNode(); - info.flux.preProdNode = preProdNode; info.flux.nodeJsVersion = nodeJsVersionsRes.data.node; const syncthingVersion = await syncthingService.systemVersion(); if (syncthingVersion.status === 'error') { @@ -1094,6 +1093,8 @@ async function getFluxInfo(req, res) { } info.flux.ip = ipRes.data; info.flux.staticIp = geolocationService.isStaticIP(); + const preProdNode = await isPreProdNode(); + info.flux.preProdNode = preProdNode; info.flux.maxNumberOfIpChanges = fluxNetworkHelper.getMaxNumberOfIpChanges(); const zelidRes = await getFluxZelID(); if (zelidRes.status === 'error') { From 3cbe30b71f8414a3f7cdf70414884d3c3aa24e4d Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 13:22:11 +0100 Subject: [PATCH 12/17] Move fetch --- ZelBack/src/services/utils/fluxRepository.js | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index b63ab09072..af1042aac3 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -36,6 +36,9 @@ class FluxRepository { const reset = options.reset || false; const remote = options.remote || 'origin'; + // fetch first incase there are errors. + await this.git.fetch(remote, branch); + if (forceClean) { await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); } @@ -44,19 +47,19 @@ class FluxRepository { await this.git.reset(ResetMode.HARD); } - await this.git.fetch(remote, branch); - await this.git.checkout(branch); - } -} -async function main() { - const repo = new FluxRepository(); - await repo.switchBranch('noexist'); -} + // we clean / reset again as we don't know the state of the local branch + if (forceClean) { + await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); + } -if (require.main === module) { - main(); + if (reset) { + await this.git.reset(ResetMode.HARD); + } + + await this.git.merge(['--ff-only']); + } } module.exports = { FluxRepository }; From 140c57ee3442dae62dde62d831858d36781f3811 Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 13:49:04 +0100 Subject: [PATCH 13/17] Handle same branch on multiple remotes --- ZelBack/src/services/utils/fluxRepository.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index af1042aac3..81d3da6275 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -47,18 +47,15 @@ class FluxRepository { await this.git.reset(ResetMode.HARD); } - await this.git.checkout(branch); + const exists = this.git.revparse(['--verify', branch]).catch(() => false); - // we clean / reset again as we don't know the state of the local branch - if (forceClean) { - await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); - } - - if (reset) { - await this.git.reset(ResetMode.HARD); + if (exists) { + await this.git.checkout(branch); + await this.git.merge(['--ff-only']); + return; } - await this.git.merge(['--ff-only']); + await this.git.checkout(['--track', `${remote}/${branch}`]); } } From 3227ec7d5bf3197ae21b04a214d7fcaf2b54e506 Mon Sep 17 00:00:00 2001 From: David White Date: Wed, 24 Jul 2024 13:55:19 +0100 Subject: [PATCH 14/17] Add missing await --- ZelBack/src/services/utils/fluxRepository.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index 81d3da6275..b58cc80f45 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -47,7 +47,7 @@ class FluxRepository { await this.git.reset(ResetMode.HARD); } - const exists = this.git.revparse(['--verify', branch]).catch(() => false); + const exists = await this.git.revparse(['--verify', branch]).catch(() => false); if (exists) { await this.git.checkout(branch); From 84d8267b142a2e749c905b109840fca671da277e Mon Sep 17 00:00:00 2001 From: David White Date: Thu, 25 Jul 2024 10:20:04 +0100 Subject: [PATCH 15/17] Add unit and integration tests (using real test repo) --- ZelBack/src/services/utils/fluxRepository.js | 23 +- tests/ZelBack/repoTests.js | 151 +++++++++++ tests/unit/fluxRepository.test.js | 251 +++++++++++++++++++ 3 files changed, 421 insertions(+), 4 deletions(-) create mode 100644 tests/ZelBack/repoTests.js create mode 100644 tests/unit/fluxRepository.test.js diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index b58cc80f45..1eab243135 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -1,8 +1,9 @@ const path = require('node:path'); const os = require('node:os'); -const { simpleGit, CleanOptions, ResetMode } = require('simple-git'); +const sg = require('simple-git'); class FluxRepository { + // this may not exist defaultRepoDir = path.join(os.homedir(), 'zelflux'); constructor(options = {}) { @@ -15,13 +16,22 @@ class FluxRepository { trimmed: true, }; - this.git = simpleGit(gitOptions); + this.git = sg.simpleGit(gitOptions); } async remotes() { return this.git.getRemotes(true).catch(() => []); } + async addRemote(name, url) { + await this.git.addRemote(name, url).catch(() => { }); + } + + async currentCommitId() { + const id = await this.git.revparse('HEAD').catch(() => null); + return id; + } + async currentBranch() { const branches = await this.git.branch().catch(() => null); if (!branches) return null; @@ -31,6 +41,10 @@ class FluxRepository { return detached ? null : current; } + async resetToCommitId(id) { + await this.git.reset(sg.ResetMode.HARD, [id]).catch(() => { }); + } + async switchBranch(branch, options = {}) { const forceClean = options.forceClean || false; const reset = options.reset || false; @@ -40,17 +54,18 @@ class FluxRepository { await this.git.fetch(remote, branch); if (forceClean) { - await this.git.clean(CleanOptions.FORCE + CleanOptions.RECURSIVE); + await this.git.clean(sg.CleanOptions.FORCE + sg.CleanOptions.RECURSIVE); } if (reset) { - await this.git.reset(ResetMode.HARD); + await this.git.reset(sg.ResetMode.HARD); } const exists = await this.git.revparse(['--verify', branch]).catch(() => false); if (exists) { await this.git.checkout(branch); + // don't think we need to reset here await this.git.merge(['--ff-only']); return; } diff --git a/tests/ZelBack/repoTests.js b/tests/ZelBack/repoTests.js new file mode 100644 index 0000000000..45f3d1353a --- /dev/null +++ b/tests/ZelBack/repoTests.js @@ -0,0 +1,151 @@ +const chai = require('chai'); +chai.use(require('chai-as-promised')); + +const { expect } = chai; + +const os = require('node:os'); +const fs = require('node:fs/promises'); +const path = require('node:path'); + +const { simpleGit } = require('simple-git'); + +const { FluxRepository } = require('../../ZelBack/src/services/utils/fluxRepository'); + +describe('Flux preprod branch tests', () => { + const repoName = 'flux-integration'; + const testRepoName = `${repoName}-test`; + + let testDir; + let repoDir; + let testRepoDir; + + before(async () => { + testDir = os.tmpdir(); + repoDir = path.join(testDir, repoName); + testRepoDir = path.join(testDir, testRepoName); + + await fs.mkdir(repoDir); + + console.log('Cloning test repository'); + const git = simpleGit(); + await git.clone('https://github.com/RunOnFlux/flux-integration.git', repoDir); + }); + + beforeEach(async () => { + await fs.cp(repoDir, testRepoDir, { recursive: true }); + }); + + afterEach(async () => { + await fs.rm(testRepoDir, { recursive: true, force: true }); + }); + + after(async () => { + console.log('Deleting test repository'); + await fs.rm(repoDir, { recursive: true, force: true }); + }); + + it('should return the correct branch as current branch', async () => { + const repo = new FluxRepository({ repoDir: testRepoDir }); + const branch = await repo.currentBranch(); + expect(branch).to.equal('master'); + }); + + it('should list remote branches', async () => { + const repo = new FluxRepository({ repoDir: testRepoDir }); + const expected = [ + { + name: 'origin', + refs: { + fetch: 'https://github.com/RunOnFlux/flux-integration.git', + push: 'https://github.com/RunOnFlux/flux-integration.git', + }, + }, + ]; + const remotes = await repo.remotes(); + expect(remotes).to.deep.equal(expected); + }); + + it('should switch branches if local is up to date with remote', async () => { + const repo = new FluxRepository({ repoDir: testRepoDir }); + await expect(repo.switchBranch('preprod')).to.be.fulfilled; + const branch = await repo.currentBranch(); + expect(branch).to.equal('preprod'); + }); + + it('should switch branches if local has untracked work', async () => { + const testFile = path.join(testRepoDir, 'untracked-file'); + await fs.writeFile(testFile, 'test content'); + + const repo = new FluxRepository({ repoDir: testRepoDir }); + await expect(repo.switchBranch('preprod')).to.be.fulfilled; + const branch = await repo.currentBranch(); + expect(branch).to.equal('preprod'); + + const fileExists = Boolean(await fs.stat(testFile).catch(() => false)); + expect(fileExists).to.equal(true); + }); + + it('should switch branches and remove untracked if local has untracked work', async () => { + const testFile = path.join(testRepoDir, 'untracked-file'); + await fs.writeFile(testFile, 'test content'); + + const repo = new FluxRepository({ repoDir: testRepoDir }); + await expect(repo.switchBranch('preprod', { forceClean: true })).to.be.fulfilled; + const branch = await repo.currentBranch(); + expect(branch).to.equal('preprod'); + + const fileExists = Boolean(await fs.stat(testFile).catch(() => false)); + expect(fileExists).to.equal(false); + }); + + it('should reset current branch and allow switch where local work would be overwritten', async () => { + const testFile = path.join(testRepoDir, 'README.md'); + await fs.writeFile(testFile, 'this file has been modified and would be overwritten on switch'); + + const repo = new FluxRepository({ repoDir: testRepoDir }); + await expect(repo.switchBranch('preprod', { reset: true })).to.be.fulfilled; + const branch = await repo.currentBranch(); + expect(branch).to.equal('preprod'); + }); + + it('should sync local branch to remote if switching to existing branch', async () => { + // setup + const priorCommit = '815f77059ce7f968d259af1333b04f2f6d2cab6f'; + const repo = new FluxRepository({ repoDir: testRepoDir }); + await repo.switchBranch('preprod'); + const latestCommit = await repo.currentCommitId(); + await repo.resetToCommitId(priorCommit); + const testCommit = await repo.currentCommitId(); + + // test + expect(testCommit).to.equal(priorCommit); + await repo.switchBranch('master'); + + await repo.switchBranch('preprod'); + const currentCommit = await repo.currentCommitId(); + + expect(currentCommit).to.equal(latestCommit); + }); + + it('should switch to correct branch if multiple remotes', async () => { + // setup + const repoUrl = 'https://github.com/RunOnFlux/flux-integration.git'; + const forkUrl = 'https://github.com/RunOnFlux/flux-integration-fork.git'; + const latestCommit = 'fb4f9097d8b0bc19d0fb901238a4643e72b69398'; + + const repo = new FluxRepository({ repoDir: testRepoDir }); + await repo.addRemote('test_remote', forkUrl); + + // test + const remotes = await repo.remotes(); + const remote = remotes.find( + (r) => r.refs.fetch === repoUrl, + ); + + await repo.switchBranch('preprod', { remote: remote.name }); + + const currentCommit = await repo.currentCommitId(); + + expect(currentCommit).to.equal(latestCommit); + }); +}); diff --git a/tests/unit/fluxRepository.test.js b/tests/unit/fluxRepository.test.js new file mode 100644 index 0000000000..211d845cd8 --- /dev/null +++ b/tests/unit/fluxRepository.test.js @@ -0,0 +1,251 @@ +const chai = require('chai'); +chai.use(require('chai-as-promised')); + +const { expect } = chai; + +const sinon = require('sinon'); + +const os = require('node:os'); +const sg = require('simple-git'); + +const { FluxRepository } = require('../../ZelBack/src/services/utils/fluxRepository'); + +describe('fluxRepository tests', () => { + let getRemotesStub; + let addRemoteStub; + let branchStub; + let fetchStub; + let cleanStub; + let resetStub; + let revparseStub; + let checkoutStub; + let mergeStub; + let gitStub; + beforeEach(async () => { + getRemotesStub = sinon.stub(); + addRemoteStub = sinon.stub(); + branchStub = sinon.stub(); + fetchStub = sinon.stub(); + cleanStub = sinon.stub(); + resetStub = sinon.stub(); + revparseStub = sinon.stub(); + checkoutStub = sinon.stub(); + mergeStub = sinon.stub(); + + gitStub = sinon.stub(sg, 'simpleGit').returns({ + getRemotes: getRemotesStub, + addRemote: addRemoteStub, + branch: branchStub, + fetch: fetchStub, + clean: cleanStub, + reset: resetStub, + revparse: revparseStub, + checkout: checkoutStub, + merge: mergeStub, + }); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should instantiate and create a git instance with correct properties', () => { + const testOptions = { + baseDir: '/testdir', + binary: 'git', + maxConcurrentProcesses: 6, + trimmed: true, + }; + + expect(() => new FluxRepository({ repoDir: '/testdir' })).to.not.throw(); + + sinon.assert.calledOnceWithExactly(gitStub, testOptions); + }); + + it('should instantiate and use default zelflux dir as baseDir', () => { + sinon.stub(os, 'homedir').returns('/home/testfluxdir'); + + const testOptions = { + baseDir: '/home/testfluxdir/zelflux', + binary: 'git', + maxConcurrentProcesses: 6, + trimmed: true, + }; + + expect(() => new FluxRepository()).to.not.throw(); + + sinon.assert.calledOnceWithExactly(gitStub, testOptions); + }); + + it('should get git remotes in verbose mode', async () => { + const expected = [ + { + name: 'origin', + refs: { + fetch: 'https://github.com/RunOnFlux/flux.git', + push: 'https://github.com/RunOnFlux/flux.git', + }, + }, + ]; + + getRemotesStub.resolves(expected); + + const repo = new FluxRepository({ repoDir: '/test' }); + const remotes = await repo.remotes(); + + sinon.assert.calledOnceWithExactly(getRemotesStub, true); + expect(remotes).to.deep.equal(expected); + }); + + it('should call the underlying addRemote with name and url', async () => { + addRemoteStub.resolves(); + + const repo = new FluxRepository({ repoDir: '/test' }); + await repo.addRemote('test_remote', 'https://blah.com'); + + sinon.assert.calledOnceWithExactly(addRemoteStub, 'test_remote', 'https://blah.com'); + }); + + it('should return the commit id of HEAD on the current branch', async () => { + revparseStub.resolves('12345'); + + const repo = new FluxRepository({ repoDir: '/test' }); + const id = await repo.currentCommitId(); + + sinon.assert.calledOnceWithExactly(revparseStub, 'HEAD'); + expect(id).to.equal('12345'); + }); + + it('should return the current branch', async () => { + branchStub.resolves({ + all: [ + 'master', + 'preprod', + 'remotes/origin/master', + 'remotes/origin/preprod', + ], + branches: { + master: { + current: false, + linkedWorkTree: false, + name: 'master', + commit: '17f91ea', + label: 'Check in new file (ahead of fork)', + }, + preprod: { + current: true, + linkedWorkTree: false, + name: 'preprod', + commit: 'fb4f909', + label: 'Update test_file', + }, + 'remotes/origin/master': { + current: false, + linkedWorkTree: false, + name: 'remotes/origin/master', + commit: '17f91ea', + label: 'Check in new file (ahead of fork)', + }, + 'remotes/origin/preprod': { + current: false, + linkedWorkTree: false, + name: 'remotes/origin/preprod', + commit: 'fb4f909', + label: 'Update test_file', + }, + }, + current: 'preprod', + detached: false, + }); + + const repo = new FluxRepository({ repoDir: '/test' }); + const branch = await repo.currentBranch(); + + sinon.assert.calledOnce(branchStub); + expect(branch).to.equal('preprod'); + }); + + it('should call underlying git reset when resetToId called', async () => { + resetStub.resolves(); + + const expected = [sg.ResetMode.HARD, ['12345']]; + + const repo = new FluxRepository({ repoDir: '/test' }); + await repo.resetToCommitId('12345'); + + sinon.assert.calledWithExactly(resetStub, ...expected); + }); + + it('should track new remote when switchBranch called and it doesn\'t exist locally', async () => { + fetchStub.resolves(); + // local branch doesn't exist + revparseStub.rejects(); + checkoutStub.resolves(); + + const repo = new FluxRepository({ repoDir: '/test' }); + await repo.switchBranch('test_branch'); + + sinon.assert.notCalled(cleanStub); + sinon.assert.notCalled(resetStub); + sinon.assert.calledOnce(fetchStub); + sinon.assert.calledOnce(revparseStub); + sinon.assert.notCalled(mergeStub); + sinon.assert.calledOnceWithExactly(checkoutStub, ['--track', 'origin/test_branch']); + }); + + it('should fetch, then switch to existing branch and fast-forward when switchBranch called and branch exists locally', async () => { + fetchStub.resolves(); + // local branch does exist + revparseStub.resolves('latest commit id here'); + checkoutStub.resolves(); + mergeStub.resolves(); + + const repo = new FluxRepository({ repoDir: '/test' }); + await repo.switchBranch('test_branch'); + + sinon.assert.notCalled(cleanStub); + sinon.assert.notCalled(resetStub); + sinon.assert.calledOnce(fetchStub); + sinon.assert.calledOnce(revparseStub); + sinon.assert.calledOnce(mergeStub); + sinon.assert.calledOnceWithExactly(checkoutStub, 'test_branch'); + }); + + it('should force clean local branch if clean requested on switchBranch', async () => { + fetchStub.resolves(); + // local branch does exist + revparseStub.resolves('latest commit id here'); + checkoutStub.resolves(); + mergeStub.resolves(); + cleanStub.resolves(); + + const repo = new FluxRepository({ repoDir: '/test' }); + await repo.switchBranch('test_branch', { forceClean: true }); + + sinon.assert.calledOnce(fetchStub); + sinon.assert.calledOnce(cleanStub); + sinon.assert.notCalled(resetStub); + sinon.assert.calledOnce(revparseStub); + sinon.assert.calledOnce(mergeStub); + sinon.assert.calledOnceWithExactly(checkoutStub, 'test_branch'); + }); + + it('should reset local branch if reset requested on switchBranch', async () => { + fetchStub.resolves(); + // local branch does exist + revparseStub.resolves('latest commit id here'); + checkoutStub.resolves(); + mergeStub.resolves(); + resetStub.resolves(); + + const repo = new FluxRepository({ repoDir: '/test' }); + await repo.switchBranch('test_branch', { reset: true }); + + sinon.assert.calledOnce(fetchStub); + sinon.assert.notCalled(cleanStub); + sinon.assert.calledOnce(resetStub); + sinon.assert.calledOnce(revparseStub); + sinon.assert.calledOnce(mergeStub); + sinon.assert.calledOnceWithExactly(checkoutStub, 'test_branch'); + }); +}); From 376b272dbf943d6e4669cf5aaf2916ce854e2706 Mon Sep 17 00:00:00 2001 From: David White Date: Thu, 25 Jul 2024 11:50:17 +0100 Subject: [PATCH 16/17] Fix fluxService tests --- ZelBack/src/services/fluxService.js | 1 + tests/unit/fluxService.test.js | 11 ++++++++++- tests/unit/globalconfig/default.js | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/ZelBack/src/services/fluxService.js b/ZelBack/src/services/fluxService.js index 7723fd48f6..4bcadf7b61 100644 --- a/ZelBack/src/services/fluxService.js +++ b/ZelBack/src/services/fluxService.js @@ -782,6 +782,7 @@ async function tailDaemonDebug(req, res) { } const defaultDir = daemonServiceUtils.getFluxdDir(); + const datadir = daemonServiceUtils.getConfigValue('datadir') || defaultDir; const filepath = path.join(datadir, 'debug.log'); diff --git a/tests/unit/fluxService.test.js b/tests/unit/fluxService.test.js index 8de9195329..3722b29520 100644 --- a/tests/unit/fluxService.test.js +++ b/tests/unit/fluxService.test.js @@ -27,14 +27,19 @@ const daemonServiceControlRpcs = require('../../ZelBack/src/services/daemonServi const daemonServiceBenchmarkRpcs = require('../../ZelBack/src/services/daemonService/daemonServiceBenchmarkRpcs'); const daemonServiceFluxnodeRpcs = require('../../ZelBack/src/services/daemonService/daemonServiceFluxnodeRpcs'); const daemonServiceBlockchainRpcs = require('../../ZelBack/src/services/daemonService/daemonServiceBlockchainRpcs'); +const daemonServiceUtils = require('../../ZelBack/src/services/daemonService/daemonServiceUtils'); const serviceHelper = require('../../ZelBack/src/services/serviceHelper'); const syncthingService = require('../../ZelBack/src/services/syncthingService'); const packageJson = require('../../package.json'); const adminConfig = require('../../config/userconfig'); +const FluxRepository = function TestClass() { + this.currentBranch = sinon.stub().resolves('master'); +}; + const fluxService = proxyquire( '../../ZelBack/src/services/fluxService', - { '../../../config/userconfig': adminConfig }, + { '../../../config/userconfig': adminConfig, './utils/fluxRepository': { FluxRepository } }, ); const generateResponse = () => { @@ -47,6 +52,10 @@ const generateResponse = () => { }; describe('fluxService tests', () => { + before(async () => { + await daemonServiceUtils.readDaemonConfig(); + }); + describe('fluxBackendFolder tests', () => { afterEach(() => { sinon.restore(); diff --git a/tests/unit/globalconfig/default.js b/tests/unit/globalconfig/default.js index 5661565112..456baf5156 100644 --- a/tests/unit/globalconfig/default.js +++ b/tests/unit/globalconfig/default.js @@ -77,6 +77,12 @@ module.exports = { minimumDockerAllowedVersion: '26.1.2', fluxTeamFluxID: '1NH9BP155Rp3HSf5ef6NpUbE8JcyLRruAM', deterministicNodesStart: 558000, + preProd: { + probability: 0.07, + remote: 'https://github.com/RunOnFlux/flux.git', + branch: 'preprod', + daysToNextEval: 30, + }, fluxapps: { // in flux main chain per month (blocksLasting) price: [ From 39f9dd60d3379a19bbdf2bb6b6d43f277647bf85 Mon Sep 17 00:00:00 2001 From: David White Date: Thu, 25 Jul 2024 12:01:35 +0100 Subject: [PATCH 17/17] Hard reset to remote branch --- ZelBack/src/services/utils/fluxRepository.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ZelBack/src/services/utils/fluxRepository.js b/ZelBack/src/services/utils/fluxRepository.js index 1eab243135..250af6c07e 100644 --- a/ZelBack/src/services/utils/fluxRepository.js +++ b/ZelBack/src/services/utils/fluxRepository.js @@ -49,6 +49,7 @@ class FluxRepository { const forceClean = options.forceClean || false; const reset = options.reset || false; const remote = options.remote || 'origin'; + const remoteBranch = `${remote}/${branch}`; // fetch first incase there are errors. await this.git.fetch(remote, branch); @@ -58,7 +59,7 @@ class FluxRepository { } if (reset) { - await this.git.reset(sg.ResetMode.HARD); + await this.git.reset(sg.ResetMode.HARD, [remoteBranch]); } const exists = await this.git.revparse(['--verify', branch]).catch(() => false); @@ -70,7 +71,7 @@ class FluxRepository { return; } - await this.git.checkout(['--track', `${remote}/${branch}`]); + await this.git.checkout(['--track', remoteBranch]); } }