mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 21:19:13 +00:00
Generate default config automatically
This commit is contained in:
parent
fe0e3637ea
commit
7ba6f0f37c
15
.github/workflows/main.yml
vendored
15
.github/workflows/main.yml
vendored
@ -31,5 +31,18 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- run: yarn
|
||||
- run: yarn build
|
||||
- run: yarn test
|
||||
config:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node_version: ['14', '12']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
- run: yarn
|
||||
- run: node lib/Config/Defaults.js > expected-config.sample.yml
|
||||
- run: cmp --silent config.sample.yml expected-config.sample.yml
|
||||
|
@ -1,55 +1,39 @@
|
||||
# This is an example configuration file
|
||||
|
||||
bridge:
|
||||
# Basic homeserver configuration
|
||||
#
|
||||
domain: example.com
|
||||
url: http://localhost:8008
|
||||
mediaUrl: http://example.com
|
||||
port: 9993
|
||||
bindAddress: 127.0.0.1
|
||||
github:
|
||||
# You can find this by going to https://github.com/settings/installations,
|
||||
# clicking on the bridge app and taking note of the ID in the URL.
|
||||
# E.g. https://github.com/settings/installations/6854059
|
||||
installationId: 6854059
|
||||
auth:
|
||||
auth:
|
||||
id: 123
|
||||
privateKeyFile: "github-key.pem"
|
||||
privateKeyFile: github-key.pem
|
||||
oauth:
|
||||
client_id: foo
|
||||
client_secret: bar
|
||||
redirect_uri: baz
|
||||
redirect_uri: https://example.com/bridge_oauth/
|
||||
webhook:
|
||||
secret: webhooksecret
|
||||
port: 7775
|
||||
bindAddress: 0.0.0.0
|
||||
userTokens:
|
||||
# If you want to hardcode a users token in the config
|
||||
- "@hardcoded:localhost": "foobarbaz"
|
||||
bridge:
|
||||
# Homeserver servername
|
||||
domain: example.com
|
||||
# Homeserver (internal) url
|
||||
url: http://localhost:8008
|
||||
# This is your public facing url where media is served from
|
||||
mediaUrl: http://example.com/
|
||||
port: 9993 # Port the appservice is binding to
|
||||
bindAddress: 127.0.0.1 # Address to bind to
|
||||
queue:
|
||||
monolithic: true # If this is false, use a Redis server as a queue.
|
||||
# Redis settings
|
||||
port: 6379 # Port for the redis server, omit for 6379
|
||||
host: localhost # Port for the redis server, omit for localhost
|
||||
# Note that setting the above will make the bridge use redis as a storage location
|
||||
logging:
|
||||
level: "info" # One of "debug", "info", "warn", "error"
|
||||
|
||||
# This is used to securely store passwords.
|
||||
# Run openssl genpkey -out passkey.pem -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:4096
|
||||
passFile: "passkey.pem"
|
||||
|
||||
secret: secrettoken
|
||||
gitlab:
|
||||
instances:
|
||||
gitlab.com:
|
||||
url: https://gitlab.com
|
||||
webhook:
|
||||
secret: secrettoken
|
||||
webhook:
|
||||
port: 9000
|
||||
bindAddress: 0.0.0.0
|
||||
passFile: passkey.pem
|
||||
queue:
|
||||
monolithic: true
|
||||
port: 6379
|
||||
host: localhost
|
||||
logging:
|
||||
level: info
|
||||
|
||||
widgets:
|
||||
# The port to listen on for the widget API
|
||||
port: 5000
|
||||
# Public url that the widget API is reachable on
|
||||
publicUrl: "https://example.com/bridgewidgets/"
|
||||
# Add the widget to admin rooms
|
||||
addToAdminRooms: true
|
||||
|
||||
bot:
|
||||
displayname: "GitHub Bot"
|
||||
avatar: mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d
|
||||
sdasdsade
|
@ -18,7 +18,8 @@
|
||||
"start:webhooks": "node --require source-map-support/register lib/App/GithubWebhookApp.js",
|
||||
"start:matrixsender": "node --require source-map-support/register lib/App/MatrixSenderApp.js",
|
||||
"test": "mocha -r ts-node/register tests/*.ts",
|
||||
"lint": "eslint -c .eslintrc.js src/**/*.ts"
|
||||
"lint": "eslint -c .eslintrc.js src/**/*.ts",
|
||||
"generate-default-config": "node lib/Config/Defaults.js > config.sample.yml"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "2.10.2",
|
||||
@ -42,7 +43,7 @@
|
||||
"string-argv": "v0.3.1",
|
||||
"uuid": "^8.3.1",
|
||||
"winston": "^3.3.3",
|
||||
"yaml": "^1.10.0"
|
||||
"yaml": "^2.0.0-1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@prefresh/snowpack": "^2.2.0",
|
||||
|
@ -288,6 +288,9 @@ export class AdminRoom extends EventEmitter {
|
||||
org,
|
||||
})).data;
|
||||
} catch (ex) {
|
||||
if (ex.status === 404) {
|
||||
return this.sendNotice('Not found');
|
||||
}
|
||||
log.warn(`Failed to fetch projects:`, ex);
|
||||
return this.sendNotice(`Failed to fetch projects due to an error. See logs for details`);
|
||||
}
|
||||
@ -318,6 +321,9 @@ export class AdminRoom extends EventEmitter {
|
||||
});
|
||||
this.emit('open.project', project.data);
|
||||
} catch (ex) {
|
||||
if (ex.status === 404) {
|
||||
return this.sendNotice('Not found');
|
||||
}
|
||||
log.warn(`Failed to fetch project:`, ex);
|
||||
return this.sendNotice(`Failed to fetch project due to an error. See logs for details`);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GithubBridge } from "../GithubBridge";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
|
||||
import { parseConfig, parseRegistrationFile } from "../Config";
|
||||
import { BridgeConfig, parseRegistrationFile } from "../Config";
|
||||
import { GithubWebhooks } from "../GithubWebhooks";
|
||||
import { MatrixSender } from "../MatrixSender";
|
||||
|
||||
@ -10,7 +10,7 @@ const log = new LogWrapper("App");
|
||||
async function start() {
|
||||
const configFile = process.argv[2] || "./config.yml";
|
||||
const registrationFile = process.argv[3] || "./registration.yml";
|
||||
const config = await parseConfig(configFile, process.env);
|
||||
const config = await BridgeConfig.parseConfig(configFile, process.env);
|
||||
const registration = await parseRegistrationFile(registrationFile);
|
||||
LogWrapper.configureLogging(config.logging.level);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parseConfig } from "../Config";
|
||||
import { BridgeConfig } from "../Config";
|
||||
import { GithubWebhooks } from "../GithubWebhooks";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
|
||||
@ -7,7 +7,7 @@ const log = new LogWrapper("App");
|
||||
|
||||
async function start() {
|
||||
const configFile = process.argv[2] || "./config.yml";
|
||||
const config = await parseConfig(configFile, process.env);
|
||||
const config = await BridgeConfig.parseConfig(configFile, process.env);
|
||||
LogWrapper.configureLogging(config.logging.level);
|
||||
const webhookHandler = new GithubWebhooks(config);
|
||||
webhookHandler.listen();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { parseConfig, parseRegistrationFile } from "../Config";
|
||||
import { BridgeConfig, parseRegistrationFile } from "../Config";
|
||||
import { MatrixSender } from "../MatrixSender";
|
||||
import LogWrapper from "../LogWrapper";
|
||||
|
||||
@ -8,7 +8,7 @@ const log = new LogWrapper("App");
|
||||
async function start() {
|
||||
const configFile = process.argv[2] || "./config.yml";
|
||||
const registrationFile = process.argv[3] || "./registration.yml";
|
||||
const config = await parseConfig(configFile, process.env);
|
||||
const config = await BridgeConfig.parseConfig(configFile, process.env);
|
||||
const registration = await parseRegistrationFile(registrationFile);
|
||||
LogWrapper.configureLogging(config.logging.level);
|
||||
const sender = new MatrixSender(config, registration);
|
||||
|
130
src/Config.ts
130
src/Config.ts
@ -1,6 +1,8 @@
|
||||
import YAML from "yaml";
|
||||
import { promises as fs } from "fs";
|
||||
import { IAppserviceRegistration } from "matrix-bot-sdk";
|
||||
import * as assert from "assert";
|
||||
import { configKey } from "./Config/Decorators";
|
||||
|
||||
export interface BridgeConfigGitHub {
|
||||
auth: {
|
||||
@ -31,10 +33,6 @@ export interface GitLabInstance {
|
||||
}
|
||||
|
||||
interface BridgeConfigGitLab {
|
||||
auth: {
|
||||
id: number|string;
|
||||
privateKeyFile: string;
|
||||
};
|
||||
webhook: {
|
||||
secret: string;
|
||||
},
|
||||
@ -47,58 +45,88 @@ interface BridgeWidgetConfig {
|
||||
publicUrl: string;
|
||||
}
|
||||
|
||||
export interface BridgeConfig {
|
||||
interface BridgeConfigBridge {
|
||||
domain: string;
|
||||
url: string;
|
||||
mediaUrl?: string;
|
||||
port: number;
|
||||
bindAddress: string;
|
||||
}
|
||||
|
||||
interface BridgeConfigWebhook {
|
||||
port: number;
|
||||
bindAddress: string;
|
||||
}
|
||||
|
||||
interface BridgeConfigQueue {
|
||||
monolithic: boolean;
|
||||
port?: number;
|
||||
host?: string;
|
||||
}
|
||||
|
||||
interface BridgeConfigLogging {
|
||||
level: string;
|
||||
}
|
||||
|
||||
interface BridgeConfigBot {
|
||||
displayname?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
interface BridgeConfigRoot {
|
||||
bridge: BridgeConfigBridge;
|
||||
webhook: BridgeConfigWebhook;
|
||||
queue: BridgeConfigQueue;
|
||||
logging: BridgeConfigLogging;
|
||||
passFile: string;
|
||||
github?: BridgeConfigGitHub;
|
||||
gitlab?: BridgeConfigGitLab;
|
||||
webhook: {
|
||||
port: number;
|
||||
bindAddress: string;
|
||||
};
|
||||
bridge: {
|
||||
domain: string;
|
||||
url: string;
|
||||
mediaUrl: string;
|
||||
port: number;
|
||||
bindAddress: string;
|
||||
store: string;
|
||||
};
|
||||
queue: {
|
||||
monolithic: boolean;
|
||||
port?: number;
|
||||
host?: string;
|
||||
};
|
||||
logging: {
|
||||
level: string;
|
||||
};
|
||||
passFile: string;
|
||||
bot?: {
|
||||
displayname?: string;
|
||||
avatar?: string;
|
||||
}
|
||||
bot?: BridgeConfigBot;
|
||||
widgets?: BridgeWidgetConfig;
|
||||
}
|
||||
|
||||
export class BridgeConfig {
|
||||
@configKey("Basic homeserver configuration")
|
||||
public readonly bridge: BridgeConfigBridge;
|
||||
public readonly webhook: BridgeConfigWebhook;
|
||||
public readonly queue: BridgeConfigQueue;
|
||||
public readonly logging: BridgeConfigLogging;
|
||||
public readonly passFile: string;
|
||||
public readonly github?: BridgeConfigGitHub;
|
||||
public readonly gitlab?: BridgeConfigGitLab;
|
||||
public readonly bot?: BridgeConfigBot;
|
||||
public readonly widgets?: BridgeWidgetConfig;
|
||||
|
||||
constructor(configData: BridgeConfigRoot, env: {[key: string]: string|undefined}) {
|
||||
this.bridge = configData.bridge;
|
||||
assert.ok(this.bridge);
|
||||
this.github = configData.github;
|
||||
this.gitlab = configData.gitlab;
|
||||
this.webhook = configData.webhook;
|
||||
this.passFile = configData.passFile;
|
||||
assert.ok(this.webhook);
|
||||
this.queue = configData.queue || {
|
||||
monolithic: true,
|
||||
};
|
||||
this.logging = configData.logging || {
|
||||
level: "info",
|
||||
}
|
||||
// TODO: Formalize env support
|
||||
if (env.CFG_QUEUE_MONOLITHIC && ["false", "off", "no"].includes(env.CFG_QUEUE_MONOLITHIC)) {
|
||||
this.queue.monolithic = false;
|
||||
this.queue.host = env.CFG_QUEUE_HOST;
|
||||
this.queue.port = env.CFG_QUEUE_POST ? parseInt(env.CFG_QUEUE_POST, 10) : undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static async parseConfig(filename: string, env: {[key: string]: string|undefined}) {
|
||||
const file = await fs.readFile(filename, "utf-8");
|
||||
return new BridgeConfig(YAML.parse(file), env);
|
||||
}
|
||||
}
|
||||
|
||||
export async function parseRegistrationFile(filename: string) {
|
||||
const file = await fs.readFile(filename, "utf-8");
|
||||
return YAML.parse(file) as IAppserviceRegistration;
|
||||
}
|
||||
|
||||
export async function parseConfig(filename: string, env: {[key: string]: string|undefined}) {
|
||||
const file = await fs.readFile(filename, "utf-8");
|
||||
const config = YAML.parse(file) as BridgeConfig;
|
||||
config.queue = config.queue || {
|
||||
monolithic: true,
|
||||
};
|
||||
if (!config.logging || !config.logging.level) {
|
||||
config.logging = {
|
||||
level: "info",
|
||||
};
|
||||
}
|
||||
config.bridge.mediaUrl = config.bridge.mediaUrl || config.bridge.url;
|
||||
if (env.CFG_QUEUE_MONOLITHIC && ["false", "off", "no"].includes(env.CFG_QUEUE_MONOLITHIC)) {
|
||||
config.queue.monolithic = false;
|
||||
config.queue.host = env.CFG_QUEUE_HOST;
|
||||
config.queue.port = env.CFG_QUEUE_POST ? parseInt(env.CFG_QUEUE_POST, 10) : undefined;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
11
src/Config/Decorators.ts
Normal file
11
src/Config/Decorators.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import "reflect-metadata";
|
||||
|
||||
const configKeyMetadataKey = Symbol("configKey");
|
||||
|
||||
export function configKey(comment?: string, optional = false) {
|
||||
return Reflect.metadata(configKeyMetadataKey, [comment, optional]);
|
||||
}
|
||||
|
||||
export function getConfigKeyMetadata(target: any, propertyKey: string): [string, boolean] {
|
||||
return Reflect.getMetadata(configKeyMetadataKey, target, propertyKey);
|
||||
}
|
98
src/Config/Defaults.ts
Normal file
98
src/Config/Defaults.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { BridgeConfig } from "../Config";
|
||||
import YAML from "yaml";
|
||||
import { getConfigKeyMetadata } from "./Decorators";
|
||||
import { Node, YAMLSeq } from "yaml/types";
|
||||
|
||||
const DefaultConfig = new BridgeConfig({
|
||||
bridge: {
|
||||
domain: "example.com",
|
||||
url: "http://localhost:8008",
|
||||
mediaUrl: "http://example.com",
|
||||
port: 9993,
|
||||
bindAddress: "127.0.0.1",
|
||||
},
|
||||
queue: {
|
||||
monolithic: true,
|
||||
port: 6379,
|
||||
host: "localhost",
|
||||
},
|
||||
logging: {
|
||||
level: "info",
|
||||
},
|
||||
passFile: "passkey.pem",
|
||||
webhook: {
|
||||
port: 9000,
|
||||
bindAddress: "0.0.0.0"
|
||||
},
|
||||
widgets: {
|
||||
port: 5000,
|
||||
publicUrl: "https://example.com/bridge_widget/",
|
||||
addToAdminRooms: true,
|
||||
},
|
||||
bot: {
|
||||
displayname: "GitHub Bot",
|
||||
avatar: "mxc://half-shot.uk/2876e89ccade4cb615e210c458e2a7a6883fe17d"
|
||||
},
|
||||
github: {
|
||||
installationId: 6854059,
|
||||
auth: {
|
||||
id: 123,
|
||||
privateKeyFile: "github-key.pem",
|
||||
},
|
||||
oauth: {
|
||||
client_id: "foo",
|
||||
client_secret: "bar",
|
||||
redirect_uri: "https://example.com/bridge_oauth/",
|
||||
},
|
||||
webhook: {
|
||||
secret: "secrettoken",
|
||||
},
|
||||
},
|
||||
gitlab: {
|
||||
instances: {
|
||||
"gitlab.com": {
|
||||
url: "https://gitlab.com",
|
||||
}
|
||||
},
|
||||
webhook: {
|
||||
secret: "secrettoken",
|
||||
}
|
||||
}
|
||||
}, {});
|
||||
|
||||
function renderSection(doc: YAML.Document, obj: Record<string, unknown>, parentNode?: YAMLSeq) {
|
||||
const entries = Object.entries(obj);
|
||||
entries.forEach(([key, value], i) => {
|
||||
let newNode: Node;
|
||||
if (typeof value === "object") {
|
||||
newNode = doc.createNode({});
|
||||
renderSection(doc, value as any, newNode as YAMLSeq);
|
||||
} else {
|
||||
newNode = doc.createNode(value);
|
||||
}
|
||||
|
||||
const metadata = getConfigKeyMetadata(obj, key);
|
||||
if (metadata) {
|
||||
newNode.commentBefore = `${metadata[1] ? '(Optional)' : ''} ${metadata[0]}\n`;
|
||||
}
|
||||
|
||||
if (parentNode) {
|
||||
parentNode.add({key, value: newNode});
|
||||
} else {
|
||||
doc.add({key, value: newNode});
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function renderDefaultConfig() {
|
||||
const doc = new YAML.Document({});
|
||||
doc.commentBefore = ' This is an example configuration file';
|
||||
// Needed because the entries syntax below would not work otherwise
|
||||
//const typeLessDefaultConfig = DefaultConfig as any;
|
||||
renderSection(doc, DefaultConfig as any);
|
||||
return doc.toString();
|
||||
}
|
||||
|
||||
// Can be called directly
|
||||
console.log(renderDefaultConfig())
|
@ -172,7 +172,7 @@ export class GithubBridge {
|
||||
|
||||
this.widgetApi = new BridgeWidgetApi(this.adminRooms);
|
||||
|
||||
this.commentProcessor = new CommentProcessor(this.as, this.config.bridge.mediaUrl);
|
||||
this.commentProcessor = new CommentProcessor(this.as, this.config.bridge.mediaUrl || this.config.bridge.url);
|
||||
|
||||
this.tokenStore = new UserTokenStore(this.config.passFile || "./passkey.pem", this.as.botIntent);
|
||||
await this.tokenStore.load();
|
||||
|
Loading…
x
Reference in New Issue
Block a user