From c26ccfe49bcef9a3891203eb33d82143a1f1c7a1 Mon Sep 17 00:00:00 2001 From: bullman Date: Wed, 8 Apr 2026 12:38:49 -0700 Subject: [PATCH 1/4] add qol fixes --- app/services/LibraryDb.js | 17 +++---- gulpfile.babel.js | 96 ++++++++++++++++++++++++++++++++------- server/server.js | 2 + server/views/index.pug | 3 ++ 4 files changed, 94 insertions(+), 24 deletions(-) diff --git a/app/services/LibraryDb.js b/app/services/LibraryDb.js index 3c9fd02a2..91d1ab632 100644 --- a/app/services/LibraryDb.js +++ b/app/services/LibraryDb.js @@ -1,12 +1,15 @@ "use strict"; +import fs from "fs"; +import path from "path"; import _ from "underscore"; -import StepTemplates from "./step-templates.json"; - class LibraryDb { - constructor() { - this._items = _.chain(StepTemplates.items) + loadTemplates() { + const templatePath = path.join(__dirname, "step-templates.json"); + const stepTemplates = JSON.parse(fs.readFileSync(templatePath, "utf8")); + + return _.chain(stepTemplates.items) .map(function (t) { if (t.Properties) { var script = t.Properties["Octopus.Action.Script.ScriptBody"]; @@ -41,16 +44,14 @@ class LibraryDb { return t.Name.toLowerCase(); }) .value(); - - this._all = _.indexBy(this._items, "Id"); } list(cb) { - cb(null, this._items); + cb(null, this.loadTemplates()); } get(id, cb) { - var item = this._all[id]; + var item = _.indexBy(this.loadTemplates(), "Id")[id]; cb(null, item); } } diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 82120c4ae..695b6fcdf 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -30,8 +30,11 @@ import jasmineReporters from "jasmine-reporters"; import jasmineTerminalReporter from "jasmine-terminal-reporter"; import eventStream from "event-stream"; import fs from "fs"; +import http from "http"; +import https from "https"; import jsonlint from "gulp-jsonlint"; import path from "path"; +import { spawn } from "child_process"; const sass = gulpSass(dartSass); const clientDir = "app"; @@ -49,6 +52,54 @@ const $ = gulpLoadPlugins({ const reload = browserSync.reload; const argv = yargs.argv; +function openBrowser(url) { + if (process.env.CI) { + return; + } + + if (process.platform === "darwin") { + spawn("open", [url], { detached: true, stdio: "ignore" }).unref(); + return; + } + + if (process.platform === "win32") { + spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref(); + return; + } + + spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref(); +} + +function waitForServer(url, { timeoutMs = 10000, pollIntervalMs = 200 } = {}) { + const parsedUrl = new URL(url); + const client = parsedUrl.protocol === "https:" ? https : http; + const startedAt = Date.now(); + + return new Promise((resolve) => { + function tryConnect() { + const request = client.get(url, (response) => { + response.resume(); + resolve(); + }); + + request.on("error", () => { + if (Date.now() - startedAt >= timeoutMs) { + resolve(); + return; + } + + setTimeout(tryConnect, pollIntervalMs); + }); + + request.setTimeout(pollIntervalMs, () => { + request.destroy(); + }); + } + + tryConnect(); + }); +} + const vendorStyles = [ "node_modules/font-awesome/css/font-awesome.min.css", "node_modules/font-awesome/css/font-awesome.css.map", @@ -323,17 +374,16 @@ function provideMissingData() { }); } -gulp.task( - "step-templates", - gulp.series("tests", () => { - return gulp - .src("./step-templates/*.json") - .pipe(provideMissingData()) - .pipe(concat("step-templates.json", { newLine: "," })) - .pipe(insert.wrap('{"items": [', "]}")) - .pipe(argv.production ? gulp.dest(`${publishDir}/app/services`) : gulp.dest(`${buildDir}/app/services`)); - }) -); +gulp.task("step-templates:data", () => { + return gulp + .src("./step-templates/*.json") + .pipe(provideMissingData()) + .pipe(concat("step-templates.json", { newLine: "," })) + .pipe(insert.wrap('{"items": [', "]}")) + .pipe(argv.production ? gulp.dest(`${publishDir}/app/services`) : gulp.dest(`${buildDir}/app/services`)); +}); + +gulp.task("step-templates", gulp.series("tests", "step-templates:data")); gulp.task("styles:vendor", () => { return gulp.src(vendorStyles, { base: "node_modules/" }).pipe(argv.production ? gulp.dest(`${publishDir}/public/styles/vendor`) : gulp.dest(`${buildDir}/public/styles/vendor`)); @@ -426,14 +476,28 @@ gulp.task( server.start(); process.chdir(`../`); - browserSync.init(null, { - proxy: "http://localhost:9000", - }); + browserSync.init( + null, + { + proxy: "http://localhost:9000", + open: false, + }, + () => { + waitForServer("http://localhost:9000").then(() => { + openBrowser("http://localhost:9000"); + }); + } + ); + + function reloadServer(done) { + server.start.bind(server)(); + done(); + } gulp.watch(`${clientDir}/**/*.jade`, gulp.series("build:client")); - gulp.watch(`${clientDir}/**/*.jsx`, gulp.series("scripts", "copy:app")); + gulp.watch(`${clientDir}/**/*.jsx`, gulp.series("scripts", "copy:app", reloadServer)); gulp.watch(`${clientDir}/content/styles/**/*.scss`, gulp.series("styles:client")); - gulp.watch("step-templates/*.json", gulp.series("step-templates")); + gulp.watch("step-templates/*.json", gulp.series("step-templates:data")); gulp.watch(`${buildDir}/**/*.*`).on("change", reload); }) diff --git a/server/server.js b/server/server.js index a219f0ac3..92021e31b 100644 --- a/server/server.js +++ b/server/server.js @@ -74,11 +74,13 @@ app.get("*", (req, res) => { LibraryActions.sendTemplates(data, () => { var libraryAppHtml = ReactDOMServer.renderToStaticMarkup(); + const browserSyncClientUrl = process.env.NODE_ENV === "development" ? `${req.protocol}://${req.hostname}:3000/browser-sync/browser-sync-client.js` : null; res.render("index", { siteKeywords: config.keywords.join(), siteDescription: config.description, reactOutput: libraryAppHtml, stepTemplates: JSON.stringify(data), + browserSyncClientUrl, }); }); }); diff --git a/server/views/index.pug b/server/views/index.pug index 7e5d21571..4732d1874 100644 --- a/server/views/index.pug +++ b/server/views/index.pug @@ -46,5 +46,8 @@ html ga('create', 'UA-24461753-5', 'octopusdeploy.com'); + if browserSyncClientUrl + script(type='text/javascript', async=true, src=browserSyncClientUrl) + //- inject:js //- endinject From ddb9cd424057a89e4039416844226bee469f26ae Mon Sep 17 00:00:00 2001 From: bullman Date: Wed, 8 Apr 2026 13:02:37 -0700 Subject: [PATCH 2/4] fix live reload on ract components --- gulpfile.babel.js | 5 ++- package-lock.json | 111 +++++++++++++++++++++++++++++++++------------- server/server.js | 17 ++++++- 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 695b6fcdf..d008327f5 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -115,7 +115,6 @@ function lint(files, options = {}) { return () => { return gulp .src(files) - .pipe(reload({ stream: true, once: true })) .pipe($.eslint(options)) .pipe($.eslint.format("compact")) .pipe($.if(!browserSync.active, $.eslint.failOnError())); @@ -490,7 +489,9 @@ gulp.task( ); function reloadServer(done) { - server.start.bind(server)(); + process.chdir(`${buildDir}`); + server.start(); + process.chdir(`../`); done(); } diff --git a/package-lock.json b/package-lock.json index 79fe93f4a..bef1c47a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2992,6 +2992,17 @@ "node": ">=0.10.0" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "1.2.3", "dev": true, @@ -3329,6 +3340,21 @@ "node": ">=8" } }, + "node_modules/browser-sync/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/browser-sync/node_modules/get-caller-file": { "version": "2.0.5", "dev": true, @@ -6454,6 +6480,14 @@ "node": ">=0.10.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/fill-range": { "version": "4.0.0", "dev": true, @@ -6737,6 +6771,26 @@ "version": "1.0.0", "license": "ISC" }, + "node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -10114,6 +10168,14 @@ "version": "0.0.7", "license": "ISC" }, + "node_modules/nan": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz", + "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.4", "dev": true, @@ -10515,38 +10577,8 @@ } }, "node_modules/octopus-library": { - "version": "2.0.0", - "resolved": "file:", - "license": "Apache-2.0", - "dependencies": { - "@babel/core": "^7.24.4", - "@babel/node": "^7.23.9", - "@babel/preset-env": "^7.24.3", - "@babel/preset-react": "^7.24.1", - "@babel/register": "^7.23.7", - "compression": "^1.6.1", - "eslint-config-prettier": "^4.3.0", - "events": "^1.1.0", - "express": "^4.13.4", - "font-awesome": "^4.6.1", - "frameguard": "^3.0.0", - "history": "^2.1.0", - "isomorphic-dompurify": "^0.17.0", - "marked": "^4.0.10", - "moment": "^2.13.0", - "node-uuid": "^1.4.7", - "normalize.css": "^4.1.1", - "octopus-library": "file:", - "prettier": "^2.6.2", - "prop-types": "^15.6.0", - "pug": "^3.0.2", - "react": "^15.0.1", - "react-copy-to-clipboard": "^5.0.2", - "react-dom": "^15.0.1", - "react-router": "^2.3.0", - "react-syntax-highlighter": "^15.4.5", - "underscore": "^1.12.1" - } + "resolved": "", + "link": true }, "node_modules/on-finished": { "version": "2.3.0", @@ -12480,6 +12512,21 @@ "node": ">=8" } }, + "node_modules/sass/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/sass/node_modules/glob-parent": { "version": "5.1.2", "dev": true, diff --git a/server/server.js b/server/server.js index 92021e31b..4f2bc310f 100644 --- a/server/server.js +++ b/server/server.js @@ -18,9 +18,22 @@ import LibraryStorageService from "./app/services/LibraryDb.js"; import LibraryActions from "./app/actions/LibraryActions"; let app = express(); +const isDevelopment = process.env.NODE_ENV === "development"; +const staticAssetOptions = isDevelopment + ? { + maxAge: 0, + etag: false, + lastModified: false, + setHeaders: (res) => { + res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); + res.setHeader("Pragma", "no-cache"); + res.setHeader("Expires", "0"); + }, + } + : { maxage: "1d" }; -app.use(express.static(path.join(__dirname, "public"), { maxage: "1d" })); -app.use(express.static(path.join(__dirname, "views"), { maxage: "1d" })); +app.use(express.static(path.join(__dirname, "public"), staticAssetOptions)); +app.use(express.static(path.join(__dirname, "views"), staticAssetOptions)); app.use("/.well-known", express.static(".well-known")); app.set("views", path.join(__dirname, "views")); From 40dc62a4d7ae606369fa32e9c36dbd224120dcd8 Mon Sep 17 00:00:00 2001 From: bullman Date: Wed, 8 Apr 2026 13:07:02 -0700 Subject: [PATCH 3/4] Revert package-lock.json churn --- package-lock.json | 111 +++++++++++++--------------------------------- 1 file changed, 32 insertions(+), 79 deletions(-) diff --git a/package-lock.json b/package-lock.json index bef1c47a4..79fe93f4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2992,17 +2992,6 @@ "node": ">=0.10.0" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bl": { "version": "1.2.3", "dev": true, @@ -3340,21 +3329,6 @@ "node": ">=8" } }, - "node_modules/browser-sync/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/browser-sync/node_modules/get-caller-file": { "version": "2.0.5", "dev": true, @@ -6480,14 +6454,6 @@ "node": ">=0.10.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/fill-range": { "version": "4.0.0", "dev": true, @@ -6771,26 +6737,6 @@ "version": "1.0.0", "license": "ISC" }, - "node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -10168,14 +10114,6 @@ "version": "0.0.7", "license": "ISC" }, - "node_modules/nan": { - "version": "2.26.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz", - "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/nanoid": { "version": "3.3.4", "dev": true, @@ -10577,8 +10515,38 @@ } }, "node_modules/octopus-library": { - "resolved": "", - "link": true + "version": "2.0.0", + "resolved": "file:", + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/node": "^7.23.9", + "@babel/preset-env": "^7.24.3", + "@babel/preset-react": "^7.24.1", + "@babel/register": "^7.23.7", + "compression": "^1.6.1", + "eslint-config-prettier": "^4.3.0", + "events": "^1.1.0", + "express": "^4.13.4", + "font-awesome": "^4.6.1", + "frameguard": "^3.0.0", + "history": "^2.1.0", + "isomorphic-dompurify": "^0.17.0", + "marked": "^4.0.10", + "moment": "^2.13.0", + "node-uuid": "^1.4.7", + "normalize.css": "^4.1.1", + "octopus-library": "file:", + "prettier": "^2.6.2", + "prop-types": "^15.6.0", + "pug": "^3.0.2", + "react": "^15.0.1", + "react-copy-to-clipboard": "^5.0.2", + "react-dom": "^15.0.1", + "react-router": "^2.3.0", + "react-syntax-highlighter": "^15.4.5", + "underscore": "^1.12.1" + } }, "node_modules/on-finished": { "version": "2.3.0", @@ -12512,21 +12480,6 @@ "node": ">=8" } }, - "node_modules/sass/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/sass/node_modules/glob-parent": { "version": "5.1.2", "dev": true, From 76e8ec546e2ca918c3008737021434c0273d038d Mon Sep 17 00:00:00 2001 From: bullman Date: Wed, 8 Apr 2026 13:26:01 -0700 Subject: [PATCH 4/4] limit rebuilds to dev --- app/services/LibraryDb.js | 47 ++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/app/services/LibraryDb.js b/app/services/LibraryDb.js index 91d1ab632..ab6e97e10 100644 --- a/app/services/LibraryDb.js +++ b/app/services/LibraryDb.js @@ -5,11 +5,22 @@ import path from "path"; import _ from "underscore"; class LibraryDb { - loadTemplates() { + constructor() { + this._items = null; + this._all = null; + } + + isDevelopment() { + return process.env.NODE_ENV === "development"; + } + + readTemplatesFromDisk() { const templatePath = path.join(__dirname, "step-templates.json"); - const stepTemplates = JSON.parse(fs.readFileSync(templatePath, "utf8")); + return JSON.parse(fs.readFileSync(templatePath, "utf8")); + } - return _.chain(stepTemplates.items) + hydrateTemplates(stepTemplates) { + const items = _.chain(stepTemplates.items) .map(function (t) { if (t.Properties) { var script = t.Properties["Octopus.Action.Script.ScriptBody"]; @@ -44,14 +55,40 @@ class LibraryDb { return t.Name.toLowerCase(); }) .value(); + + return { + items, + all: _.indexBy(items, "Id"), + }; + } + + loadTemplates() { + return this.hydrateTemplates(this.readTemplatesFromDisk()); + } + + getTemplates() { + if (this.isDevelopment()) { + return this.loadTemplates(); + } + + if (!this._items || !this._all) { + const templates = this.loadTemplates(); + this._items = templates.items; + this._all = templates.all; + } + + return { + items: this._items, + all: this._all, + }; } list(cb) { - cb(null, this.loadTemplates()); + cb(null, this.getTemplates().items); } get(id, cb) { - var item = _.indexBy(this.loadTemplates(), "Id")[id]; + var item = this.getTemplates().all[id]; cb(null, item); } }