Skip to content

Commit

Permalink
Merge pull request #38 from pfongkye/feat/config-json
Browse files Browse the repository at this point in the history
feat(config): use json for project configs
  • Loading branch information
pfongkye authored Oct 10, 2024
2 parents d593471 + ce62132 commit 8d98eff
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 32 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,26 @@ displayed.

You should now be able to use Homer commands on Slack 🎉

### 7. Configure releases

Releases management can be configured in `config/homer/projects.json`.
Here is a sample configuration with one project:

```json
{
"projects": [
{
"description": "project_example",
"notificationChannelIds": ["C0XXXXXXXXX"],
"projectId": 1148,
"releaseChannelId": "C0XXXXXXXXX",
"releaseManager": "defaultReleaseManager",
"releaseTagManager": "stableDateReleaseTagManager"
}
]
}
```

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).
66 changes: 66 additions & 0 deletions __tests__/release/utils/configBuilder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { defaultReleaseManager } from '@/release/commands/create/managers/defaultReleaseManager';
import { federationReleaseTagManager } from '@/release/commands/create/managers/federationReleaseTagManager';
import { buildProjectReleaseConfigs } from '@/release/utils/configBuilder';

describe('configBuilder', () => {
it('should build configs', () => {
const projects = [
{
releaseManager: 'defaultReleaseManager',
releaseTagManager: 'federationReleaseTagManager',
notificationChannelIds: ['C678'],
projectId: 123,
releaseChannelId: 'C456',
},
{
releaseManager: 'defaultReleaseManager',
notificationChannelIds: ['C678'],
projectId: 890,
releaseChannelId: 'C456',
},
];
expect(
buildProjectReleaseConfigs(
{ projects },
{ defaultReleaseManager },
{ federationReleaseTagManager }
)
).toEqual([
{
notificationChannelIds: ['C678'],
projectId: 123,
releaseChannelId: 'C456',
releaseManager: defaultReleaseManager,
releaseTagManager: federationReleaseTagManager,
},
{
notificationChannelIds: ['C678'],
projectId: 890,
releaseChannelId: 'C456',
releaseManager: defaultReleaseManager,
},
]);
});
it('should throw an error if projects is not an array', () => {
expect(() =>
buildProjectReleaseConfigs(
{} as any,
{ defaultReleaseManager },
{ federationReleaseTagManager }
)
).toThrow(
'The config file should contain an array of valid project configurations'
);
});
it('should throw an error if there is an invalid project configuration', () => {
expect(() =>
buildProjectReleaseConfigs(
[{ projectId: 123, releaseManager: 'defaultReleaseManager' }] as any,
{ defaultReleaseManager },
{ federationReleaseTagManager }
)
).toThrow(
'The config file should contain an array of valid project configurations'
);
});
});
15 changes: 0 additions & 15 deletions config/homer/projectReleaseConfigs.ts

This file was deleted.

