-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from pfongkye/feat/plugin-manager
feat(plugin): add simple plugin manager for release
- Loading branch information
Showing
27 changed files
with
439 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Plugin System for Custom Release Manager | ||
|
||
## Introduction | ||
|
||
This document provides instructions on how to use the plugin system to add your own release manager to the Homer project. | ||
You can create a project containing a `plugins/release` directory which will contain your release manager and build a new Docker image where `plugins/release` will be copied to `dist/plugins`. | ||
|
||
## Steps to Add a Custom Release Manager | ||
|
||
1. **Create a New Release Manager** | ||
|
||
- Navigate to the `plugins/release` directory in your Homer project. | ||
- Create `myOwnReleaseManager.js` and implement the logic(for now the types are not available as a standalone package but you can have the interface [here](./src/release/typings/ReleaseManager.ts)). | ||
- You have an implementation example with [defaultReleaseManager](./plugins/release/defaultReleaseManager.ts) | ||
|
||
2. **Register the Plugin** | ||
|
||
- If you have a project which needs the custom release manager, declare the project in the configuration [file](./config/homer/projects.json) | ||
- Add an entry for your project (the name of the release manager must correspond to its file name). | ||
```json | ||
{ | ||
"description": "project_example", | ||
"notificationChannelIds": ["C0XXXXXXXXX"], | ||
"projectId": 1234, | ||
"releaseChannelId": "C0XXXXXXXXX", | ||
"releaseManager": "myOwnReleaseManager", | ||
"releaseTagManager": "stableDateReleaseTagManager" | ||
} | ||
``` | ||
|
||
3. **Deploy** | ||
- Build the plugins and config with Homer project before deploying the application. | ||
|
||
## Conclusion | ||
|
||
By following these steps, you can extend the Homer project with your own custom release manager using the plugin system. | ||
Please only **add your own release managers or managers from trusted sources** to minimize security breaches in your Homer Slack application. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import ReleasePluginManager from '@/core/pluginManager/ReleasePluginManager'; | ||
import { semanticReleaseTagManager } from '@/release/commands/create/managers/semanticReleaseTagManager'; | ||
import defaultReleaseManager from '@root/plugins/release/defaultReleaseManager'; | ||
|
||
describe('pluginManager', () => { | ||
beforeAll(async () => { | ||
// Due to singleton pattern, the release manager may already be loaded during test launch | ||
if (!ReleasePluginManager.getReleaseManager('defaultReleaseManager')) { | ||
await ReleasePluginManager.loadReleaseManagerPlugin( | ||
'@root/plugins/release/defaultReleaseManager' | ||
); | ||
} | ||
}); | ||
it('should throw an error if the plugin is not found', async () => { | ||
await expect(async () => | ||
ReleasePluginManager.loadReleaseManagerPlugin('invalidPath') | ||
).rejects.toThrow( | ||
'Cannot load release manager plugin. Invalid path or plugin already loaded.' | ||
); | ||
}); | ||
it('should throw an error if the plugin is already added', async () => { | ||
await expect(async () => | ||
ReleasePluginManager.loadReleaseManagerPlugin( | ||
'@root/plugins/release/defaultReleaseManager' | ||
) | ||
).rejects.toThrow( | ||
'Cannot load release manager plugin. Invalid path or plugin already loaded.' | ||
); | ||
}); | ||
it('should return release manager by name', async () => { | ||
expect( | ||
ReleasePluginManager.getReleaseManager('defaultReleaseManager') | ||
).toEqual(defaultReleaseManager); | ||
}); | ||
it('should return a provided release manager', async () => { | ||
expect( | ||
ReleasePluginManager.getReleaseManager('defaultReleaseManager') | ||
).toEqual(defaultReleaseManager); | ||
}); | ||
it('should return release tag manager by name', async () => { | ||
expect( | ||
ReleasePluginManager.getReleaseTagManager('semanticReleaseTagManager') | ||
).toEqual(semanticReleaseTagManager); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { federationReleaseTagManager } from '@/release/commands/create/managers/federationReleaseTagManager'; | ||
import { semanticReleaseTagManager } from '@/release/commands/create/managers/semanticReleaseTagManager'; | ||
import { stableDateReleaseTagManager } from '@/release/commands/create/managers/stableDateReleaseTagManager'; | ||
import type { ReleaseManager } from '@/release/typings/ReleaseManager'; | ||
import type { ReleaseTagManager } from '@/release/typings/ReleaseTagManager'; | ||
|
||
export default class ReleasePluginManager { | ||
// singleton to load release and release tag managers | ||
private static instance: ReleasePluginManager = new ReleasePluginManager(); | ||
private readonly releaseManagers: Map<string, ReleaseManager> = new Map(); | ||
private readonly releaseTagManagers: Map<string, ReleaseTagManager> = | ||
new Map(); | ||
|
||
private constructor() { | ||
// provided release tag managers | ||
this.releaseTagManagers.set( | ||
'semanticReleaseTagManager', | ||
semanticReleaseTagManager | ||
); | ||
this.releaseTagManagers.set( | ||
'federationReleaseTagManager', | ||
federationReleaseTagManager | ||
); | ||
this.releaseTagManagers.set( | ||
'stableDateReleaseTagManager', | ||
stableDateReleaseTagManager | ||
); | ||
} | ||
|
||
/** | ||
* Dynamically load a plugin from the given path | ||
* @param path where the plugin is located | ||
* @returns default export of the plugin | ||
*/ | ||
static async loadReleaseManagerPlugin(path: string) { | ||
const errMessage = | ||
'Cannot load release manager plugin. Invalid path or plugin already loaded.'; | ||
const releaseManagerName = path.split('/').pop()!; | ||
if (this.instance.releaseManagers.has(releaseManagerName)) { | ||
throw new Error(errMessage); | ||
} | ||
try { | ||
// import default export from the given path | ||
const { default: releaseManager } = await import(path); | ||
// add the release manager to the map | ||
this.instance.releaseManagers.set(releaseManagerName, releaseManager); | ||
return releaseManager; | ||
} catch (error) { | ||
throw new Error(errMessage); | ||
} | ||
} | ||
|
||
static getReleaseManager(name: string) { | ||
return this.instance.releaseManagers.get(name); | ||
} | ||
|
||
static getReleaseTagManager(name: string) { | ||
return this.instance.releaseTagManagers.get(name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.