Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a script to generate a sample config #8899

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ config.json
You can configure the app by copying `config.sample.json` to
`config.json` and customising it:

For a good example, see https://riot.im/develop/config.json.
For a good example of a production config, see https://riot.im/develop/config.json.

1. `default_server_name` sets the default server name to use for authentication.
This will trigger Riot to ask
Expand Down
118 changes: 102 additions & 16 deletions config.sample.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,120 @@
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really wish we could parse the config in such a way that allows inline comments. I think the sample file would be improved even more (especially if we do list every default value like this) if we could:

  • Clearly state that these are defaults
  • Comment them out, because they are defaults and don't need to be set
  • Describe the purpose of each setting in a comment in the sample file
    • This could be done by adding description key or similar to Settings.js which is extracted here as a comment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON can't have comments :(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we used YAML or something, I'd definitely be including descriptions for all of this. I couldn't think of a nice way to actually put a comment in here without people thinking that it is a config option itself.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, JSON can't have comments (which annoys me for most JSON-based configuration). Sorry, I should been more specific...

Along with YAML, other posibilities include:

  • Cheat and still call it "JSON", but fetch it as text, strip out comments, then hand it JSON.parse (ESLint does this for *.json config files)
  • Rename to config.js and have the config file export an object (which implicitly allows comments by allowing full JS syntax)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm moderately in favour of calling it """"""JSON""""""

(or we take up HCL)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self: replace https://github.com/vector-im/riot-web#configjson with comments, as mentioned above

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@turt2live @jryans one way to have inline comments might be to allow json5 as a second format of the json: https://json5.org/

But as that needs more changes to the core itself I guess that should (If wanted) go into an extra PR

"default_hs_url": "https://matrix.org",
"default_is_url": "https://vector.im",
"default_server_name": "matrix.org",
"disable_custom_urls": false,
"disable_guests": false,
"disable_login_language_selector": false,
"disable_3pid_login": false,
"brand": "Riot",
"welcomeUserId": "@riot-bot:matrix.org",
"default_federate": true,
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_jitsi_widget_url": "https://scalar.vector.im/api/widgets/jitsi.html",
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"features": {
"feature_groups": "labs",
"feature_pinning": "labs"
},
"default_federate": true,
"integrations_widgets_urls": [
"https://scalar-staging.riot.im/scalar/api",
"https://scalar.vector.im/api"
],
"default_theme": "light",
"roomDirectory": {
"servers": [
"matrix.org"
]
},
"welcomeUserId": "@riot-bot:matrix.org",
"piwik": {
"url": "https://piwik.riot.im/",
"whitelistedHSUrls": ["https://matrix.org"],
"whitelistedISUrls": ["https://vector.im", "https://matrix.org"],
"siteId": 1
},
"enable_presence_by_hs_url": {
"https://matrix.org": false
},
"piwik": {
"siteId": 1,
"url": "https://piwik.riot.im",
"whitelistedHSUrls": [
"https://matrix.org"
],
"whitelistedISUrls": [
"https://vector.im",
"https://matrix.org"
]
},
"embeddedPages": {
"homeUrl": "home.html",
"welcomeUrl": "welcome.html"
},
"cross_origin_renderer_url": "https://usercontent.riot.im/v1.html",
"branding": {
"authHeaderLogoUrl": "themes/riot/img/logos/riot-im-logo-black-text.svg",
"welcomeBackgroundUrl": "themes/riot/img/backgrounds/valley.jpg"
},
"update_base_url": "https://riot.im/download/desktop/update/",
"sync_timeline_limit": 8,
"terms_and_conditions_links": [
{
"url": "https://matrix.org/docs/guides/code_of_conduct",
"text": "matrix.org code of conduct"
}
],
"phasedRollOut": {
"feature_pinning": {
"offset": 1550893490769,
"period": 604800000
},
"feature_custom_status": {
"offset": 1550893490769,
"period": 604800000
},
"feature_room_breadcrumbs": {
"offset": 1550893490769,
"period": 604800000
},
"feature_custom_tags": {
"offset": 1550893490769,
"period": 604800000
},
"feature_state_counters": {
"offset": 1550893490769,
"period": 604800000
}
},
"features": {
"feature_pinning": "labs",
"feature_custom_status": "labs",
"feature_room_breadcrumbs": "labs",
"feature_custom_tags": "labs",
"feature_state_counters": "labs"
},
"settingDefaults": {
"MessageComposerInput.dontSuggestEmoji": false,
"useCompactLayout": false,
"hideRedactions": false,
"hideJoinLeaves": false,
"hideAvatarChanges": false,
"hideDisplaynameChanges": false,
"hideReadReceipts": false,
"showTwelveHourTimestamps": false,
"alwaysShowTimestamps": false,
"autoplayGifsAndVideos": false,
"alwaysShowEncryptionIcons": true,
"showRoomRecoveryReminder": true,
"enableSyntaxHighlightLanguageDetection": false,
"Pill.shouldHidePillAvatar": false,
"TextualBody.disableBigEmoji": false,
"MessageComposerInput.isRichTextEnabled": false,
"MessageComposer.showFormatting": false,
"dontSendTypingNotifications": false,
"MessageComposerInput.autoReplaceEmoji": false,
"VideoView.flipVideoHorizontally": false,
"TagPanel.disableTagPanel": false,
"webRtcForceTURN": false,
"language": "en",
"analyticsOptIn": false,
"showCookieBar": true,
"autocompleteDelay": 200,
"urlPreviewsEnabled": true,
"roomColor": {
"primary_color": "#a442f4",
"secondary_color": "#cc92fc"
},
"enableWidgetScreenshots": false,
"promptBeforeInviteUnknownUsers": true,
"showDeveloperTools": false
}
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"reskindex:watch": "reskindex -h src/header -w",
"i18n": "matrix-gen-i18n",
"prunei18n": "matrix-prune-i18n",
"gensampleconf": "node scripts/gen-sample-config.js",
"build:res": "node scripts/copy-res.js",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:compile": "npm run reskindex && babel --source-maps -d lib src",
Expand Down
157 changes: 157 additions & 0 deletions scripts/gen-sample-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
Copyright 2019 New Vector Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// The sample config is generated as follows:
// 1. Start with SdkConfig's DEFAULTS
// 2. Overwrite with features and Settings defaults
// 3. Overwrite with samples defined here