12 changes: 12 additions & 0 deletions config/homer/projects.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"projects": [
{
"description": "project_example",
"notificationChannelIds": ["C0XXXXXXXXX"],
"projectId": 1148,
"releaseChannelId": "C0XXXXXXXXX",
"releaseManager": "defaultReleaseManager",
"releaseTagManager": "stableDateReleaseTagManager"
}
]
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
"pre-commit": "exec-staged",
"prepare": "husky install",
"start": "node dist/src/index.js",
"start-env": "node --env-file=.env dist/src/index.js",
"test": "jest --runInBand --forceExit"
},
"engines": {
"node": ">=20"
},
"dependencies": {
"@slack/web-api": "6.8.1",
"ajv": "^8.17.1",
"dayjs": "1.11.7",
"dd-trace": "3.13.2",
"dotenv": "16.0.3",
Expand Down
8 changes: 1 addition & 7 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { GitlabMergeRequestState } from '@/core/typings/GitlabMergeRequest';
import { getEnvVariable } from '@/core/utils/getEnvVariable';

export const CHANNEL_NOT_FOUND_SLACK_ERROR = 'channel_not_found';
export const EXPIRED_TRIGGER_ID_ERROR_MESSAGE =
Expand All @@ -19,12 +18,7 @@ export const MERGE_REQUEST_OPEN_STATES: GitlabMergeRequestState[] = [
'opened',
'reopened',
];
export const SLACK_SUPPORT_CHANNEL_ID = getEnvVariable(
'SLACK_SUPPORT_CHANNEL_ID'
);
export const SLACK_SUPPORT_CHANNEL_NAME = getEnvVariable(
'SLACK_SUPPORT_CHANNEL_NAME'
);

export const PRIVATE_CHANNEL_ERROR_MESSAGE =
'D’oh! It looks like you tried to use me on a private channel I’m not in. Please invite me using `/invite @homer` so I can publish messages :homer-donut:';
export const REQUEST_BODY_SIZE_LIMIT = '500kb';
Expand Down
13 changes: 7 additions & 6 deletions src/core/viewBuilders/buildHelpMessage.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { ChatPostMessageArguments } from '@slack/web-api';
import {
SLACK_SUPPORT_CHANNEL_ID,
SLACK_SUPPORT_CHANNEL_NAME,
} from '@/constants';
import { getEnvVariable } from '@/core/utils/getEnvVariable';

export function buildHelpMessage(channelId: string): ChatPostMessageArguments {
return {
Expand All @@ -18,7 +15,9 @@ Here are the available commands:
- /homer review list - List ongoing reviews shared in a channel.
- /homer release - Create a release for configured Gitlab project in a channel.
Don't hesitate to join me on #${SLACK_SUPPORT_CHANNEL_NAME} to take a beer!`,
Don't hesitate to join me on #${getEnvVariable(
'SLACK_SUPPORT_CHANNEL_NAME'
)} to take a beer!`,
blocks: [
{
type: 'section',
Expand All @@ -35,7 +34,9 @@ Here are the available commands:
• \`/homer review list\` List ongoing reviews shared in a channel.
• \`/homer release\` Create a release for configured Gitlab project in a channel.
Don't hesitate to join me on <#${SLACK_SUPPORT_CHANNEL_ID}> to take a :beer:!`,
Don't hesitate to join me on <#${getEnvVariable(
'SLACK_SUPPORT_CHANNEL_ID'
)}> to take a :beer:!`,
},
},
],
Expand Down
7 changes: 5 additions & 2 deletions src/home/buildAppHomeView.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { View } from '@slack/web-api';
import { HOMER_GIT_URL, SLACK_SUPPORT_CHANNEL_ID } from '@/constants';
import { HOMER_GIT_URL } from '@/constants';
import type { SlackUser } from '@/core/typings/SlackUser';
import { getEnvVariable } from '@/core/utils/getEnvVariable';

export async function buildAppHomeView(user: SlackUser): Promise<View> {
const firstName = user.real_name.split(' ')[0];
Expand All @@ -15,7 +16,9 @@ export async function buildAppHomeView(user: SlackUser): Promise<View> {
text: `\
Hello *${firstName}*, I'm *Homer*, the Slack Gitlab master!
If you want to take a :beer: with me, don't hesitate to join <#${SLACK_SUPPORT_CHANNEL_ID}>.
If you want to take a :beer: with me, don't hesitate to join <#${getEnvVariable(
'SLACK_SUPPORT_CHANNEL_ID'
)}>.
If you want to better know me, here are some useful links:
Expand Down
13 changes: 13 additions & 0 deletions src/release/typings/ProjectReleaseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,16 @@ export interface ProjectReleaseConfig {
releaseTagManager?: ReleaseTagManager;
hasReleasePipeline?: boolean;
}

export type ProjectConfigJSON = Omit<
ProjectReleaseConfig,
'releaseManager' | 'releaseTagManager'
> & {
releaseManager: string;
releaseTagManager?: string;
description?: string;
};

export type ProjectConfigurationsJSON = {
projects: Array<ProjectConfigJSON>;
};
81 changes: 81 additions & 0 deletions src/release/utils/configBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { JSONSchemaType } from 'ajv';
import Ajv from 'ajv';
import type {
ProjectConfigurationsJSON,
ProjectReleaseConfig,
ProjectConfigJSON,
} from '../typings/ProjectReleaseConfig';
import type { ReleaseManager } from '../typings/ReleaseManager';
import type { ReleaseTagManager } from '../typings/ReleaseTagManager';

