summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClement Tsang <34804052+ClementTsang@users.noreply.github.com>2024-02-04 06:26:24 -0500
committerGitHub <noreply@github.com>2024-02-04 06:26:24 -0500
commit0b92679e1656dd486654ff45de85c528e96d5b26 (patch)
tree7676365c72e29a120ccfe6eb07f35825ae637d58
parentb6660610d02a88aeb23fbcda8378be67a85259e7 (diff)
other: add v1 schema + versioning + tests (#1407)
* other: add v1.0 schema * add tests, rename some files for consistency
-rw-r--r--.github/workflows/clear_workflow_cache.yml (renamed from .github/workflows/clear-workflow-cache.yml)0
-rw-r--r--.github/workflows/post_release.yml (renamed from .github/workflows/post-release.yml)0
-rw-r--r--.github/workflows/test_docs.yml (renamed from .github/workflows/test-docs.yml)2
-rw-r--r--.github/workflows/validate_schema.yml56
-rw-r--r--.gitignore2
-rw-r--r--schema/v1.0/bottom.json385
-rw-r--r--scripts/schema/bad_file.toml2
-rw-r--r--scripts/schema/requirements.txt2
-rw-r--r--scripts/schema/validator.py55
9 files changed, 503 insertions, 1 deletions
diff --git a/.github/workflows/clear-workflow-cache.yml b/.github/workflows/clear_workflow_cache.yml
index 6d6ca6c9..6d6ca6c9 100644
--- a/.github/workflows/clear-workflow-cache.yml
+++ b/.github/workflows/clear_workflow_cache.yml
diff --git a/.github/workflows/post-release.yml b/.github/workflows/post_release.yml
index 5f1b0485..5f1b0485 100644
--- a/.github/workflows/post-release.yml
+++ b/.github/workflows/post_release.yml
diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test_docs.yml
index 128f6e48..c91b6389 100644
--- a/.github/workflows/test-docs.yml
+++ b/.github/workflows/test_docs.yml
@@ -19,7 +19,7 @@ jobs:
uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5.3.1
with:
skip_after_successful_duplicate: "true"
- paths: '["docs/**", ".github/workflows/docs.yml", ".github/workflows/test-docs.yml"]'
+ paths: '["docs/**", ".github/workflows/docs.yml", ".github/workflows/test_docs.yml"]'
do_not_skip: '["workflow_dispatch"]'
test-build-documentation:
diff --git a/.github/workflows/validate_schema.yml b/.github/workflows/validate_schema.yml
new file mode 100644
index 00000000..d14d7f66
--- /dev/null
+++ b/.github/workflows/validate_schema.yml
@@ -0,0 +1,56 @@
+# Workflow to validate the latest schema.
+
+name: "validate schema"
+on:
+ workflow_dispatch:
+ pull_request:
+ push:
+ branches:
+ - main
+ paths:
+ - "schema/**"
+ - ".github/workflows/validate_schema.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'ClementTsang/bottom' }}
+
+jobs:
+ pre-job:
+ runs-on: ubuntu-latest
+ outputs:
+ should_skip: ${{ steps.skip_check.outputs.should_skip }}
+ steps:
+ - id: skip_check
+ uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5.3.1
+ with:
+ skip_after_successful_duplicate: "true"
+ paths: '["schema/**", ".github/workflows/validate_schema.yml"]'
+ do_not_skip: '["workflow_dispatch"]'
+
+ test-build-documentation:
+ name: Test validating schema
+ needs: pre-job
+ if: ${{ needs.pre-job.outputs.should_skip != 'true' }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ with:
+ fetch-depth: 0
+
+ - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
+ with:
+ python-version: 3.11
+
+ - name: Install Python dependencies
+ run: pip install -r scripts/schema/requirements.txt
+
+ - name: Test nightly validates on valid sample configs
+ run: |
+ python3 scripts/schema/validator.py -s ./schema/nightly/bottom.json -f ./sample_configs/default_config.toml
+ python3 scripts/schema/validator.py -s ./schema/nightly/bottom.json -f ./sample_configs/demo_config.toml
+
+ - name: Test nightly catches on a bad sample config
+ run: |
+ python3 scripts/schema/validator.py -s ./schema/nightly/bottom.json -f scripts/schema/bad_file.toml --should_fail
diff --git a/.gitignore b/.gitignore
index 2e446385..a89e65d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,5 @@ supply-chain/
# samply profiling
profile.json
+
+**/venv/ \ No newline at end of file
diff --git a/schema/v1.0/bottom.json b/schema/v1.0/bottom.json
new file mode 100644
index 00000000..807862d9
--- /dev/null
+++ b/schema/v1.0/bottom.json
@@ -0,0 +1,385 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://github.com/ClementTsang/bottom/tree/main/schema/nightly/schema.json",
+ "$comment": "https://clementtsang.github.io/bottom/nightly/configuration/config-file",
+ "title": "Schema for bottom's configs (v1)",
+ "type": "object",
+ "definitions": {
+ "row": {
+ "items": {
+ "properties": {
+ "ratio": {
+ "default": 1,
+ "type": "integer"
+ },
+ "type": {
+ "enum": ["cpu", "mem", "proc", "net", "temp", "disk", "empty"],
+ "type": "string"
+ },
+ "default": {
+ "default": true,
+ "type": "boolean"
+ }
+ },
+ "patternProperties": {
+ "row(.child)+": {
+ "$ref": "#/definitions/row"
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ },
+ "filter": {
+ "description": "hide specific temperature sensors, network interfaces, and disks using filters",
+ "properties": {
+ "is_list_ignored": {
+ "default": true,
+ "type": "boolean"
+ },
+ "list": {
+ "type": "array"
+ },
+ "regex": {
+ "default": true,
+ "type": "boolean"
+ },
+ "case_sensitive": {
+ "default": false,
+ "type": "boolean"
+ },
+ "whole_word": {
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "properties": {
+ "flags": {
+ "description": "This group of options represents a command-line flag/option. Flags explicitly added when running (ie: btm -a) will override this config file if an option is also set here",
+ "properties": {
+ "hide_avg_cpu": {
+ "default": false,
+ "description": "Whether to hide the average cpu entry",
+ "type": "boolean"
+ },
+ "dot_marker": {
+ "default": false,
+ "description": "Whether to use dot markers rather than braille",
+ "type": "boolean"
+ },
+ "rate": {
+ "default": 1000,
+ "description": "The update rate of the application",
+ "type": "integer"
+ },
+ "left_legend": {
+ "default": false,
+ "description": "Whether to put the CPU legend to the left",
+ "type": "boolean"
+ },
+ "current_usage": {
+ "default": false,
+ "description": "Whether to set CPU% on a process to be based on the total CPU or just current usage",
+ "type": "boolean"
+ },
+ "unnormalized_cpu": {
+ "default": false,
+ "description": "Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus)",
+ "type": "boolean"
+ },
+ "group_processes": {
+ "default": false,
+ "description": "Whether to group processes with the same name together by default",
+ "type": "boolean"
+ },
+ "case_sensitive": {
+ "default": false,
+ "description": "Whether to make process searching case sensitive by default",
+ "type": "boolean"
+ },
+ "whole_word": {
+ "default": false,
+ "description": "Whether to make process searching look for matching the entire word by default",
+ "type": "boolean"
+ },
+ "regex": {
+ "default": false,
+ "description": "Whether to make process searching use regex by default",
+ "type": "boolean"
+ },
+ "temperature_type": {
+ "default": "k",
+ "enum": ["k", "f", "c", "kelvin", "fahrenheit", "celsius"],
+ "description": "Defaults to Celsius",
+ "type": "string"
+ },
+ "default_time_value": {
+ "default": 60000,
+ "description": "The default time interval in milliseconds",
+ "type": "integer"
+ },
+ "time_delta": {
+ "default": 15000,
+ "description": "The time delta on each zoom in/out action in milliseconds",
+ "type": "integer"
+ },
+ "hide_time": {
+ "default": false,
+ "description": "Hides the time scale",
+ "type": "boolean"
+ },
+ "default_widget_type": {
+ "default": "proc",
+ "description": "Override layout default widget",
+ "type": "string"
+ },
+ "default_widget_count": {
+ "default": 1,
+ "description": "Override layout default widget",
+ "type": "integer"
+ },
+ "expanded_on_startup": {
+ "default": true,
+ "description": "Expand selected widget upon starting the app",
+ "type": "boolean"
+ },
+ "basic": {
+ "default": false,
+ "description": "Use basic mode",
+ "type": "boolean"
+ },
+ "use_old_network_legend": {
+ "default": false,
+ "description": "Use the old network legend style",
+ "type": "boolean"
+ },
+ "hide_table_gap": {
+ "default": false,
+ "description": "Remove space in tables",
+ "type": "boolean"
+ },
+ "battery": {
+ "default": false,
+ "description": "Show the battery widgets",
+ "type": "boolean"
+ },
+ "disable_click": {
+ "default": false,
+ "description": "Disable mouse clicks",
+ "type": "boolean"
+ },
+ "color": {
+ "default": "default",
+ "enum": [
+ "default",
+ "default-light",
+ "gruvbox",
+ "gruvbox-light",
+ "nord",
+ "nord-light"
+ ],
+ "description": "Built-in themes",
+ "type": "string"
+ },
+ "mem_as_value": {
+ "default": false,
+ "description": "Show memory values in the processes widget as values by default",
+ "type": "boolean"
+ },
+ "tree": {
+ "default": false,
+ "description": "Show tree mode by default in the processes widget",
+ "type": "boolean"
+ },
+ "show_table_scroll_position": {
+ "default": false,
+ "description": "Shows an indicator in table widgets tracking where in the list you are",
+ "type": "boolean"
+ },
+ "process_command": {
+ "default": false,
+ "description": "Show processes as their commands by default in the process widget",
+ "type": "boolean"
+ },
+ "network_use_binary_prefix": {
+ "default": false,
+ "description": "Displays the network widget with binary prefixes",
+ "type": "boolean"
+ },
+ "network_use_bytes": {
+ "default": false,
+ "description": "Displays the network widget using bytes",
+ "type": "boolean"
+ },
+ "network_use_log": {
+ "default": false,
+ "description": "Displays the network widget with a log scale",
+ "type": "boolean"
+ },
+ "disable_advanced_kill": {
+ "default": false,
+ "description": "Hides advanced options to stop a process on Unix-like systems",
+ "type": "boolean"
+ },
+ "enable_gpu_memory": {
+ "default": false,
+ "description": "Shows GPU(s) memory",
+ "type": "boolean"
+ },
+ "retention": {
+ "default": "10m",
+ "description": "How much data is stored at once in terms of time",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "colors": {
+ "description": "These are all the components that support custom theming. Note that colour support will depend on terminal support",
+ "properties": {
+ "table_header_color": {
+ "default": "LightBlue",
+ "description": "Represents the colour of table headers (processes, CPU, disks, temperature)",
+ "type": "string"
+ },
+ "widget_title_color": {
+ "default": "Gray",
+ "description": "Represents the colour of the label each widget has",
+ "type": "string"
+ },
+ "avg_cpu_color": {
+ "default": "Red",
+ "description": "Represents the average CPU color",
+ "type": "string"
+ },
+ "cpu_core_colors": {
+ "items": {
+ "uniqueItems": true,
+ "minItems": 1,
+ "type": "string"
+ },
+ "default": [
+ "LightMagenta",
+ "LightYellow",
+ "LightCyan",
+ "LightGreen",
+ "LightBlue",
+ "LightRed",
+ "Cyan",
+ "Green",
+ "Blue",
+ "Red"
+ ],
+ "description": "Represents the colour the core will use in the CPU legend and graph",
+ "type": "array"
+ },
+ "ram_color": {
+ "default": "LightMagenta",
+ "description": "Represents the colour RAM will use in the memory legend and graph",
+ "type": "string"
+ },
+ "swap_color": {
+ "default": "LightYellow",
+ "description": "Represents the colour SWAP will use in the memory legend and graph",
+ "type": "string"
+ },
+ "arc_color": {
+ "default": "LightCyan",
+ "description": "Represents the colour ARC will use in the memory legend and graph",
+ "type": "string"
+ },
+ "gpu_core_colors": {
+ "items": {
+ "uniqueItems": true,
+ "minItems": 1,
+ "type": "string"
+ },
+ "default": [
+ "LightGreen",
+ "LightBlue",
+ "LightRed",
+ "Cyan",
+ "Green",
+ "Blue",
+ "Red"
+ ],
+ "description": "Represents the colour the GPU will use in the memory legend and graph",
+ "type": "array"
+ },
+ "rx_color": {
+ "default": "LightCyan",
+ "description": "Represents the colour rx will use in the network legend and graph",
+ "type": "string"
+ },
+ "tx_color": {
+ "default": "LightGreen",
+ "description": "Represents the colour tx will use in the network legend and graph",
+ "type": "string"
+ },
+ "border_color": {
+ "default": "Gray",
+ "description": "Represents the colour of the border of unselected widgets",
+ "type": "string"
+ },
+ "highlighted_border_color": {
+ "default": "LightBlue",
+ "description": "Represents the colour of the border of selected widgets",
+ "type": "string"
+ },
+ "text_color": {
+ "default": "Gray",
+ "description": "Represents the colour of most text",
+ "type": "string"
+ },
+ "selected_text_color": {
+ "default": "Black",
+ "description": "Represents the colour of text that is selected",
+ "type": "string"
+ },
+ "selected_bg_color": {
+ "default": "LightBlue",
+ "description": "Represents the background colour of text that is selected",
+ "type": "string"
+ },
+ "graph_color": {
+ "default": "Gray",
+ "description": "Represents the colour of the lines and text of the graph",
+ "type": "string"
+ },
+ "high_battery_color": {
+ "default": "green",
+ "description": "Represents the colours of the battery based on charge",
+ "type": "string"
+ },
+ "medium_battery_color": {
+ "default": "yellow",
+ "description": "Represents the colours of the battery based on charge",
+ "type": "string"
+ },
+ "low_battery_color": {
+ "default": "red",
+ "description": "Represents the colours of the battery based on charge",
+ "type": "string"
+ }
+ }
+ },
+ "row": {
+ "$ref": "#/definitions/row"
+ },
+ "disk_filter": {
+ "$ref": "#/definitions/filter"
+ },
+ "mount_filter": {
+ "$ref": "#/definitions/filter"
+ },
+ "temp_filter": {
+ "$ref": "#/definitions/filter"
+ },
+ "net_filter": {
+ "$ref": "#/definitions/filter"
+ }
+ }
+}
diff --git a/scripts/schema/bad_file.toml b/scripts/schema/bad_file.toml
new file mode 100644
index 00000000..8107aef5
--- /dev/null
+++ b/scripts/schema/bad_file.toml
@@ -0,0 +1,2 @@
+[flags]
+hide_avg_cpu = 'bad'
diff --git a/scripts/schema/requirements.txt b/scripts/schema/requirements.txt
new file mode 100644
index 00000000..1db290b1
--- /dev/null
+++ b/scripts/schema/requirements.txt
@@ -0,0 +1,2 @@
+jsonschema-rs == 0.17.1
+toml == 0.10.2 \ No newline at end of file
diff --git a/scripts/schema/validator.py b/scripts/schema/validator.py
new file mode 100644
index 00000000..d537f928
--- /dev/null
+++ b/scripts/schema/validator.py
@@ -0,0 +1,55 @@
+#!/bin/python3
+
+# A simple script to validate that a schema is valid for a file.
+
+import argparse
+import toml
+import jsonschema_rs
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Validates a file against a JSON schema"
+ )
+ parser.add_argument(
+ "-f", "--file", type=str, required=True, help="The file to check."
+ )
+ parser.add_argument(
+ "-s", "--schema", type=str, required=True, help="The schema to use."
+ )
+ parser.add_argument(
+ "--should_fail",
+ required=False,
+ action="store_true",
+ help="Whether the checked file should fail.",
+ )
+ args = parser.parse_args()
+
+ file = args.file
+ schema = args.schema
+ should_fail = args.should_fail
+
+ with open(file) as f, open(schema) as s:
+ try:
+ validator = jsonschema_rs.JSONSchema.from_str(s.read())
+ except:
+ print("Coudln't create validator.")
+ exit()
+
+ is_valid = validator.is_valid(toml.load(f))
+ if is_valid:
+ if should_fail:
+ print("Fail!")
+ exit(1)
+ else:
+ print("All good!")
+ else:
+ if should_fail:
+ print("Caught error, good!")
+ else:
+ print("Fail!")
+ exit(1)
+
+
+if __name__ == "__main__":
+ main()