const SdkConfig = require("matrix-react-sdk/lib/SdkConfig");
const fs = require("fs");

const SAMPLES = {
default_server_name: "matrix.org",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't decided yet if this sample info is better here or inline in Settings.js with each setting... I just worry it might get forgotten over here, but I suppose the idea is you'd run this script whenever a new setting is added, so hopefully not forgotten then...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is also that the config is more than just settings. Personally, I'd rather us fill out the defaults for SdkConfig so we don't need this here, but that raises problems. The first problem being that default_server_name or default_hs_url (and is_url) can be defined - it's hard to represent that in a config.

disable_custom_urls: false,
disable_guests: false,
disable_login_language_selector: false,
disable_3pid_login: false,
brand: "Riot",
welcomeUserId: "@riot-bot:matrix.org",
default_federate: true,
bug_report_endpoint_url: "https://riot.im/bugreports/submit",
integrations_ui_url: "https://scalar.vector.im/",
integrations_rest_url: "https://scalar.vector.im/api",
integrations_jitsi_widget_url: "https://scalar.vector.im/api/widgets/jitsi.html",
integrations_widgets_urls: [
"https://scalar-staging.riot.im/scalar/api",
"https://scalar.vector.im/api",
],
default_theme: "light",
roomDirectory: {
servers: [
"matrix.org",
],
},
enable_presence_by_hs_url: {
"https://matrix.org": false,
},
piwik: {
siteId: 1,
url: "https://piwik.riot.im",
whitelistedHSUrls: ["https://matrix.org"],
whitelistedISUrls: ["https://vector.im", "https://matrix.org"],
},
embeddedPages: {
homeUrl: "home.html",
welcomeUrl: "welcome.html",
},
cross_origin_renderer_url: "https://usercontent.riot.im/v1.html",
branding: {
authHeaderLogoUrl: "themes/riot/img/logos/riot-im-logo-black-text.svg",
welcomeBackgroundUrl: "themes/riot/img/backgrounds/valley.jpg",
},
update_base_url: "https://riot.im/download/desktop/update/",
sync_timeline_limit: 8,
terms_and_conditions_links: [
{
url: "https://matrix.org/docs/guides/code_of_conduct",
text: "matrix.org code of conduct",
},
],
};

function parseSettings() {
// This is by far the worst and cleanest way to load the settings config.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, agreed this is unfortunate... Is there any way to avoid the eval? Some possibilities:

  • Move SETTINGS object out of Settings.js without the controllers and such (not great, since likely means bits of settings in multiple places)
  • This script could set an environments variable, and then Settings.js could check this at import time and replace dependencies with no-op stubs to allow a normal require to complete

// We parse the file (and eval(!!!) it), stripping out lines that will cause
// us issues, like imports, controllers, and language references. If we don't
// strip out imports and such, we'll get all kinds of fun errors because we
// won't have loaded the dependency chain for a web browser, making things like
// the language handler complain.
const contents = fs.readFileSync(require.resolve("matrix-react-sdk/src/settings/Settings"));
const lines = (contents.toString()).split('\n').map(s => s.trim());

let foundFirstConst = false;
const rebuiltLines = lines.filter(s => {
// Strip everything up until the first variable definition (because multiline imports are a thing)
if (s.indexOf("const ") === 0) foundFirstConst = true;
if (!foundFirstConst) return false;

// Filter out anything else that might cause us problems when we eval it
return s.indexOf("controller:") === -1 && s.indexOf("_td(") === -1;
}).map(s => s.indexOf("export const") === 0 ? s.substring("export ".length) : s);

const scriptBase = rebuiltLines.join('\n');

// We wrap it to prevent variable leaking, but this doesn't protect us from random file access, etc.
const wrapper = `(function(){${scriptBase}\nreturn SETTINGS;})();`;
return eval(wrapper);
}

function generateSettingsConfig() {
const settingDefaults = {};
const phasedRollOut = {};
const features = {};

const overrides = {
"roomColor": {
"primary_color": "#a442f4",
"secondary_color": "#cc92fc"
}
};

const skipSettings = ['theme'];

const settings = parseSettings();
for (const settingName of Object.keys(settings)) {
const setting = settings[settingName];

if (setting.isFeature) {
phasedRollOut[settingName] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a bit like overkill to print this phased roll out bit for every feature. Maybe just the first?

offset: new Date().getTime(),
period: 604800000,
};

features[settingName] = 'labs';

continue;
}

if (setting.supportedLevels.indexOf('config') === -1) continue;
if (skipSettings.indexOf(settingName) !== -1) continue;

if (setting.invertedSettingName) {
settingDefaults[setting.invertedSettingName] = !setting.default;
} else {
settingDefaults[settingName] = setting.default;
}
}

return {
phasedRollOut,
features,
settingDefaults: Object.assign(settingDefaults, overrides),
};
}


const finalSettings = Object.assign({},
SdkConfig.DEFAULTS,
SAMPLES,
generateSettingsConfig(),
);

fs.writeFileSync("config.sample.json", JSON.stringify(finalSettings, null, 4));
console.log("Wrote new sample config");