@fishawack/lab-env 5.0.0-beta.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ # HUB_URL="http://localhost:8000"
2
+ # HUB_TOKEN=
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  ## Changelog
2
2
 
3
+ ### 5.0.0 (2025-10-29)
4
+
5
+ #### Features
6
+
7
+ * added branch config sync from the hub on content command ([105e04c](https://bitbucket.org/fishawackdigital/lab-env/commits/105e04c29525afc1fd67e1affbda09bf269deaca))
8
+ * added hub.js ([6966d89](https://bitbucket.org/fishawackdigital/lab-env/commits/6966d89b42345276e088e627fe2d6769efaf044d))
9
+ * added package command ([51e42b8](https://bitbucket.org/fishawackdigital/lab-env/commits/51e42b83f166400bb404d0013b2c65bdce1e071c))
10
+ * copy .env.example in lab-env root if present ([c85d60b](https://bitbucket.org/fishawackdigital/lab-env/commits/c85d60b5c389f3fcad185290a3e6058572e59e8d))
11
+ * **core@1:** point to 1.8.0-beta.2 ([aaaed0a](https://bitbucket.org/fishawackdigital/lab-env/commits/aaaed0af45bde22341fbeb2e59467902be747aff))
12
+ * export core config to globals ([0907327](https://bitbucket.org/fishawackdigital/lab-env/commits/0907327ca0b04af9032e90814d4bb7aef796ad87))
13
+ * expose remote in globals ([7f440cb](https://bitbucket.org/fishawackdigital/lab-env/commits/7f440cb51bd7db884f89c069901f7431d8b09b11))
14
+ * **laravel:** source env.sh when present by default ([7fe564a](https://bitbucket.org/fishawackdigital/lab-env/commits/7fe564a85b014c67cfcc6d34e699c8e7e5d77acd))
15
+ * setup hub logging on deployments ([65c3ea6](https://bitbucket.org/fishawackdigital/lab-env/commits/65c3ea632a06b4d7cafc7b118bd68774268f63df))
16
+
17
+ #### Bug Fixes
18
+
19
+ * dont ignore bootstrap files now a lot of laravel code is in there ([92d67c2](https://bitbucket.org/fishawackdigital/lab-env/commits/92d67c2dc9e065a2781a2ff02ee559fb615fc90f))
20
+ * switched from elasticsearch to opensearch ([a589384](https://bitbucket.org/fishawackdigital/lab-env/commits/a5893848e37415d769419f4dc54549b0cd6408c6))
21
+
22
+ #### Build Updates
23
+
24
+ * added example env ([7c5e3ba](https://bitbucket.org/fishawackdigital/lab-env/commits/7c5e3baf05023ae3ca1a8d5632d297052b3a1443))
25
+ * **core/1:** Bumped lab-env-core-1 ([a0ce4c7](https://bitbucket.org/fishawackdigital/lab-env/commits/a0ce4c77514c2bb3b24870e63d5ec23b411db9b6))
26
+ * ignore env varS ([c94c127](https://bitbucket.org/fishawackdigital/lab-env/commits/c94c1274de85c4818cbbc7a7683678dc6edf5d17))
27
+ * installed dotenv and form-data ([ebb4969](https://bitbucket.org/fishawackdigital/lab-env/commits/ebb49697308b359c8be7343950dad20577531c16))
28
+
29
+ ### 5.0.0-beta.2 (2025-10-29)
30
+
31
+ #### Features
32
+
33
+ * added branch config sync from the hub on content command ([105e04c](https://bitbucket.org/fishawackdigital/lab-env/commits/105e04c29525afc1fd67e1affbda09bf269deaca))
34
+ * added hub.js ([6966d89](https://bitbucket.org/fishawackdigital/lab-env/commits/6966d89b42345276e088e627fe2d6769efaf044d))
35
+ * added package command ([51e42b8](https://bitbucket.org/fishawackdigital/lab-env/commits/51e42b83f166400bb404d0013b2c65bdce1e071c))
36
+ * copy .env.example in lab-env root if present ([c85d60b](https://bitbucket.org/fishawackdigital/lab-env/commits/c85d60b5c389f3fcad185290a3e6058572e59e8d))
37
+ * export core config to globals ([0907327](https://bitbucket.org/fishawackdigital/lab-env/commits/0907327ca0b04af9032e90814d4bb7aef796ad87))
38
+ * expose remote in globals ([7f440cb](https://bitbucket.org/fishawackdigital/lab-env/commits/7f440cb51bd7db884f89c069901f7431d8b09b11))
39
+ * setup hub logging on deployments ([65c3ea6](https://bitbucket.org/fishawackdigital/lab-env/commits/65c3ea632a06b4d7cafc7b118bd68774268f63df))
40
+
41
+ #### Build Updates
42
+
43
+ * added example env ([7c5e3ba](https://bitbucket.org/fishawackdigital/lab-env/commits/7c5e3baf05023ae3ca1a8d5632d297052b3a1443))
44
+ * ignore env varS ([c94c127](https://bitbucket.org/fishawackdigital/lab-env/commits/c94c1274de85c4818cbbc7a7683678dc6edf5d17))
45
+ * installed dotenv and form-data ([ebb4969](https://bitbucket.org/fishawackdigital/lab-env/commits/ebb49697308b359c8be7343950dad20577531c16))
46
+
3
47
  ### 5.0.0-beta.1 (2025-10-27)
4
48
 
5
49
  #### Features
Binary file
package/_Test/hub.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+ const expect = require("chai").expect;
5
+ const hub = require("../hub.js");
6
+
7
+ let branch, remote;
8
+
9
+ try {
10
+ branch =
11
+ process.env.BRANCH ||
12
+ process.env.CI_COMMIT_REF_NAME ||
13
+ require("git-branch").sync();
14
+ } catch {
15
+ branch = "unknown";
16
+ }
17
+
18
+ try {
19
+ remote =
20
+ process.env.REMOTE_URL ||
21
+ require("child_process")
22
+ .execSync(`git config --get remote.origin.url`, {
23
+ encoding: "utf8",
24
+ })
25
+ .trim();
26
+ } catch {
27
+ remote = "unknown";
28
+ }
29
+
30
+ describe("hub", () => {
31
+ describe("deployment", () => {
32
+ let deployment;
33
+
34
+ it("Should create a deployment model", async () => {
35
+ const repository = await hub.findRepository(remote);
36
+
37
+ deployment = await hub.startedDeployment(repository.id, branch);
38
+
39
+ expect(deployment.status).to.equal("started");
40
+ });
41
+
42
+ it("Should fail a deployment", async () => {
43
+ deployment = await hub.failedDeployment(deployment.id);
44
+
45
+ expect(deployment.status).to.equal("failed");
46
+ });
47
+
48
+ it("Should finish a deployment", async () => {
49
+ deployment = await hub.finishedDeployment(deployment.id);
50
+
51
+ expect(deployment.status).to.equal("completed");
52
+ });
53
+
54
+ after(() => {
55
+ hub.purgeDeployment(deployment.id);
56
+ });
57
+ });
58
+
59
+ describe("artifacts", () => {
60
+ let artifact;
61
+
62
+ it("Should create a artifact model", async () => {
63
+ const repository = await hub.findRepository(remote);
64
+
65
+ artifact = await hub.storeArtifact(
66
+ repository.id,
67
+ branch,
68
+ "pdf",
69
+ path.join(__dirname, "_fixtures/dummy.pdf"),
70
+ );
71
+
72
+ expect(artifact.file_path).to.not.be.empty;
73
+ });
74
+
75
+ after(() => {
76
+ hub.purgeArtifact(artifact.id);
77
+ });
78
+ });
79
+
80
+ describe("configurations", () => {
81
+ let configuration;
82
+
83
+ it("Should create a configuration model", async () => {
84
+ const repository = await hub.findRepository(remote);
85
+
86
+ configuration = await hub.getConfigurations(repository.id);
87
+
88
+ expect(configuration).to.not.be.undefined;
89
+ });
90
+ });
91
+ });
package/cli.js CHANGED
@@ -1,5 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const path = require("path");
4
+ const { copyFileSync, existsSync } = require("fs");
5
+
6
+ // Copy env.example if .env doesnt exist
7
+ if (
8
+ existsSync(path.join(__dirname, ".env.example")) &&
9
+ !existsSync(path.join(__dirname, ".env"))
10
+ ) {
11
+ copyFileSync(
12
+ path.join(__dirname, ".env.example"),
13
+ path.join(__dirname, ".env"),
14
+ );
15
+ }
16
+
3
17
  process.env.CWD = process.cwd();
4
18
 
5
19
  const _ = require("./globals.js");
@@ -89,6 +103,7 @@ const args = hideBin(process.argv);
89
103
  content: _.config.preset !== "external" && _.platform !== "python",
90
104
  deploy: _.config.preset === "permanent",
91
105
  origin: _.config.preset === "permanent",
106
+ package: _.config.preset === "permanent",
92
107
  };
93
108
 
94
109
  // Build commands array
@@ -1,6 +1,9 @@
1
1
  const execSync = require("child_process").execSync;
2
+ const fs = require("fs-extra");
2
3
  const utilities = require("./create/libs/utilities");
3
4
  const _ = require("../globals.js");
5
+ const { getConfigurations, findRepository } = require("../hub.js");
6
+ const glob = require("glob");
4
7
 
5
8
  module.exports = [
6
9
  "content",
@@ -12,7 +15,7 @@ module.exports = [
12
15
  type: "boolean",
13
16
  });
14
17
  },
15
- (argv) => {
18
+ async (argv) => {
16
19
  if (argv.init) {
17
20
  const stringify = JSON.stringify(
18
21
  {
@@ -33,6 +36,38 @@ module.exports = [
33
36
  );
34
37
  } else {
35
38
  _.command("core", `npm run content`);
39
+
40
+ if (process.env.HUB_URL) {
41
+ console.log(`Syncing branch configurations from Hub...`);
42
+ // TODO: Fetch specific values and map to core config vlaues
43
+ // TODO: Add migration step of test ui to docs
44
+ const repository = await findRepository(_.remote);
45
+ const configurations = await getConfigurations(repository?.id);
46
+
47
+ // Remove existing hub configuration files
48
+ glob.sync("hub.*.*.json").forEach((file) =>
49
+ fs.removeSync(file),
50
+ );
51
+
52
+ configurations?.forEach(({ config, id, branch }) => {
53
+ const filePath = `hub.${branch}.${id}.json`;
54
+
55
+ fs.writeFileSync(
56
+ filePath,
57
+ JSON.stringify(
58
+ {
59
+ attributes: {
60
+ targets: {
61
+ [branch]: config,
62
+ },
63
+ },
64
+ },
65
+ null,
66
+ 4,
67
+ ),
68
+ );
69
+ });
70
+ }
36
71
  }
37
72
  },
38
73
  ];
@@ -1,4 +1,10 @@
1
1
  const _ = require("../globals.js");
2
+ const {
3
+ startedDeployment,
4
+ finishedDeployment,
5
+ failedDeployment,
6
+ findRepository,
7
+ } = require("../hub.js");
2
8
 
3
9
  const execSync = require("child_process").execSync;
4
10
 
@@ -6,26 +12,42 @@ module.exports = [
6
12
  "deploy",
7
13
  "deploys the repo",
8
14
  () => {},
9
- () => {
10
- if (
11
- _.platform === "laravel" ||
12
- _.platform === "wordpress" ||
13
- _.platform === "drupal" ||
14
- _.platform === "craftcms" ||
15
- _.platform === "adonis" ||
16
- _.platform === "python"
17
- ) {
18
- _.up(() =>
19
- execSync(
20
- `source ${__dirname}/../intercept.sh && /bin/bash ./_Scripts/deploy.sh`,
21
- _.opts,
22
- ),
23
- );
24
- } else {
25
- _.command(
26
- "core",
27
- `${process.env.VERSION === "0" ? "xvfb-run " : ""}npm run deploy`,
28
- );
15
+ async () => {
16
+ if (!_.coreConfig?.attributes?.deploy) {
17
+ console.log(`No deployment location configured for ${_.branch}`);
18
+ return;
29
19
  }
20
+
21
+ const repository = await findRepository(_.remote);
22
+ const deployment = await startedDeployment(repository?.id, _.branch);
23
+
24
+ try {
25
+ if (
26
+ _.platform === "laravel" ||
27
+ _.platform === "wordpress" ||
28
+ _.platform === "drupal" ||
29
+ _.platform === "craftcms" ||
30
+ _.platform === "adonis" ||
31
+ _.platform === "python"
32
+ ) {
33
+ _.up(() =>
34
+ execSync(
35
+ `source ${__dirname}/../intercept.sh && /bin/bash ./_Scripts/deploy.sh`,
36
+ _.opts,
37
+ ),
38
+ );
39
+ } else {
40
+ _.command(
41
+ "core",
42
+ `${process.env.VERSION === "0" ? "xvfb-run " : ""}npm run deploy`,
43
+ );
44
+ }
45
+ } catch (e) {
46
+ await failedDeployment(deployment?.id);
47
+
48
+ throw e;
49
+ }
50
+
51
+ await finishedDeployment(deployment?.id);
30
52
  },
31
53
  ];
@@ -0,0 +1,51 @@
1
+ const fs = require("fs-extra");
2
+ const _ = require("../globals.js");
3
+ const path = require("path");
4
+ const { storeArtifact, findRepository } = require("../hub.js");
5
+ const glob = require("glob");
6
+
7
+ module.exports = [
8
+ "package",
9
+ "creates and transfers artifacts",
10
+ () => {},
11
+ async () => {
12
+ _.command("core", `npm run package`);
13
+
14
+ const repository = await findRepository(_.remote);
15
+
16
+ const artifactDirs = [
17
+ { dir: "_Zips/", type: "build" },
18
+ { dir: "_Pdfs/", type: "pdf" },
19
+ ];
20
+
21
+ for (const { dir, type } of artifactDirs) {
22
+ if (!fs.existsSync(dir)) {
23
+ console.log(`No artifacts found in ${dir}`);
24
+ continue;
25
+ }
26
+
27
+ // Store artifacts in Hub
28
+ const files = glob.sync(`${dir}**/*`, { nodir: true });
29
+ for (const file of files) {
30
+ const fileName = path.basename(file);
31
+
32
+ // Check if it's a screenshot in _Zips directory
33
+ const artifactType =
34
+ type === "build" && fileName.includes("Screenshot")
35
+ ? "screenshots"
36
+ : type;
37
+
38
+ const res = await storeArtifact(
39
+ repository?.id,
40
+ _.branch,
41
+ artifactType,
42
+ file,
43
+ );
44
+
45
+ if (res) {
46
+ console.log(`Stored ${artifactType} artifact: ${fileName}`);
47
+ }
48
+ }
49
+ }
50
+ },
51
+ ];
package/globals.js CHANGED
@@ -127,6 +127,7 @@ var exec;
127
127
  var running;
128
128
  var services;
129
129
  var branch;
130
+ var remote;
130
131
  let vscodeSettings = {};
131
132
  var diagnosis = "4.0.0";
132
133
  var opts = { encoding: "utf8", stdio: "inherit", shell: "/bin/bash" };
@@ -152,6 +153,18 @@ try {
152
153
  branch = "unknown";
153
154
  }
154
155
 
156
+ try {
157
+ remote =
158
+ process.env.REMOTE_URL ||
159
+ require("child_process")
160
+ .execSync(`git config --get remote.origin.url`, {
161
+ encoding: "utf8",
162
+ })
163
+ .trim();
164
+ } catch {
165
+ remote = "unknown";
166
+ }
167
+
155
168
  const coreConfig = getCoreMergedConfigs();
156
169
 
157
170
  try {
@@ -756,6 +769,7 @@ module.exports = {
756
769
  volumes: volumes.map((d) => `${repoSafe}_${d}`),
757
770
  repo,
758
771
  branch,
772
+ remote,
759
773
  repoSafe,
760
774
  pkg,
761
775
  docker,
@@ -763,6 +777,7 @@ module.exports = {
763
777
  running,
764
778
  opts,
765
779
  users,
780
+ coreConfig,
766
781
  ports: {
767
782
  async set() {
768
783
  if (!running) {
package/hub.js ADDED
@@ -0,0 +1,159 @@
1
+ require("dotenv").config({
2
+ path: `${__dirname}/.env`,
3
+ quiet: true,
4
+ override: true,
5
+ });
6
+
7
+ const axios = require("axios");
8
+ const FormData = require("form-data");
9
+ const fs = require("fs");
10
+
11
+ const baseUrl = process.env.HUB_URL;
12
+ const headers = {
13
+ headers: {
14
+ Authorization: `Bearer ${process.env.HUB_TOKEN}`,
15
+ },
16
+ };
17
+
18
+ module.exports = {
19
+ async startedDeployment(repositoryId, branch) {
20
+ try {
21
+ return (
22
+ await axios.post(
23
+ `${baseUrl}/api/services/deployments`,
24
+ {
25
+ repository_id: repositoryId,
26
+ branch,
27
+ status: "started",
28
+ },
29
+ headers,
30
+ )
31
+ ).data.data;
32
+ } catch (e) {
33
+ console.log(
34
+ `Deployment Hub: Error logging deployment start in hub: ${e.message}`,
35
+ );
36
+ process.exit(1);
37
+ }
38
+ },
39
+ async finishedDeployment(deploymentId) {
40
+ try {
41
+ return (
42
+ await axios.patch(
43
+ `${baseUrl}/api/services/deployments/${deploymentId}`,
44
+ {
45
+ status: "completed",
46
+ },
47
+ headers,
48
+ )
49
+ ).data.data;
50
+ } catch (e) {
51
+ console.log(
52
+ `Deployment Hub: Error logging deployment finish in hub: ${e.message}`,
53
+ );
54
+ process.exit(1);
55
+ }
56
+ },
57
+ async failedDeployment(deploymentId) {
58
+ try {
59
+ return (
60
+ await axios.patch(
61
+ `${baseUrl}/api/services/deployments/${deploymentId}`,
62
+ {
63
+ status: "failed",
64
+ },
65
+ headers,
66
+ )
67
+ ).data.data;
68
+ } catch (e) {
69
+ console.log(
70
+ `Error logging deployment failure in hub: ${e.message}`,
71
+ );
72
+ process.exit(1);
73
+ }
74
+ },
75
+ async purgeDeployment(deploymentId) {
76
+ try {
77
+ await axios.delete(
78
+ `${baseUrl}/api/services/deployments/${deploymentId}`,
79
+ headers,
80
+ );
81
+ } catch (e) {
82
+ console.log(e.message);
83
+ process.exit(1);
84
+ }
85
+ },
86
+ async storeArtifact(repositoryId, branch, type, file) {
87
+ try {
88
+ const formData = new FormData();
89
+ formData.append("repository_id", repositoryId);
90
+ formData.append("branch", branch);
91
+ formData.append("type", type);
92
+ formData.append("file", fs.createReadStream(file));
93
+
94
+ return (
95
+ await axios.post(
96
+ `${baseUrl}/api/services/artifacts`,
97
+ formData,
98
+ {
99
+ headers: {
100
+ ...headers.headers,
101
+ ...formData.getHeaders(),
102
+ },
103
+ },
104
+ )
105
+ ).data.data;
106
+ } catch (e) {
107
+ console.log(
108
+ `Deployment Hub: Error storing artifact in hub: ${e.message}`,
109
+ );
110
+ process.exit(1);
111
+ }
112
+ },
113
+ async purgeArtifact(artifactId) {
114
+ try {
115
+ await axios.delete(
116
+ `${baseUrl}/api/services/artifacts/${artifactId}`,
117
+ headers,
118
+ );
119
+ } catch (e) {
120
+ console.log(e.message);
121
+ process.exit(1);
122
+ }
123
+ },
124
+ async getConfigurations(repositoryId) {
125
+ try {
126
+ return (
127
+ await axios.get(`${baseUrl}/api/services/configurations`, {
128
+ ...headers,
129
+ ...{
130
+ params: {
131
+ "filter[repository_id]": repositoryId,
132
+ },
133
+ },
134
+ })
135
+ ).data.data;
136
+ } catch (e) {
137
+ console.log(e.message);
138
+ process.exit(1);
139
+ }
140
+ },
141
+ async findRepository(remoteUrl) {
142
+ try {
143
+ return (
144
+ await axios.post(
145
+ `${baseUrl}/api/services/repositories/find-or-create`,
146
+ {
147
+ remote_url: remoteUrl,
148
+ },
149
+ headers,
150
+ )
151
+ ).data.data;
152
+ } catch (e) {
153
+ console.log(
154
+ `Deployment Hub: Error finding repository: ${e.message}`,
155
+ );
156
+ process.exit(1);
157
+ }
158
+ },
159
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-env",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0",
4
4
  "description": "Docker manager for FW",
5
5
  "main": "cli.js",
6
6
  "scripts": {
@@ -29,6 +29,8 @@
29
29
  "apache-md5": "^1.1.8",
30
30
  "axios": "^0.21.4",
31
31
  "chalk": "4.1.0",
32
+ "dotenv": "^17.2.3",
33
+ "form-data": "^4.0.4",
32
34
  "fs-extra": "^11.1.1",
33
35
  "generate-password": "^1.7.0",
34
36
  "get-port": "5.1.1",