@kapeta/local-cluster-service 0.19.1 → 0.19.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.19.3](https://github.com/kapetacom/local-cluster-service/compare/v0.19.2...v0.19.3) (2023-09-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * if codegen is already started for block - run after ([#69](https://github.com/kapetacom/local-cluster-service/issues/69)) ([a1b13ee](https://github.com/kapetacom/local-cluster-service/commit/a1b13eeab8413be8c165419ee36c26e3509338b4))
7
+
8
+ ## [0.19.2](https://github.com/kapetacom/local-cluster-service/compare/v0.19.1...v0.19.2) (2023-09-05)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Control boot order and reload code gen targets when repo changes ([#68](https://github.com/kapetacom/local-cluster-service/issues/68)) ([5557259](https://github.com/kapetacom/local-cluster-service/commit/5557259dda10f509a2b8cc45c0b4a4631e1d975a))
14
+
1
15
  ## [0.19.1](https://github.com/kapetacom/local-cluster-service/compare/v0.19.0...v0.19.1) (2023-09-03)
2
16
 
3
17
 
package/dist/cjs/index.js CHANGED
@@ -28,6 +28,7 @@ const repositoryManager_1 = require("./src/repositoryManager");
28
28
  const commandLineUtils_1 = require("./src/utils/commandLineUtils");
29
29
  const DefaultProviderInstaller_1 = require("./src/utils/DefaultProviderInstaller");
30
30
  const authManager_1 = require("./src/authManager");
31
+ const codeGeneratorManager_1 = require("./src/codeGeneratorManager");
31
32
  let currentServer = null;
32
33
  function createServer() {
33
34
  const app = (0, express_1.default)();
@@ -109,6 +110,7 @@ exports.default = {
109
110
  console.error('Could not ping docker runtime: ' + e.toString() + '. Make sure docker is running and working.');
110
111
  }
111
112
  await DefaultProviderInstaller_1.defaultProviderInstaller.checkForDefault();
113
+ await codeGeneratorManager_1.codeGeneratorManager.initialize();
112
114
  const clusterPort = storageService_1.storageService.get('cluster', 'port');
113
115
  if (clusterPort) {
114
116
  clusterService_1.clusterService.setClusterServicePort(clusterPort);
@@ -1,5 +1,7 @@
1
+ /// <reference types="node" />
1
2
  import { SourceOfChange } from './types';
2
- export declare class RepositoryWatcher {
3
+ import { EventEmitter } from 'node:events';
4
+ export declare class RepositoryWatcher extends EventEmitter {
3
5
  private watcher?;
4
6
  private disabled;
5
7
  private readonly baseDir;
@@ -13,8 +13,9 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
13
13
  const lodash_1 = __importDefault(require("lodash"));
14
14
  const socketManager_1 = require("./socketManager");
15
15
  const cacheManager_1 = require("./cacheManager");
16
+ const node_events_1 = require("node:events");
16
17
  const KAPETA_YML_RX = /^kapeta.ya?ml$/;
17
- class RepositoryWatcher {
18
+ class RepositoryWatcher extends node_events_1.EventEmitter {
18
19
  watcher;
19
20
  disabled = false;
20
21
  baseDir;
@@ -22,6 +23,7 @@ class RepositoryWatcher {
22
23
  symbolicLinks = {};
23
24
  sourceOfChange = new Map();
24
25
  constructor() {
26
+ super();
25
27
  this.baseDir = local_cluster_config_1.default.getRepositoryBasedir();
26
28
  }
27
29
  setDisabled(disabled) {
@@ -204,6 +206,7 @@ class RepositoryWatcher {
204
206
  this.allDefinitions = newDefinitions;
205
207
  //console.log('Asset changed', payload);
206
208
  socketManager_1.socketManager.emitGlobal('asset-change', payload);
209
+ this.emit('change', payload);
207
210
  cacheManager_1.cacheManager.flush();
208
211
  }
209
212
  async exists(path) {
@@ -234,8 +237,13 @@ class RepositoryWatcher {
234
237
  try {
235
238
  // Make sure we're not watching the symlink target
236
239
  await this.removeSymlinkTarget(path);
237
- const stat = await fs_extra_1.default.lstat(path);
238
- if (stat.isSymbolicLink()) {
240
+ let symbolicLink = false;
241
+ try {
242
+ const stat = await fs_extra_1.default.lstat(path);
243
+ symbolicLink = stat.isSymbolicLink();
244
+ }
245
+ catch (e) { }
246
+ if (symbolicLink) {
239
247
  const realPath = `${await fs_extra_1.default.realpath(path)}/kapeta.yml`;
240
248
  if (await this.exists(realPath)) {
241
249
  //console.log('Watching symlink target %s => %s', path, realPath);
@@ -16,6 +16,7 @@ const definitionsManager_1 = require("./definitionsManager");
16
16
  const utils_1 = require("./utils/utils");
17
17
  const taskManager_1 = require("./taskManager");
18
18
  const cacheManager_1 = require("./cacheManager");
19
+ const node_uuid_1 = __importDefault(require("node-uuid"));
19
20
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
20
21
  const toKey = (ref) => `assetManager:asset:${ref}`;
21
22
  function enrichAsset(asset) {
@@ -180,10 +181,14 @@ class AssetManager {
180
181
  ref = (0, utils_1.normalizeKapetaUri)(ref);
181
182
  if (await codeGeneratorManager_1.codeGeneratorManager.canGenerateCode(block)) {
182
183
  const assetTitle = block.metadata.title ? block.metadata.title : (0, nodejs_utils_1.parseKapetaUri)(block.metadata.name).name;
183
- taskManager_1.taskManager.add(`codegen:${ref}`, async () => {
184
+ const taskId = `codegen:${node_uuid_1.default.v4()}`;
185
+ const group = `codegen:${ref}`;
186
+ // We group the codegen tasks since we want to run them all but only 1 at a time per block
187
+ taskManager_1.taskManager.add(taskId, async () => {
184
188
  await codeGeneratorManager_1.codeGeneratorManager.generate(ymlPath, block);
185
189
  }, {
186
190
  name: `Generating code for ${assetTitle}`,
191
+ group, //Group prevents multiple tasks from running at the same time
187
192
  });
188
193
  return true;
189
194
  }
@@ -1,6 +1,8 @@
1
1
  import { BlockDefinition } from '@kapeta/schemas';
2
2
  declare class CodeGeneratorManager {
3
+ private ensureLanguageTargetInRegistry;
3
4
  reload(): Promise<void>;
5
+ initialize(): Promise<void>;
4
6
  canGenerateCode(yamlContent: BlockDefinition): Promise<boolean>;
5
7
  generate(yamlFile: string, yamlContent: BlockDefinition): Promise<void>;
6
8
  }
@@ -7,27 +7,51 @@ exports.codeGeneratorManager = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const codegen_1 = require("@kapeta/codegen");
9
9
  const definitionsManager_1 = require("./definitionsManager");
10
+ const assetManager_1 = require("./assetManager");
11
+ const utils_1 = require("./utils/utils");
12
+ const repositoryManager_1 = require("./repositoryManager");
10
13
  const TARGET_KIND = 'core/language-target';
11
14
  const BLOCK_TYPE_KIND = 'core/block-type';
12
15
  class CodeGeneratorManager {
16
+ async ensureLanguageTargetInRegistry(path, version, definition) {
17
+ const key = `${definition.metadata.name}:${version}`;
18
+ try {
19
+ if (await codegen_1.registry.get(key)) {
20
+ return;
21
+ }
22
+ }
23
+ catch (e) { }
24
+ try {
25
+ const target = require(path);
26
+ if (target.default) {
27
+ codegen_1.registry.register(key, target.default);
28
+ }
29
+ else {
30
+ codegen_1.registry.register(key, target);
31
+ }
32
+ }
33
+ catch (e) {
34
+ console.error('Failed to load target: %s', key, e);
35
+ }
36
+ }
13
37
  async reload() {
14
38
  codegen_1.registry.reset();
15
39
  const languageTargets = await definitionsManager_1.definitionsManager.getDefinitions(TARGET_KIND);
16
40
  for (const languageTarget of languageTargets) {
17
- const key = `${languageTarget.definition.metadata.name}:${languageTarget.version}`;
41
+ await this.ensureLanguageTargetInRegistry(languageTarget.path, languageTarget.version, languageTarget.definition);
42
+ }
43
+ }
44
+ async initialize() {
45
+ await this.reload();
46
+ repositoryManager_1.repositoryManager.on('change', async () => {
47
+ // Reload code generators when the repository changes
18
48
  try {
19
- const target = require(languageTarget.path);
20
- if (target.default) {
21
- codegen_1.registry.register(key, target.default);
22
- }
23
- else {
24
- codegen_1.registry.register(key, target);
25
- }
49
+ await this.reload();
26
50
  }
27
51
  catch (e) {
28
- console.error('Failed to load target: %s', key, e);
52
+ console.error('Failed to reload code generators', e);
29
53
  }
30
- }
54
+ });
31
55
  }
32
56
  async canGenerateCode(yamlContent) {
33
57
  if (!yamlContent.spec.target?.kind) {
@@ -39,6 +63,18 @@ class CodeGeneratorManager {
39
63
  return !!(yamlContent && yamlContent.kind && blockTypeKinds.indexOf(yamlContent.kind.toLowerCase()) > -1);
40
64
  }
41
65
  async generate(yamlFile, yamlContent) {
66
+ if (!yamlContent.spec.target?.kind) {
67
+ //Not all block types have targets
68
+ return;
69
+ }
70
+ const targetRef = (0, utils_1.normalizeKapetaUri)(yamlContent.spec.target?.kind);
71
+ // Automatically downloads target if not available
72
+ const targetAsset = await assetManager_1.assetManager.getAsset(targetRef);
73
+ if (!targetAsset) {
74
+ console.error('Language target not found: %s', yamlContent.spec.target?.kind);
75
+ return;
76
+ }
77
+ await this.ensureLanguageTargetInRegistry(targetAsset?.path, targetAsset?.version, targetAsset?.data);
42
78
  const baseDir = path_1.default.dirname(yamlFile);
43
79
  console.log('Generating code for path: %s', baseDir);
44
80
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
@@ -50,4 +86,3 @@ class CodeGeneratorManager {
50
86
  }
51
87
  }
52
88
  exports.codeGeneratorManager = new CodeGeneratorManager();
53
- exports.codeGeneratorManager.reload();
@@ -1,6 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import { Task } from './taskManager';
2
3
  import { SourceOfChange } from './types';
3
- declare class RepositoryManager {
4
+ import { EventEmitter } from 'node:events';
5
+ declare class RepositoryManager extends EventEmitter {
4
6
  private _registryService;
5
7
  private watcher;
6
8
  constructor();
@@ -13,6 +13,7 @@ const utils_1 = require("./utils/utils");
13
13
  const progressListener_1 = require("./progressListener");
14
14
  const RepositoryWatcher_1 = require("./RepositoryWatcher");
15
15
  const cacheManager_1 = require("./cacheManager");
16
+ const node_events_1 = require("node:events");
16
17
  const EVENT_DEFAULT_PROVIDERS_START = 'default-providers-start';
17
18
  const EVENT_DEFAULT_PROVIDERS_END = 'default-providers-end';
18
19
  const DEFAULT_PROVIDERS = [
@@ -29,13 +30,17 @@ const DEFAULT_PROVIDERS = [
29
30
  'kapeta/language-target-nodejs',
30
31
  'kapeta/language-target-java-spring-boot',
31
32
  ];
32
- class RepositoryManager {
33
+ class RepositoryManager extends node_events_1.EventEmitter {
33
34
  _registryService;
34
35
  watcher;
35
36
  constructor() {
37
+ super();
36
38
  this._registryService = new nodejs_registry_utils_1.RegistryService(nodejs_registry_utils_1.Config.data.registry.url);
37
39
  this.watcher = new RepositoryWatcher_1.RepositoryWatcher();
38
40
  this.listenForChanges();
41
+ this.watcher.on('change', (file, source) => {
42
+ this.emit('change', file, source);
43
+ });
39
44
  }
40
45
  listenForChanges() {
41
46
  this.watcher.watch();
package/dist/esm/index.js CHANGED
@@ -28,6 +28,7 @@ const repositoryManager_1 = require("./src/repositoryManager");
28
28
  const commandLineUtils_1 = require("./src/utils/commandLineUtils");
29
29
  const DefaultProviderInstaller_1 = require("./src/utils/DefaultProviderInstaller");
30
30
  const authManager_1 = require("./src/authManager");
31
+ const codeGeneratorManager_1 = require("./src/codeGeneratorManager");
31
32
  let currentServer = null;
32
33
  function createServer() {
33
34
  const app = (0, express_1.default)();
@@ -109,6 +110,7 @@ exports.default = {
109
110
  console.error('Could not ping docker runtime: ' + e.toString() + '. Make sure docker is running and working.');
110
111
  }
111
112
  await DefaultProviderInstaller_1.defaultProviderInstaller.checkForDefault();
113
+ await codeGeneratorManager_1.codeGeneratorManager.initialize();
112
114
  const clusterPort = storageService_1.storageService.get('cluster', 'port');
113
115
  if (clusterPort) {
114
116
  clusterService_1.clusterService.setClusterServicePort(clusterPort);
@@ -1,5 +1,7 @@
1
+ /// <reference types="node" />
1
2
  import { SourceOfChange } from './types';
2
- export declare class RepositoryWatcher {
3
+ import { EventEmitter } from 'node:events';
4
+ export declare class RepositoryWatcher extends EventEmitter {
3
5
  private watcher?;
4
6
  private disabled;
5
7
  private readonly baseDir;
@@ -13,8 +13,9 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
13
13
  const lodash_1 = __importDefault(require("lodash"));
14
14
  const socketManager_1 = require("./socketManager");
15
15
  const cacheManager_1 = require("./cacheManager");
16
+ const node_events_1 = require("node:events");
16
17
  const KAPETA_YML_RX = /^kapeta.ya?ml$/;
17
- class RepositoryWatcher {
18
+ class RepositoryWatcher extends node_events_1.EventEmitter {
18
19
  watcher;
19
20
  disabled = false;
20
21
  baseDir;
@@ -22,6 +23,7 @@ class RepositoryWatcher {
22
23
  symbolicLinks = {};
23
24
  sourceOfChange = new Map();
24
25
  constructor() {
26
+ super();
25
27
  this.baseDir = local_cluster_config_1.default.getRepositoryBasedir();
26
28
  }
27
29
  setDisabled(disabled) {
@@ -204,6 +206,7 @@ class RepositoryWatcher {
204
206
  this.allDefinitions = newDefinitions;
205
207
  //console.log('Asset changed', payload);
206
208
  socketManager_1.socketManager.emitGlobal('asset-change', payload);
209
+ this.emit('change', payload);
207
210
  cacheManager_1.cacheManager.flush();
208
211
  }
209
212
  async exists(path) {
@@ -234,8 +237,13 @@ class RepositoryWatcher {
234
237
  try {
235
238
  // Make sure we're not watching the symlink target
236
239
  await this.removeSymlinkTarget(path);
237
- const stat = await fs_extra_1.default.lstat(path);
238
- if (stat.isSymbolicLink()) {
240
+ let symbolicLink = false;
241
+ try {
242
+ const stat = await fs_extra_1.default.lstat(path);
243
+ symbolicLink = stat.isSymbolicLink();
244
+ }
245
+ catch (e) { }
246
+ if (symbolicLink) {
239
247
  const realPath = `${await fs_extra_1.default.realpath(path)}/kapeta.yml`;
240
248
  if (await this.exists(realPath)) {
241
249
  //console.log('Watching symlink target %s => %s', path, realPath);
@@ -16,6 +16,7 @@ const definitionsManager_1 = require("./definitionsManager");
16
16
  const utils_1 = require("./utils/utils");
17
17
  const taskManager_1 = require("./taskManager");
18
18
  const cacheManager_1 = require("./cacheManager");
19
+ const node_uuid_1 = __importDefault(require("node-uuid"));
19
20
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
20
21
  const toKey = (ref) => `assetManager:asset:${ref}`;
21
22
  function enrichAsset(asset) {
@@ -180,10 +181,14 @@ class AssetManager {
180
181
  ref = (0, utils_1.normalizeKapetaUri)(ref);
181
182
  if (await codeGeneratorManager_1.codeGeneratorManager.canGenerateCode(block)) {
182
183
  const assetTitle = block.metadata.title ? block.metadata.title : (0, nodejs_utils_1.parseKapetaUri)(block.metadata.name).name;
183
- taskManager_1.taskManager.add(`codegen:${ref}`, async () => {
184
+ const taskId = `codegen:${node_uuid_1.default.v4()}`;
185
+ const group = `codegen:${ref}`;
186
+ // We group the codegen tasks since we want to run them all but only 1 at a time per block
187
+ taskManager_1.taskManager.add(taskId, async () => {
184
188
  await codeGeneratorManager_1.codeGeneratorManager.generate(ymlPath, block);
185
189
  }, {
186
190
  name: `Generating code for ${assetTitle}`,
191
+ group, //Group prevents multiple tasks from running at the same time
187
192
  });
188
193
  return true;
189
194
  }
@@ -1,6 +1,8 @@
1
1
  import { BlockDefinition } from '@kapeta/schemas';
2
2
  declare class CodeGeneratorManager {
3
+ private ensureLanguageTargetInRegistry;
3
4
  reload(): Promise<void>;
5
+ initialize(): Promise<void>;
4
6
  canGenerateCode(yamlContent: BlockDefinition): Promise<boolean>;
5
7
  generate(yamlFile: string, yamlContent: BlockDefinition): Promise<void>;
6
8
  }
@@ -7,27 +7,51 @@ exports.codeGeneratorManager = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const codegen_1 = require("@kapeta/codegen");
9
9
  const definitionsManager_1 = require("./definitionsManager");
10
+ const assetManager_1 = require("./assetManager");
11
+ const utils_1 = require("./utils/utils");
12
+ const repositoryManager_1 = require("./repositoryManager");
10
13
  const TARGET_KIND = 'core/language-target';
11
14
  const BLOCK_TYPE_KIND = 'core/block-type';
12
15
  class CodeGeneratorManager {
16
+ async ensureLanguageTargetInRegistry(path, version, definition) {
17
+ const key = `${definition.metadata.name}:${version}`;
18
+ try {
19
+ if (await codegen_1.registry.get(key)) {
20
+ return;
21
+ }
22
+ }
23
+ catch (e) { }
24
+ try {
25
+ const target = require(path);
26
+ if (target.default) {
27
+ codegen_1.registry.register(key, target.default);
28
+ }
29
+ else {
30
+ codegen_1.registry.register(key, target);
31
+ }
32
+ }
33
+ catch (e) {
34
+ console.error('Failed to load target: %s', key, e);
35
+ }
36
+ }
13
37
  async reload() {
14
38
  codegen_1.registry.reset();
15
39
  const languageTargets = await definitionsManager_1.definitionsManager.getDefinitions(TARGET_KIND);
16
40
  for (const languageTarget of languageTargets) {
17
- const key = `${languageTarget.definition.metadata.name}:${languageTarget.version}`;
41
+ await this.ensureLanguageTargetInRegistry(languageTarget.path, languageTarget.version, languageTarget.definition);
42
+ }
43
+ }
44
+ async initialize() {
45
+ await this.reload();
46
+ repositoryManager_1.repositoryManager.on('change', async () => {
47
+ // Reload code generators when the repository changes
18
48
  try {
19
- const target = require(languageTarget.path);
20
- if (target.default) {
21
- codegen_1.registry.register(key, target.default);
22
- }
23
- else {
24
- codegen_1.registry.register(key, target);
25
- }
49
+ await this.reload();
26
50
  }
27
51
  catch (e) {
28
- console.error('Failed to load target: %s', key, e);
52
+ console.error('Failed to reload code generators', e);
29
53
  }
30
- }
54
+ });
31
55
  }
32
56
  async canGenerateCode(yamlContent) {
33
57
  if (!yamlContent.spec.target?.kind) {
@@ -39,6 +63,18 @@ class CodeGeneratorManager {
39
63
  return !!(yamlContent && yamlContent.kind && blockTypeKinds.indexOf(yamlContent.kind.toLowerCase()) > -1);
40
64
  }
41
65
  async generate(yamlFile, yamlContent) {
66
+ if (!yamlContent.spec.target?.kind) {
67
+ //Not all block types have targets
68
+ return;
69
+ }
70
+ const targetRef = (0, utils_1.normalizeKapetaUri)(yamlContent.spec.target?.kind);
71
+ // Automatically downloads target if not available
72
+ const targetAsset = await assetManager_1.assetManager.getAsset(targetRef);
73
+ if (!targetAsset) {
74
+ console.error('Language target not found: %s', yamlContent.spec.target?.kind);
75
+ return;
76
+ }
77
+ await this.ensureLanguageTargetInRegistry(targetAsset?.path, targetAsset?.version, targetAsset?.data);
42
78
  const baseDir = path_1.default.dirname(yamlFile);
43
79
  console.log('Generating code for path: %s', baseDir);
44
80
  const codeGenerator = new codegen_1.BlockCodeGenerator(yamlContent);
@@ -50,4 +86,3 @@ class CodeGeneratorManager {
50
86
  }
51
87
  }
52
88
  exports.codeGeneratorManager = new CodeGeneratorManager();
53
- exports.codeGeneratorManager.reload();
@@ -1,6 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import { Task } from './taskManager';
2
3
  import { SourceOfChange } from './types';
3
- declare class RepositoryManager {
4
+ import { EventEmitter } from 'node:events';
5
+ declare class RepositoryManager extends EventEmitter {
4
6
  private _registryService;
5
7
  private watcher;
6
8
  constructor();
@@ -13,6 +13,7 @@ const utils_1 = require("./utils/utils");
13
13
  const progressListener_1 = require("./progressListener");
14
14
  const RepositoryWatcher_1 = require("./RepositoryWatcher");
15
15
  const cacheManager_1 = require("./cacheManager");
16
+ const node_events_1 = require("node:events");
16
17
  const EVENT_DEFAULT_PROVIDERS_START = 'default-providers-start';
17
18
  const EVENT_DEFAULT_PROVIDERS_END = 'default-providers-end';
18
19
  const DEFAULT_PROVIDERS = [
@@ -29,13 +30,17 @@ const DEFAULT_PROVIDERS = [
29
30
  'kapeta/language-target-nodejs',
30
31
  'kapeta/language-target-java-spring-boot',
31
32
  ];
32
- class RepositoryManager {
33
+ class RepositoryManager extends node_events_1.EventEmitter {
33
34
  _registryService;
34
35
  watcher;
35
36
  constructor() {
37
+ super();
36
38
  this._registryService = new nodejs_registry_utils_1.RegistryService(nodejs_registry_utils_1.Config.data.registry.url);
37
39
  this.watcher = new RepositoryWatcher_1.RepositoryWatcher();
38
40
  this.listenForChanges();
41
+ this.watcher.on('change', (file, source) => {
42
+ this.emit('change', file, source);
43
+ });
39
44
  }
40
45
  listenForChanges() {
41
46
  this.watcher.watch();
package/index.ts CHANGED
@@ -24,6 +24,7 @@ import { repositoryManager } from './src/repositoryManager';
24
24
  import { ensureCLI } from './src/utils/commandLineUtils';
25
25
  import { defaultProviderInstaller } from './src/utils/DefaultProviderInstaller';
26
26
  import { authManager } from './src/authManager';
27
+ import { codeGeneratorManager } from './src/codeGeneratorManager';
27
28
 
28
29
  export type LocalClusterService = HTTP.Server & { host?: string; port?: number };
29
30
 
@@ -125,6 +126,7 @@ export default {
125
126
  }
126
127
 
127
128
  await defaultProviderInstaller.checkForDefault();
129
+ await codeGeneratorManager.initialize();
128
130
 
129
131
  const clusterPort = storageService.get('cluster', 'port');
130
132
  if (clusterPort) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.19.1",
3
+ "version": "0.19.3",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -8,6 +8,7 @@ import _ from 'lodash';
8
8
  import { socketManager } from './socketManager';
9
9
  import { SourceOfChange, WatchEventName } from './types';
10
10
  import { cacheManager } from './cacheManager';
11
+ import { EventEmitter } from 'node:events';
11
12
 
12
13
  interface AssetIdentity {
13
14
  handle: string;
@@ -15,7 +16,7 @@ interface AssetIdentity {
15
16
  version: string;
16
17
  }
17
18
  const KAPETA_YML_RX = /^kapeta.ya?ml$/;
18
- export class RepositoryWatcher {
19
+ export class RepositoryWatcher extends EventEmitter {
19
20
  private watcher?: FSWatcher;
20
21
  private disabled: boolean = false;
21
22
  private readonly baseDir: string;
@@ -23,6 +24,7 @@ export class RepositoryWatcher {
23
24
  private symbolicLinks: { [link: string]: string } = {};
24
25
  private sourceOfChange: Map<string, SourceOfChange> = new Map();
25
26
  constructor() {
27
+ super();
26
28
  this.baseDir = ClusterConfiguration.getRepositoryBasedir();
27
29
  }
28
30
 
@@ -228,6 +230,7 @@ export class RepositoryWatcher {
228
230
 
229
231
  //console.log('Asset changed', payload);
230
232
  socketManager.emitGlobal('asset-change', payload);
233
+ this.emit('change', payload);
231
234
 
232
235
  cacheManager.flush();
233
236
  }
@@ -261,8 +264,13 @@ export class RepositoryWatcher {
261
264
  try {
262
265
  // Make sure we're not watching the symlink target
263
266
  await this.removeSymlinkTarget(path);
264
- const stat = await FS.lstat(path);
265
- if (stat.isSymbolicLink()) {
267
+ let symbolicLink = false;
268
+ try {
269
+ const stat = await FS.lstat(path);
270
+ symbolicLink = stat.isSymbolicLink();
271
+ } catch (e) {}
272
+
273
+ if (symbolicLink) {
266
274
  const realPath = `${await FS.realpath(path)}/kapeta.yml`;
267
275
  if (await this.exists(realPath)) {
268
276
  //console.log('Watching symlink target %s => %s', path, realPath);
@@ -13,6 +13,7 @@ import { normalizeKapetaUri } from './utils/utils';
13
13
  import { taskManager } from './taskManager';
14
14
  import { SourceOfChange } from './types';
15
15
  import { cacheManager } from './cacheManager';
16
+ import uuid from 'node-uuid';
16
17
 
17
18
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
18
19
 
@@ -242,13 +243,17 @@ class AssetManager {
242
243
  ref = normalizeKapetaUri(ref);
243
244
  if (await codeGeneratorManager.canGenerateCode(block)) {
244
245
  const assetTitle = block.metadata.title ? block.metadata.title : parseKapetaUri(block.metadata.name).name;
246
+ const taskId = `codegen:${uuid.v4()}`;
247
+ const group = `codegen:${ref}`;
248
+ // We group the codegen tasks since we want to run them all but only 1 at a time per block
245
249
  taskManager.add(
246
- `codegen:${ref}`,
250
+ taskId,
247
251
  async () => {
248
252
  await codeGeneratorManager.generate(ymlPath, block);
249
253
  },
250
254
  {
251
255
  name: `Generating code for ${assetTitle}`,
256
+ group, //Group prevents multiple tasks from running at the same time
252
257
  }
253
258
  );
254
259
  return true;
@@ -1,29 +1,58 @@
1
1
  import Path from 'path';
2
2
  import { registry as Targets, BlockCodeGenerator, CodeWriter } from '@kapeta/codegen';
3
- import ClusterConfiguration from '@kapeta/local-cluster-config';
4
3
  import { BlockDefinition } from '@kapeta/schemas';
5
4
  import { definitionsManager } from './definitionsManager';
5
+ import { Definition } from '@kapeta/local-cluster-config';
6
+ import { assetManager } from './assetManager';
7
+ import { normalizeKapetaUri } from './utils/utils';
8
+ import { repositoryManager } from './repositoryManager';
6
9
 
7
10
  const TARGET_KIND = 'core/language-target';
8
11
  const BLOCK_TYPE_KIND = 'core/block-type';
9
12
 
10
13
  class CodeGeneratorManager {
14
+ private async ensureLanguageTargetInRegistry(path: string, version: string, definition: Definition) {
15
+ const key = `${definition.metadata.name}:${version}`;
16
+
17
+ try {
18
+ if (await Targets.get(key)) {
19
+ return;
20
+ }
21
+ } catch (e) {}
22
+
23
+ try {
24
+ const target = require(path);
25
+ if (target.default) {
26
+ Targets.register(key, target.default);
27
+ } else {
28
+ Targets.register(key, target);
29
+ }
30
+ } catch (e) {
31
+ console.error('Failed to load target: %s', key, e);
32
+ }
33
+ }
11
34
  async reload() {
12
35
  Targets.reset();
13
36
  const languageTargets = await definitionsManager.getDefinitions(TARGET_KIND);
14
37
  for (const languageTarget of languageTargets) {
15
- const key = `${languageTarget.definition.metadata.name}:${languageTarget.version}`;
38
+ await this.ensureLanguageTargetInRegistry(
39
+ languageTarget.path,
40
+ languageTarget.version,
41
+ languageTarget.definition
42
+ );
43
+ }
44
+ }
45
+
46
+ async initialize() {
47
+ await this.reload();
48
+ repositoryManager.on('change', async () => {
49
+ // Reload code generators when the repository changes
16
50
  try {
17
- const target = require(languageTarget.path);
18
- if (target.default) {
19
- Targets.register(key, target.default);
20
- } else {
21
- Targets.register(key, target);
22
- }
51
+ await this.reload();
23
52
  } catch (e) {
24
- console.error('Failed to load target: %s', key, e);
53
+ console.error('Failed to reload code generators', e);
25
54
  }
26
- }
55
+ });
27
56
  }
28
57
 
29
58
  async canGenerateCode(yamlContent: BlockDefinition): Promise<boolean> {
@@ -40,6 +69,22 @@ class CodeGeneratorManager {
40
69
  }
41
70
 
42
71
  async generate(yamlFile: string, yamlContent: BlockDefinition) {
72
+ if (!yamlContent.spec.target?.kind) {
73
+ //Not all block types have targets
74
+ return;
75
+ }
76
+
77
+ const targetRef = normalizeKapetaUri(yamlContent.spec.target?.kind);
78
+
79
+ // Automatically downloads target if not available
80
+ const targetAsset = await assetManager.getAsset(targetRef);
81
+
82
+ if (!targetAsset) {
83
+ console.error('Language target not found: %s', yamlContent.spec.target?.kind);
84
+ return;
85
+ }
86
+
87
+ await this.ensureLanguageTargetInRegistry(targetAsset?.path, targetAsset?.version, targetAsset?.data);
43
88
  const baseDir = Path.dirname(yamlFile);
44
89
  console.log('Generating code for path: %s', baseDir);
45
90
  const codeGenerator = new BlockCodeGenerator(yamlContent);
@@ -55,4 +100,3 @@ class CodeGeneratorManager {
55
100
  }
56
101
 
57
102
  export const codeGeneratorManager = new CodeGeneratorManager();
58
- codeGeneratorManager.reload();
@@ -9,6 +9,7 @@ import { ProgressListener } from './progressListener';
9
9
  import { RepositoryWatcher } from './RepositoryWatcher';
10
10
  import { SourceOfChange } from './types';
11
11
  import { cacheManager } from './cacheManager';
12
+ import { EventEmitter } from 'node:events';
12
13
 
13
14
  const EVENT_DEFAULT_PROVIDERS_START = 'default-providers-start';
14
15
  const EVENT_DEFAULT_PROVIDERS_END = 'default-providers-end';
@@ -28,14 +29,19 @@ const DEFAULT_PROVIDERS = [
28
29
  'kapeta/language-target-java-spring-boot',
29
30
  ];
30
31
 
31
- class RepositoryManager {
32
+ class RepositoryManager extends EventEmitter {
32
33
  private _registryService: RegistryService;
33
34
  private watcher: RepositoryWatcher;
34
35
 
35
36
  constructor() {
37
+ super();
36
38
  this._registryService = new RegistryService(Config.data.registry.url);
37
39
  this.watcher = new RepositoryWatcher();
38
40
  this.listenForChanges();
41
+
42
+ this.watcher.on('change', (file: string, source: SourceOfChange) => {
43
+ this.emit('change', file, source);
44
+ });
39
45
  }
40
46
 
41
47
  listenForChanges() {