// only one Ajv instance should be used across all the application
// maybe a singleton class would be better
const ajv = new Ajv();

const projectSchema: JSONSchemaType<ProjectConfigJSON> = {
type: 'object',
properties: {
notificationChannelIds: { type: 'array', items: { type: 'string' } },
projectId: { type: 'number' },
releaseChannelId: { type: 'string' },
releaseManager: { type: 'string' },
releaseTagManager: { type: 'string', nullable: true },
description: { type: 'string', nullable: true },
hasReleasePipeline: { type: 'boolean', nullable: true },
},
required: [
'notificationChannelIds',
'projectId',
'releaseChannelId',
'releaseManager',
],
additionalProperties: false,
};

const configsSchema: JSONSchemaType<ProjectConfigurationsJSON> = {
type: 'object',
properties: {
projects: {
type: 'array',
items: projectSchema,
},
},
required: ['projects'],
additionalProperties: false,
};

const validateProjectReleaseConfig = ajv.compile(configsSchema);

export function buildProjectReleaseConfigs(
configs: ProjectConfigurationsJSON,
releaseManagers: Record<string, ReleaseManager>,
releaseTagManagers: Record<string, ReleaseTagManager>
) {
if (!validateProjectReleaseConfig(configs)) {
throw new Error(
'The config file should contain an array of valid project configurations'
);
}
const projects: ProjectReleaseConfig[] = [];
for (const project of configs.projects) {
const {
releaseManager,
releaseTagManager,
notificationChannelIds,
projectId,
releaseChannelId,
} = project;
if (releaseManager in releaseManagers) {
projects.push({
notificationChannelIds,
projectId,
releaseChannelId,
releaseManager: releaseManagers[releaseManager],
releaseTagManager: releaseTagManager
? releaseTagManagers[releaseTagManager]
: undefined,
});
}
}
return projects;
}
29 changes: 27 additions & 2 deletions src/release/utils/configHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
import { projectReleaseConfigs } from '@root/config/homer/projectReleaseConfigs';
import type { ProjectReleaseConfig } from '../typings/ProjectReleaseConfig';
import { defaultReleaseManager } from '@/release/commands/create/managers/defaultReleaseManager';
import { federationReleaseTagManager } from '@/release/commands/create/managers/federationReleaseTagManager';
import { libraryReleaseManager } from '@/release/commands/create/managers/libraryReleaseManager';
import { semanticReleaseTagManager } from '@/release/commands/create/managers/semanticReleaseTagManager';
import { stableDateReleaseTagManager } from '@/release/commands/create/managers/stableDateReleaseTagManager';
import type { ProjectReleaseConfig } from '@/release/typings/ProjectReleaseConfig';
import type { ReleaseManager } from '@/release/typings/ReleaseManager';
import type { ReleaseTagManager } from '@/release/typings/ReleaseTagManager';
import { buildProjectReleaseConfigs } from '@/release/utils/configBuilder';
import projectsConfig from '@root/config/homer/projects.json';

const releaseManagers: Record<string, ReleaseManager> = {
defaultReleaseManager,
libraryReleaseManager,
};
const releaseTagManagers: Record<string, ReleaseTagManager> = {
federationReleaseTagManager,
semanticReleaseTagManager,
stableDateReleaseTagManager,
};

export const projectReleaseConfigs: ProjectReleaseConfig[] =
buildProjectReleaseConfigs(
projectsConfig,
releaseManagers,
releaseTagManagers
);

export function getChannelProjectReleaseConfigs(
channelId: string
Expand Down
25 changes: 25 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,16 @@ ajv@^6.10.0, ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"

ajv@^8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
dependencies:
fast-deep-equal "^3.1.3"
fast-uri "^3.0.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"

ansi-escapes@^4.2.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
Expand Down Expand Up @@ -2334,6 +2344,11 @@ fast-safe-stringify@^2.1.1:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==

fast-uri@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134"
integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==

fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
Expand Down Expand Up @@ -3479,6 +3494,11 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==

json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==

json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
Expand Down Expand Up @@ -4559,6 +4579,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==

require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==

resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
Expand Down

0 comments on commit 8d98eff

Please sign in to comment.