diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 28a68801..98669128 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,30 +25,6 @@ ## Guidelines -### Extend scripts - -- Create a [pull request](#Pull-Request-Process) for [application.yaml](./src/application/application.yaml) -- 🙏 For any new script, please add `revertCode` and `docs` values if possible. -- Structure of `script` object: - - `name`: *`string`* (**required**) - - Name of the script - - E.g. `Disable targeted ads` - - `code`: *`string`* (**required**) - - Batch file commands that will be executed - - `docs`: *`string`* | `[ string, ... ]` - - Documentation URL or list of URLs for those who wants to learn more about the script - - E.g. `https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_telemetry` - - `revertCode`: `string` - - Code that'll undo the change done by `code` property. - - E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1` - - then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0` - - `recommend`: `"standard"` | `"strict"` | `undefined` (default) - - If not defined then the script will not be recommended - - If defined it can be either - - `standard`: Will be recommended for general users - - `strict`: Will only be recommended with a warning -- See [typings](./src/application/application.yaml.d.ts) for documentation as code. - ### Handle the state in presentation layer - There are two types of components: diff --git a/README.md b/README.md index 1eaa73d2..37484d48 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,14 @@ - No need to run any compiled software that has access to your system, just run the generated scripts - Have full visibility into what the tweaks do as you enable them - Ability to revert (undo) applied scripts +- Everything is transparent: both application and its infrastructure are open-source and automated - Easily extendable -- Everything is open-source and automated (both application and its infrastructure) ## Extend scripts - Fork it & add more scripts in [application.yaml](src/application/application.yaml) and send a pull request 👌 -- 📖 More: [extend scripts | CONTRIBUTING.md](./CONTRIBUTING.md#extend-scripts) +- 📖 If you're unsure about the syntax you can refer to the [application file | documentation](docs/application-file.md). +- 🙏 For any new script, please add `revertCode` and `docs` values if possible. ## Commands diff --git a/docs/application-file.md b/docs/application-file.md new file mode 100644 index 00000000..39df51b4 --- /dev/null +++ b/docs/application-file.md @@ -0,0 +1,139 @@ +# Application file + +- privacy.sexy is a data-driven application where it reads the necessary OS-specific logic from [`application.yaml`](./../src/application/application.yaml) +- 💡 Best practices + - If you repeat yourself, try to utilize [YAML-defined functions](#function) + - Always try to add documentation and a way to revert a tweak in [scripts](#script) +- 📖 Types in code: [`application.d.ts`](./../src/application/application.yaml.d.ts) + +## Objects + +### `Application` + +- Application file simply defines different categories and their scripts in a tree structure. +- Application file also allows defining common [function](#function)s to be used throughout the application if you'd like different scripts to share same code. + +#### `Application` syntax + +- `actions: [` ***[`Category`](#Category)*** `, ... ]` **(required)** + - Each [category](#category) is rendered as different cards in card presentation. + - ❗ Application must consist of at least one category. +- `functions: [` ***[`Function`](#Function)*** `, ... ]` + - Functions are optionally defined to re-use the same code throughout different scripts. + +### `Category` + +- Category has a parent that has tree-like structure where it can have subcategories or subscripts. +- It's a logical grouping of different scripts and other categories. + +#### `Category` syntax + +- `category:` *`string`* (**required**) + - Name of the category + - ❗ Must be unique throughout the application +- `children: [` ***[`Category`](#category)*** `|` [***`Script`***](#Script) `, ... ]` (**required**) + - ❗ Category must consist of at least one subcategory or script. + - Children can be combination of scripts and subcategories. + +### `Script` + +- Script represents a single tweak. +- A script must include either: + - A `code` and `revertCode` + - Or `call` to call YAML-defined functions +- 🙏 For any new script, please add `revertCode` and `docs` values if possible. + +#### `Script` syntax + +- `name`: *`string`* (**required**) + - Name of the script + - ❗ Must be unique throughout the application + - E.g. `Disable targeted ads` +- `code`: *`string`* (may be **required**) + - Batch file commands that will be executed + - 💡 If defined, best practice to also define `revertCode` + - ❗ If not defined `call` must be defined, do not define if `call` is defined. +- `revertCode`: `string` + - Code that'll undo the change done by `code` property. + - E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1` + - then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0` + - ❗ Do not define if `call` is defined. +- `call`: ***[`FunctionCall`](#FunctionCall)*** | `[` ***[`FunctionCall`](#FunctionCall)*** `, ... ]` (may be **required**) + - A shared function or sequence of functions to call (called in order) + - ❗ If not defined `code` must be defined +- `docs`: *`string`* | `[`*`string`*`, ... ]` + - Single documentation URL or list of URLs for those who wants to learn more about the script + - E.g. `https://docs.microsoft.com/en-us/windows-server/` +- `recommend`: `"standard"` | `"strict"` | `undefined` (default) + - If not defined then the script will not be recommended + - If defined it can be either + - `standard`: Only non-breaking scripts without limiting OS functionality + - `strict`: Scripts that can break certain functionality in favor of privacy and security + +### `FunctionCall` + +- Describes a single call to a function by optionally providing values to its parameters. +- 👀 See [parameter substitution](#parameter-substitution) for an example usage + +#### `FunctionCall` syntax + +- `function`: *`string`* (**required**) + - Name of the function to call. + - ❗ Function with same name must defined in `functions` property of [Application](#application) +- `parameters`: `[ parameterName:` *`parameterValue`*`, ... ]` + - Defines key value dictionary for each parameter and its value + - E.g. + + ```yaml + parameters: + userDefinedParameterName: parameterValue + # ... + appName: Microsoft.WindowsFeedbackHub + ``` + +### `Function` + +- Functions allow re-usable code throughout the defined scripts. +- Functions are templates compiled by privacy.sexy and uses special expressions. +- Expressions are defined inside mustaches (double brackets, `{{` and `}}`) +- 👀 See [parameter substitution](#parameter-substitution) for an example usage + +#### Parameter substitution + +A simple function example + +```yaml + function: EchoArgument + parameters: [ 'argument' ] + code: Hello {{ $argument }} ! +``` + +It would print "Hello world" if it's called in a [script](#script) as following: + +```yaml + script: Echo script + call: + function: EchoArgument + parameters: + argument: World +``` + +#### `Function` syntax + +- `name`: *`string`* (**required**) + - Name of the function that scripts will use. + - Convention is to use camelCase, and be verbs. + - E.g. `uninstallStoreApp` + - ❗ Function names must be unique +- `parameters`: `[` *`string`* `, ... ]` + - Name of the parameters that the function has. + - Parameter values are provided by a [Script](#script) through a [FunctionCall](#functioncall) + - Parameter names must be defined to be used in expressions such as [parameter substitution](#parameter-substitution) + - ❗ Parameter names must be unique + `code`: *`string`* (**required**) + - Batch file commands that will be executed + - 💡 If defined, best practice to also define `revertCode` +- `revertCode`: *`string`* + - Code that'll undo the change done by `code` property. + - E.g. let's say `code` sets an environment variable as `setx POWERSHELL_TELEMETRY_OPTOUT 1` + - then `revertCode` should be doing `setx POWERSHELL_TELEMETRY_OPTOUT 0` diff --git a/src/application/Parser/ApplicationParser.ts b/src/application/Parser/ApplicationParser.ts index c4d499ea..35c3eb0d 100644 --- a/src/application/Parser/ApplicationParser.ts +++ b/src/application/Parser/ApplicationParser.ts @@ -4,14 +4,16 @@ import { IApplication } from '@/domain/IApplication'; import { IProjectInformation } from '@/domain/IProjectInformation'; import { ApplicationYaml } from 'js-yaml-loader!./../application.yaml'; import { parseCategory } from './CategoryParser'; -import { ProjectInformation } from '../../domain/ProjectInformation'; +import { ProjectInformation } from '@/domain/ProjectInformation'; +import { ScriptCompiler } from './Compiler/ScriptCompiler'; export function parseApplication(content: ApplicationYaml, env: NodeJS.ProcessEnv = process.env): IApplication { validate(content); + const compiler = new ScriptCompiler(content.functions); const categories = new Array(); for (const action of content.actions) { - const category = parseCategory(action); + const category = parseCategory(action, compiler); categories.push(category); } const info = readAppInformation(env); @@ -21,7 +23,7 @@ export function parseApplication(content: ApplicationYaml, env: NodeJS.ProcessEn return app; } -function readAppInformation(environment): IProjectInformation { +function readAppInformation(environment: NodeJS.ProcessEnv): IProjectInformation { return new ProjectInformation( environment.VUE_APP_NAME, environment.VUE_APP_VERSION, diff --git a/src/application/Parser/CategoryParser.ts b/src/application/Parser/CategoryParser.ts index 734cc430..d806c39d 100644 --- a/src/application/Parser/CategoryParser.ts +++ b/src/application/Parser/CategoryParser.ts @@ -3,6 +3,7 @@ import { Script } from '@/domain/Script'; import { Category } from '@/domain/Category'; import { parseDocUrls } from './DocumentationParser'; import { parseScript } from './ScriptParser'; +import { IScriptCompiler } from './Compiler/IScriptCompiler'; let categoryIdCounter: number = 0; @@ -11,14 +12,17 @@ interface ICategoryChildren { subScripts: Script[]; } -export function parseCategory(category: YamlCategory): Category { +export function parseCategory(category: YamlCategory, compiler: IScriptCompiler): Category { + if (!compiler) { + throw new Error('undefined compiler'); + } ensureValid(category); const children: ICategoryChildren = { subCategories: new Array(), subScripts: new Array