From 31545acdaa0269a6b19d6b94947f5e3df2917fc4 Mon Sep 17 00:00:00 2001 From: Benj Boyer Date: Wed, 15 Apr 2020 12:45:27 -0400 Subject: [PATCH] * Implemented the pull part of #56 * Fixed Review.merge() since buffers were not closed * Enhanced toasts in Composer --- .../io/peasoup/inv/cli/InitCommand.groovy | 2 +- .../io/peasoup/inv/composer/Review.groovy | 10 +++---- .../io/peasoup/inv/composer/WebServer.groovy | 28 +++++++++++++++---- .../io/peasoup/inv/graph/DeltaGraph.groovy | 7 +++-- .../io/peasoup/inv/graph/RunGraph.groovy | 2 ++ .../io/peasoup/inv/scm/ScmDescriptor.groovy | 27 ++++++++++++++++-- .../io/peasoup/inv/scm/ScmExecutor.groovy | 8 +++--- .../resources/public/imports/choose-inv.js | 3 +- .../public/imports/configure-init.js | 3 ++ .../public/imports/configure-parameters.js | 6 ++-- .../public/imports/configure-scms.js | 10 +++++++ .../public/imports/configure-settings.js | 3 ++ src/main/resources/public/imports/layout.js | 11 +++++++- src/main/resources/public/imports/promote.js | 4 ++- src/main/resources/public/imports/review.js | 2 +- src/main/resources/public/imports/toast.js | 7 +++-- .../io/peasoup/inv/composer/ReviewTest.groovy | 7 ++--- .../io/peasoup/inv/scm/ScmHandlerTest.groovy | 2 +- 18 files changed, 108 insertions(+), 34 deletions(-) diff --git a/src/main/groovy/io/peasoup/inv/cli/InitCommand.groovy b/src/main/groovy/io/peasoup/inv/cli/InitCommand.groovy index 2f8bcac..fa77b8d 100644 --- a/src/main/groovy/io/peasoup/inv/cli/InitCommand.groovy +++ b/src/main/groovy/io/peasoup/inv/cli/InitCommand.groovy @@ -36,7 +36,7 @@ class InitCommand implements CliCommand { return false } - protected ScmExecutor.SCMReport processSCM() { + ScmExecutor.SCMReport processSCM() { String actualFileLocation = initFileLocation File scmFile diff --git a/src/main/groovy/io/peasoup/inv/composer/Review.groovy b/src/main/groovy/io/peasoup/inv/composer/Review.groovy index 7740349..1d59cde 100644 --- a/src/main/groovy/io/peasoup/inv/composer/Review.groovy +++ b/src/main/groovy/io/peasoup/inv/composer/Review.groovy @@ -66,15 +66,13 @@ class Review { Files.copy(latestRun.toPath(), latestBackup.toPath()) assert latestBackup.exists(), 'Latest run backup file must be present on filesystem' - def generatedRun = new File(RunsRoller.latest.folder(), "run.txt") - generatedRun.delete() - - DeltaGraph deltaGraph = new DeltaGraph(baseRun.newReader(), latestBackup.newReader()) + DeltaGraph deltaGraph = new DeltaGraph(baseRun.newReader(), latestRun.newReader()) deltaGraph.removeScms(removeScms) deltaGraph.resolve() - generatedRun << "This file was generated with Composer.${System.lineSeparator()}" - generatedRun.append(deltaGraph.merge()) + latestRun.delete() + latestRun << "This file was generated with Composer.${System.lineSeparator()}" + latestRun.append(deltaGraph.merge()) } Map compare() { diff --git a/src/main/groovy/io/peasoup/inv/composer/WebServer.groovy b/src/main/groovy/io/peasoup/inv/composer/WebServer.groovy index 4e90d91..7f18557 100644 --- a/src/main/groovy/io/peasoup/inv/composer/WebServer.groovy +++ b/src/main/groovy/io/peasoup/inv/composer/WebServer.groovy @@ -4,6 +4,7 @@ import groovy.json.JsonOutput import groovy.json.JsonSlurper import groovy.transform.CompileDynamic import groovy.transform.CompileStatic +import io.peasoup.inv.cli.InitCommand import io.peasoup.inv.graph.GraphNavigator import io.peasoup.inv.run.RunsRoller import io.peasoup.inv.scm.ScmDescriptor @@ -125,7 +126,8 @@ class WebServer { ], initFile : [ default: "/initfile", - save: "/initfile" + save: "/initfile", + pull: "/initfile/pull" ], run : [ default : "/run", @@ -183,7 +185,7 @@ class WebServer { get("/initfile", { Request req, Response res -> if (!webServerConfigs.initFile) - return "" + return showError("Missing init file") if (!(webServerConfigs.initFile instanceof String)) return showError(res, "InitFile is corrupted. Contact your administrator.") @@ -225,6 +227,23 @@ class WebServer { errors: exceptionMessages ]) }) + + post("/initfile/pull", { Request req, Response res -> + if (!webServerConfigs.initFile) + return showError("Missing init file") + + if (!(webServerConfigs.initFile instanceof String)) + return showError(res, "InitFile is corrupted. Contact your administrator.") + + // Reuse InitCommand to pull init file + InitCommand initCommand = new InitCommand(initFileLocation: webServerConfigs.initFile as String) + def report = initCommand.processSCM() + + if (!report) + showResult("Could not pull init") + + return showResult("Pulled init successfully") + }) } // Runs @@ -485,7 +504,7 @@ class WebServer { post("/scms/applyDefaultAll", { Request req, Response res -> scms.elements.values().each { ScmFile.SourceFileElement element -> - if (run == null || !run.isSelected(element.descriptor.name)) + if (run != null && !run.isSelected(element.descriptor.name)) return def parametersFile = new File(parametersLocation, element.simpleName() + ".json") @@ -501,8 +520,7 @@ class WebServer { post("/scms/resetAll", { Request req, Response res -> scms.elements.values().each { ScmFile.SourceFileElement element -> - - if (!run.isSelected(element.descriptor.name)) + if (run != null && !run.isSelected(element.descriptor.name)) return def parametersFile = new File(parametersLocation, element.simpleName() + ".json") diff --git a/src/main/groovy/io/peasoup/inv/graph/DeltaGraph.groovy b/src/main/groovy/io/peasoup/inv/graph/DeltaGraph.groovy index a58e947..2696078 100644 --- a/src/main/groovy/io/peasoup/inv/graph/DeltaGraph.groovy +++ b/src/main/groovy/io/peasoup/inv/graph/DeltaGraph.groovy @@ -2,7 +2,6 @@ package io.peasoup.inv.graph import groovy.text.SimpleTemplateEngine import groovy.transform.CompileStatic -import groovy.transform.ToString import io.peasoup.inv.run.InvInvoker import io.peasoup.inv.run.RunsRoller @@ -163,11 +162,15 @@ class DeltaGraph { return "Report generated at: ${htmlOutput.canonicalPath}" } - @ToString static class DeltaLine { String state GraphNavigator.Linkable link GraphNavigator.Node owner + + @Override + String toString() { + "[${state}] ${link.value}" + } } } diff --git a/src/main/groovy/io/peasoup/inv/graph/RunGraph.groovy b/src/main/groovy/io/peasoup/inv/graph/RunGraph.groovy index 711e90b..f319338 100644 --- a/src/main/groovy/io/peasoup/inv/graph/RunGraph.groovy +++ b/src/main/groovy/io/peasoup/inv/graph/RunGraph.groovy @@ -79,6 +79,8 @@ class RunGraph { files << file } } + + logs.close() } String toPlainList() { diff --git a/src/main/groovy/io/peasoup/inv/scm/ScmDescriptor.groovy b/src/main/groovy/io/peasoup/inv/scm/ScmDescriptor.groovy index 8142c76..5749e4f 100644 --- a/src/main/groovy/io/peasoup/inv/scm/ScmDescriptor.groovy +++ b/src/main/groovy/io/peasoup/inv/scm/ScmDescriptor.groovy @@ -3,6 +3,7 @@ package io.peasoup.inv.scm import groovy.json.JsonSlurper import groovy.transform.CompileStatic import io.peasoup.inv.Home +import io.peasoup.inv.run.Logger @CompileStatic class ScmDescriptor { @@ -99,10 +100,30 @@ class ScmDescriptor { class HookDescriptor { String init - def init(String value) { this.init = value } + /** + * Indicates how the init (or initialization) phase should behave. + * It is similar to 'git clone', 'svn checkout', 'tf get', etc. + * @param value the Shell Script (Sh) commands + */ + void init(String value) { this.init = value } + + String pull + /** + * Indicates how pull should behave + * @param value the Shell Script (Sh) commands + */ + void pull(String value) {this.pull = value } - String update - def update(String value) { this.update = value } + /** + * DEPRECATED. See 'pull' instead + * @param value + * @return + */ + @Deprecated + def update(String value) { + Logger.warn("scm.update() is deprecated. Use scm.pull() instead.") + pull(value) + } } class AskDescriptor { diff --git a/src/main/groovy/io/peasoup/inv/scm/ScmExecutor.groovy b/src/main/groovy/io/peasoup/inv/scm/ScmExecutor.groovy index b96f9e2..5a38e84 100644 --- a/src/main/groovy/io/peasoup/inv/scm/ScmExecutor.groovy +++ b/src/main/groovy/io/peasoup/inv/scm/ScmExecutor.groovy @@ -51,11 +51,11 @@ class ScmExecutor { report.isOk = executeCommands(repository, repository.hooks.init) Logger.info("[SCM] name: ${name}, path: ${repository.path.canonicalPath} [INIT] done") - } else if (repository.hooks.update) { + } else if (repository.hooks.pull) { - Logger.info("[SCM] name: ${name}, path: ${repository.path.canonicalPath} [UPDATE] start") - report.isOk = executeCommands(repository, repository.hooks.update) - Logger.info("[SCM] name: ${name}, path: ${repository.path.canonicalPath} [UPDATE] done") + Logger.info("[SCM] name: ${name}, path: ${repository.path.canonicalPath} [PULL] start") + report.isOk = executeCommands(repository, repository.hooks.pull) + Logger.info("[SCM] name: ${name}, path: ${repository.path.canonicalPath} [PULL] done") } return report diff --git a/src/main/resources/public/imports/choose-inv.js b/src/main/resources/public/imports/choose-inv.js index 8bf494e..3dc7b99 100644 --- a/src/main/resources/public/imports/choose-inv.js +++ b/src/main/resources/public/imports/choose-inv.js @@ -104,7 +104,8 @@ Vue.component('choose-inv', { filter() }) - .catch(response => { + .catch(err => { + vm.$bus.$emit('toast', `error:Failed to fetch broadcast owners!`) }) } diff --git a/src/main/resources/public/imports/configure-init.js b/src/main/resources/public/imports/configure-init.js index 9d23338..c972a55 100644 --- a/src/main/resources/public/imports/configure-init.js +++ b/src/main/resources/public/imports/configure-init.js @@ -109,6 +109,9 @@ Vue.component('configure-init', { vm.$bus.$emit('toast', `success:Saved init file successfully!`) } }) + .catch(err => { + vm.$bus.$emit('toast', `error:Failed to save init file!`) + }) }, close: function() { var vm = this diff --git a/src/main/resources/public/imports/configure-parameters.js b/src/main/resources/public/imports/configure-parameters.js index b9317a0..76b0fe3 100644 --- a/src/main/resources/public/imports/configure-parameters.js +++ b/src/main/resources/public/imports/configure-parameters.js @@ -178,7 +178,7 @@ Vue.component('configure-parameters', { axios.post(vm.value.api.links.scms.applyDefaultAll).then(response => { vm.updateIndex++ - vm.$bus.$emit('toast', `success:Applied all defaults parameters successfully!`) + vm.$bus.$emit('toast', `warn:Applied all defaults parameters successfully!`) }) }, resetAll: function() { @@ -483,7 +483,7 @@ Vue.component('configure-parameters-carousel', { vm.value.edit(scmParameters) }) - .catch(error => { + .catch(err => { scmParameters.changed = false scmParameters.loaded = false @@ -491,6 +491,8 @@ Vue.component('configure-parameters-carousel', { scmParameters.errors.push(error.response.data) vm.$forceUpdate() + + vm.$bus.$emit('toast', `error:Failed to edit parameters!`) }) }, resetParameters: function(scmParameters) { diff --git a/src/main/resources/public/imports/configure-scms.js b/src/main/resources/public/imports/configure-scms.js index a23522c..39c01d5 100644 --- a/src/main/resources/public/imports/configure-scms.js +++ b/src/main/resources/public/imports/configure-scms.js @@ -260,6 +260,13 @@ Vue.component('configure-scms-details', { vm.searchScm() } }) + .catch(err => { + if (vm.mode == 'edit') + vm.$bus.$emit('toast', `success:Failed to save ${vm.editScript.descriptor.name}!`) + + if (vm.mode == 'new') + vm.$bus.$emit('toast', `success:Failed to save ${vm.newName}!`) + }) }, closeEdit: function() { var vm = this @@ -284,6 +291,9 @@ Vue.component('configure-scms-details', { vm.$bus.$emit('toast', `warn:Removed ${scm.descriptor.name} successfully!`) vm.searchScm() }) + .catch(err => { + vm.$bus.$emit('toast', `error:Failed to remove ${scm.descriptor.name}!`) + }) } }, mounted: function() { diff --git a/src/main/resources/public/imports/configure-settings.js b/src/main/resources/public/imports/configure-settings.js index 0617ead..4bf7391 100644 --- a/src/main/resources/public/imports/configure-settings.js +++ b/src/main/resources/public/imports/configure-settings.js @@ -97,6 +97,9 @@ Vue.component('configure-settings', { vm.$bus.$emit('toast', `success:Saved settings.xml successfully!`) }) + .catch(err => { + vm.$bus.$emit('toast', `error:Failed to save settings.xml!`) + }) }, close: function() { var vm = this diff --git a/src/main/resources/public/imports/layout.js b/src/main/resources/public/imports/layout.js index 74ac19b..c902cbf 100644 --- a/src/main/resources/public/imports/layout.js +++ b/src/main/resources/public/imports/layout.js @@ -82,7 +82,7 @@ Vue.component('layout', { Edit SCMs Edit init file - Pull changes (coming soon!) + Pull changes Push changes (coming soon!) Reset everything (coming soon!) @@ -310,6 +310,15 @@ Vue.component('layout', { showConfigureInit: function() { this.navbar.configureInit.model.visible = true }, + pullInit: function() { + var vm = this + axios.post(vm.shared.api.links.initFile.pull).then(response => { + vm.$bus.$emit('toast', `success:Pulled init file changes successfully!`) + }) + .catch(err => { + vm.$bus.$emit('toast', `error:Failed to pull init file changes!`) + }) + } }, mounted: function() { var vm = this diff --git a/src/main/resources/public/imports/promote.js b/src/main/resources/public/imports/promote.js index be366e0..235e55e 100644 --- a/src/main/resources/public/imports/promote.js +++ b/src/main/resources/public/imports/promote.js @@ -39,10 +39,12 @@ Vue.component('promote', { window.location.href = '#choose' window.location.reload(true) }, 1000) - }).catch(reponse => { + }).catch(err => { vm.promoted = false vm.promoting = false vm.error = true + + vm.$bus.$emit('toast', `error:Failed to promote!`) }) } }, diff --git a/src/main/resources/public/imports/review.js b/src/main/resources/public/imports/review.js index 14eeac6..ca1f7e8 100644 --- a/src/main/resources/public/imports/review.js +++ b/src/main/resources/public/imports/review.js @@ -198,7 +198,7 @@ Vue.component('review', { vm.loading = false vm.ready = true }) - .catch(function() { + .catch(err => { vm.loading = false }) }, diff --git a/src/main/resources/public/imports/toast.js b/src/main/resources/public/imports/toast.js index ca22a76..829baa6 100644 --- a/src/main/resources/public/imports/toast.js +++ b/src/main/resources/public/imports/toast.js @@ -28,8 +28,11 @@ Vue.component('toast', { clearInterval(vm.latestToast) // Get which styleClass to use - if (message.startsWith('warn:')) { - vm.styleClass = 'is-warning' + if (message.startsWith('error:')) { + vm.styleClass = 'is-danger' + vm.message = message.substring(6) + } else if (message.startsWith('warn:')) { + vm.styleClass = 'is-primary' vm.message = message.substring(5) } else if (message.startsWith('success:')) { vm.styleClass = 'is-success' diff --git a/src/test/groovy/io/peasoup/inv/composer/ReviewTest.groovy b/src/test/groovy/io/peasoup/inv/composer/ReviewTest.groovy index dd057f3..dbd87da 100644 --- a/src/test/groovy/io/peasoup/inv/composer/ReviewTest.groovy +++ b/src/test/groovy/io/peasoup/inv/composer/ReviewTest.groovy @@ -44,23 +44,22 @@ class ReviewTest { RunsRoller.latest.roll() // make sure to roll once // Generate base - def baseRun = new File(Home.getCurrent(), 'run.txt') + def baseRun = new File(Home.getCurrent(), 'base-merge.txt') baseRun.delete() baseRun << new File(getClass().getResource('/baseRun.txt').toURI()).text // Generate subset - def subsetFile = new File(RunsRoller.latest.folder(), 'run.txt') + def subsetFile = new File(RunsRoller.latest.folder(), 'run-merge.txt') subsetFile.delete() subsetFile << new File(getClass().getResource('/subsetRun.txt').toURI()).text def deltaGraph = new DeltaGraph(baseRun.newReader(), subsetFile.newReader()) deltaGraph.resolve() + def removed = deltaGraph.deltaLines.findAll { it.state == 'x' } new Review(baseRun, subsetFile).merge() assert subsetFile.exists() - def newBaseFileGraph = new RunGraph(subsetFile.newReader()) - def removed = deltaGraph.deltaLines.findAll { it.state == 'x' } assert removed.size() > 0 removed.each { diff --git a/src/test/groovy/io/peasoup/inv/scm/ScmHandlerTest.groovy b/src/test/groovy/io/peasoup/inv/scm/ScmHandlerTest.groovy index 4b30dda..3b40fc6 100644 --- a/src/test/groovy/io/peasoup/inv/scm/ScmHandlerTest.groovy +++ b/src/test/groovy/io/peasoup/inv/scm/ScmHandlerTest.groovy @@ -33,7 +33,7 @@ class ScmHandlerTest { assert executor.scms["my-repository"].hooks assert executor.scms["my-repository"].hooks.init.contains("mkdir my-repository") - assert executor.scms["my-repository"].hooks.update.contains("echo 'update'") + assert executor.scms["my-repository"].hooks.pull.contains("echo 'update'") } @Test