From c3ec15900d522d3c9b582e562f66c293931227b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 5 Oct 2024 12:16:45 +0000 Subject: [PATCH] deploy: ee0e666ba62fd2b8bb88a9f0ec0620695deb7520 --- 404.html | 8 +++---- FAQ/index.html | 8 +++---- assets/js/0569acb0.242560b0.js | 1 + assets/js/0569acb0.3d2a59c9.js | 1 - ...4eb39.e48e3af1.js => 0734eb39.17f20c53.js} | 2 +- ...9367a.f438f3be.js => 0a19367a.0015d164.js} | 2 +- assets/js/244c9605.8859fe18.js | 1 + assets/js/244c9605.d6c3fa12.js | 1 - assets/js/28f080da.36b2c0f7.js | 1 + assets/js/28f080da.e3b41090.js | 1 - ...e4fd2.12640d16.js => 3dce4fd2.4524ae0a.js} | 2 +- assets/js/5c5a410f.879d0746.js | 1 - assets/js/5c5a410f.e81f04e7.js | 1 + assets/js/68ffc9f1.5917f2f6.js | 1 - assets/js/68ffc9f1.d410a3f8.js | 1 + assets/js/75e434b4.f78cefdd.js | 1 - assets/js/75e434b4.facb9f0e.js | 1 + assets/js/89d906ef.7d9e8e09.js | 1 - assets/js/89d906ef.e683a8ca.js | 1 + assets/js/8c7895a0.a4e59a8f.js | 1 + assets/js/8c7895a0.d84cdd31.js | 1 - assets/js/91c76d4c.3d35b8ab.js | 1 - assets/js/91c76d4c.9a236c64.js | 1 + assets/js/935f2afb.c3cebc34.js | 1 - assets/js/935f2afb.cf6c6ad2.js | 1 + assets/js/93f138be.a9252a89.js | 1 + assets/js/93f138be.febc7216.js | 1 - assets/js/95ad2285.13354aaf.js | 1 + assets/js/9e4f9ffe.0c955d2d.js | 1 - assets/js/9e4f9ffe.4d9c6eb1.js | 1 + assets/js/a34125e0.358d48e2.js | 1 - assets/js/a34125e0.3ff6d5e2.js | 1 + assets/js/ab3e713c.4435bc0c.js | 1 - assets/js/ab3e713c.cada5228.js | 1 + ...da7f5.c2344d62.js => b5ada7f5.1886046d.js} | 2 +- ...8bc57.8d17d27a.js => cca8bc57.906ff5a6.js} | 2 +- assets/js/ce9192f0.d1cde3bf.js | 1 - assets/js/ce9192f0.f8871535.js | 1 + ...fc049.5a739529.js => d57fc049.c32b0ca1.js} | 2 +- ...4e480.72f8ee47.js => f744e480.0771c3e7.js} | 2 +- ...c0412.9b8e25c1.js => fbcc0412.90d01f02.js} | 2 +- ...65be6.d17666dc.js => fd565be6.55aba2ff.js} | 2 +- assets/js/main.24094120.js | 2 ++ ...CENSE.txt => main.24094120.js.LICENSE.txt} | 0 assets/js/main.3c6bb4ce.js | 2 -- assets/js/runtime~main.7f3052b1.js | 1 - assets/js/runtime~main.8a3c0337.js | 1 + community/index.html | 8 +++---- developers/how-to-build-plugins/index.html | 14 ++++++------- .../how-to-create-exhaust-script/index.html | 8 +++---- developers/how-to-refine-plugins/index.html | 14 ++++++------- developers/how-to-submit-plugins/index.html | 8 +++---- developers/how-to-write-unit-tests/index.html | 12 +++++------ developers/index.html | 8 +++---- index.html | 8 +++---- intro/index.html | 8 +++---- major-concepts/aggregation/index.html | 10 ++++----- major-concepts/design-philosophy/index.html | 8 +++---- major-concepts/exhaust-script/index.html | 10 ++++----- major-concepts/if/index.html | 10 ++++----- major-concepts/index.html | 8 +++---- major-concepts/manifest-file/index.html | 10 ++++----- major-concepts/pipelines/index.html | 10 ++++----- major-concepts/plugins/index.html | 8 +++---- major-concepts/regroup/index.html | 8 +++---- major-concepts/time/index.html | 10 ++++----- pipelines/cpu-to-carbon/index.html | 17 ++++++--------- pipelines/index.html | 10 ++++----- pipelines/instance-metadata/index.html | 10 ++++----- pipelines/sci/index.html | 10 ++++----- pipelines/teads/index.html | 21 +++++++++++++++++++ reference/cli/index.html | 10 ++++----- reference/errors/index.html | 14 ++++++------- reference/features/index.html | 10 ++++----- reference/index.html | 8 +++---- reference/plugins/index.html | 10 ++++----- sitemap.xml | 2 +- .../index.html | 10 ++++----- .../index.html | 10 ++++----- users/how-to-import-plugins/index.html | 10 ++++----- users/how-to-install-if/index.html | 8 +++---- .../how-to-use-the-explain-feature/index.html | 10 ++++----- .../index.html | 10 ++++----- users/how-to-write-manifests/index.html | 11 +++++----- users/index.html | 8 +++---- users/quick-start/index.html | 10 ++++----- 86 files changed, 238 insertions(+), 222 deletions(-) create mode 100644 assets/js/0569acb0.242560b0.js delete mode 100644 assets/js/0569acb0.3d2a59c9.js rename assets/js/{0734eb39.e48e3af1.js => 0734eb39.17f20c53.js} (55%) rename assets/js/{0a19367a.f438f3be.js => 0a19367a.0015d164.js} (53%) create mode 100644 assets/js/244c9605.8859fe18.js delete mode 100644 assets/js/244c9605.d6c3fa12.js create mode 100644 assets/js/28f080da.36b2c0f7.js delete mode 100644 assets/js/28f080da.e3b41090.js rename assets/js/{3dce4fd2.12640d16.js => 3dce4fd2.4524ae0a.js} (53%) delete mode 100644 assets/js/5c5a410f.879d0746.js create mode 100644 assets/js/5c5a410f.e81f04e7.js delete mode 100644 assets/js/68ffc9f1.5917f2f6.js create mode 100644 assets/js/68ffc9f1.d410a3f8.js delete mode 100644 assets/js/75e434b4.f78cefdd.js create mode 100644 assets/js/75e434b4.facb9f0e.js delete mode 100644 assets/js/89d906ef.7d9e8e09.js create mode 100644 assets/js/89d906ef.e683a8ca.js create mode 100644 assets/js/8c7895a0.a4e59a8f.js delete mode 100644 assets/js/8c7895a0.d84cdd31.js delete mode 100644 assets/js/91c76d4c.3d35b8ab.js create mode 100644 assets/js/91c76d4c.9a236c64.js delete mode 100644 assets/js/935f2afb.c3cebc34.js create mode 100644 assets/js/935f2afb.cf6c6ad2.js create mode 100644 assets/js/93f138be.a9252a89.js delete mode 100644 assets/js/93f138be.febc7216.js create mode 100644 assets/js/95ad2285.13354aaf.js delete mode 100644 assets/js/9e4f9ffe.0c955d2d.js create mode 100644 assets/js/9e4f9ffe.4d9c6eb1.js delete mode 100644 assets/js/a34125e0.358d48e2.js create mode 100644 assets/js/a34125e0.3ff6d5e2.js delete mode 100644 assets/js/ab3e713c.4435bc0c.js create mode 100644 assets/js/ab3e713c.cada5228.js rename assets/js/{b5ada7f5.c2344d62.js => b5ada7f5.1886046d.js} (63%) rename assets/js/{cca8bc57.8d17d27a.js => cca8bc57.906ff5a6.js} (55%) delete mode 100644 assets/js/ce9192f0.d1cde3bf.js create mode 100644 assets/js/ce9192f0.f8871535.js rename assets/js/{d57fc049.5a739529.js => d57fc049.c32b0ca1.js} (87%) rename assets/js/{f744e480.72f8ee47.js => f744e480.0771c3e7.js} (60%) rename assets/js/{fbcc0412.9b8e25c1.js => fbcc0412.90d01f02.js} (72%) rename assets/js/{fd565be6.d17666dc.js => fd565be6.55aba2ff.js} (51%) create mode 100644 assets/js/main.24094120.js rename assets/js/{main.3c6bb4ce.js.LICENSE.txt => main.24094120.js.LICENSE.txt} (100%) delete mode 100644 assets/js/main.3c6bb4ce.js delete mode 100644 assets/js/runtime~main.7f3052b1.js create mode 100644 assets/js/runtime~main.8a3c0337.js create mode 100644 pipelines/teads/index.html diff --git a/404.html b/404.html index 99a7bd40..2a748166 100644 --- a/404.html +++ b/404.html @@ -8,13 +8,13 @@ Page Not Found | Impact Framework - - + +
Skip to main content
This project is an incubation project being run inside the Green Software Foundation; as such, we DON’T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed.

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - + + \ No newline at end of file diff --git a/FAQ/index.html b/FAQ/index.html index 708f1f8a..2680c3b3 100644 --- a/FAQ/index.html +++ b/FAQ/index.html @@ -8,13 +8,13 @@ FAQs | Impact Framework - - + +
Skip to main content
This project is an incubation project being run inside the Green Software Foundation; as such, we DON’T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed.

FAQs

How can I contribute to Impact framework?

We welcome all kinds of contributions to Impact Framework, from bug reports to work on core features or documentation. For detailed instructions on how to start and what to expect when you contribute, please visit our contributions guidelines. There you will find guidance on how to raise bug reports, how to contribute code and what processes we have in place for handling your issues and PRs.

To contribute to these docs, you can raise pull requests against our Github repository.

In general, you can consider any ticket on our issue board open for community contributions if it does not have the core-only tag. We tag suitable introductory issues as good-first-issue - these are great places to get started.

Is there any way to auto-generate manifest files?

Not really. We provide a command line tool called if-env which will generate a template manifest for you if you run it in an empty folder. We also provide a built-in feature called mockObservations that will autogenerate some input data for you (designed to generate dummy data for experimenting or scenario testing). There are also importer plugins that will pull data from files or APIs into IF manifests. However, we do not currently provide any tooling that will generate a full manifest file for you - we think humans in the loop are important for ensuring the nuances of your system are captured in the manifest and in general, we'd prefer not to automate that away. That said, there is potential for tooling that helps abstract out the more repetitive tasks associated with building large manifest files.

Where do I get the data for my manifest file?

This is up to you! One of the strengths of the IF is that you can build manifests your way. We expect input data to come from an external file or an API in most cases, although you can manually add input data into your manifest too. People have built importer plugins for Azure's monitor API, Prometheus databases, CSV files, Datadog, and others. These importer plugins can be found on the Explorer website.

What is your vision for the IF?

The vision is to build a protocol that enables you to calculate the appropriate environmental impact for your application, whatever it is. We want to be an open communication standard - our dream is that an IF manifest is the primary way that people share the environmental impacts of their systems.

Right now we consider our core functionality to be calculating software carbon intensity scores for software applications running in the cloud, but we have people using IF on-premise and even for supply chain modeling and other non-software applications. The idea is that we provide the minimal infrastructure required for you to build up different use cases using plugins.

Is there a way to generate an audit/report along with a csv/yaml output?

No - we see the manifest file and associated manifest as a new type of "executable audit". The IF itself does not generate any other form of report, although there is nothing stopping others from building out this functionality on top of IF.

IF makes things transparent, but how can you fight against users inputting incorrect or misleading data?

We can't really stop people providing fake data into a manifest file. We're very interested in ways we can verify that the computation was done correctly and provide public proofs, but without direct access to a user's systems we can never guarantee they are providing truthful data. This is not unique to IF - fraud is a problem across all industries.

Is it planned to make the IF more user-friendly or also more usable for less technical people?

IF is a low-level infrastructure project. The core team focuses on building out solid foundations for others to build UIs, apps etc on top. There is no official IF UI.

Does it only calculate emissions for CPU usage or does it also work with other meters like storage/bandwidth?

We do have basic models for memory and network usage in addition to CPU. You can create plugins for anything you can observe and model.

Have you compared the results to the carbon reports that cloud providers are producing for their customers?

No we haven't - we've been focussing on capturing impacts of individual applications over relatively short periods so far. It's difficult to find the underlying data to build the comparisons.

Are you planning to support e.g. for PaaS, IaaS, alternative cloud providers...etc?

We are focused on building out the low-level core infrastructure so that users can apply IF to any use case. We do not currently intend to build out support for any specific service. We want users to build plugins and publish them to the Explorer and share manifests to support individual use cases.

What is on your roadmap?

You can check what's coming up for IF core development by checking our Github discussion board. We publish our plans in advance of incorporating them into our development sprints so that the community can comment, ask for more information and report any potential impact for their specific use cases.

- - + + \ No newline at end of file diff --git a/assets/js/0569acb0.242560b0.js b/assets/js/0569acb0.242560b0.js new file mode 100644 index 00000000..800f4e51 --- /dev/null +++ b/assets/js/0569acb0.242560b0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[796],{4137:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>d});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),m=p(t),d=i,g=m["".concat(l,".").concat(d)]||m[d]||u[d]||o;return t?a.createElement(g,r(r({ref:n},c),{},{components:t})):a.createElement(g,r({ref:n},c))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,r=new Array(o);r[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var a=t(7462),i=(t(7294),t(4137));const o={"sidebar-position":1},r="Grabbing instance metadata from a CSV file",s={unversionedId:"pipelines/instance-metadata",id:"pipelines/instance-metadata",title:"Grabbing instance metadata from a CSV file",description:"Observations",source:"@site/docs/pipelines/instance-metadata.md",sourceDirName:"pipelines",slug:"/pipelines/instance-metadata",permalink:"/pipelines/instance-metadata",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/instance-metadata.md",tags:[],version:"current",frontMatter:{"sidebar-position":1},sidebar:"tutorialSidebar",previous:{title:"Teads CPU pipeline",permalink:"/pipelines/teads"},next:{title:"Software Carbon Intensity (SCI)",permalink:"/pipelines/sci"}},l={},p=[{value:"Observations",id:"observations",level:2},{value:"Impacts",id:"impacts",level:2},{value:"Scope",id:"scope",level:2},{value:"Description",id:"description",level:2},{value:"Tags",id:"tags",level:2},{value:"Common Patterns",id:"common-patterns",level:2},{value:"Assumptions and limitations",id:"assumptions-and-limitations",level:2},{value:"Components",id:"components",level:2},{value:"Plugins",id:"plugins",level:2},{value:"csv-lookup",id:"csv-lookup",level:3},{value:"config",id:"config",level:4},{value:"regex",id:"regex",level:3},{value:"config",id:"config-1",level:4},{value:"Manifest",id:"manifest",level:2}],c={toc:p};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"grabbing-instance-metadata-from-a-csv-file"},"Grabbing instance metadata from a CSV file"),(0,i.kt)("h2",{id:"observations"},"Observations"),(0,i.kt)("p",null,"This manifest requires the following observations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"name of the specific cloud instance being used")),(0,i.kt)("h2",{id:"impacts"},"Impacts"),(0,i.kt)("p",null,"This pipeline looks up metadata associated with the given cloud instance. It does not generate impacts per se, it just retrieves additional data from an external file using the given instance name as a search key."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This pipeline is likely to be used as part of a larger pipeline. All we are doing here is retrieving metadata from an external file. Typicaly, this metadata will be used to feed further plugind to support impactestimates."),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("p",null,"The instance metadata pipeline simply looks up a metadata for a given virtual machine instance name using the ",(0,i.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin from the IF standard library. However, the target dataset can return multiple processor names for a given VM instance where there are multiple possibilitiers. This means we need to create a pipeline that includes the ",(0,i.kt)("inlineCode",{parentName:"p"},"regex")," plugin so parse out just one of the possible values."),(0,i.kt)("p",null,"For this demo we'll just extract the first value if there are multiple available for the ",(0,i.kt)("inlineCode",{parentName:"p"},"processor-name"),"."),(0,i.kt)("h2",{id:"tags"},"Tags"),(0,i.kt)("p",null,"csv, instance-metadata, regex"),(0,i.kt)("h2",{id:"common-patterns"},"Common Patterns"),(0,i.kt)("p",null,"The lookup process described on this page will likely be a common pattern used in other pipelines."),(0,i.kt)("h2",{id:"assumptions-and-limitations"},"Assumptions and limitations"),(0,i.kt)("p",null,"The following are assumed to be true in this manifest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"the target dataset is up to date"),(0,i.kt)("li",{parentName:"ul"},"where there are multiple possible processors associated with an instance name, it is appropriate to select the first in the list.")),(0,i.kt)("h2",{id:"components"},"Components"),(0,i.kt)("p",null,"There is only one component in this example. It represents the entire application. The component pipeline looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"pipeline:\n compute:\n - cloud-instance-metadata\n - extract-processor-name\n")),(0,i.kt)("h2",{id:"plugins"},"Plugins"),(0,i.kt)("h3",{id:"csv-lookup"},"csv-lookup"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin is used once. The instance is named ",(0,i.kt)("inlineCode",{parentName:"p"},"cloud-instance-metadata"),". It targets a csv file in our ",(0,i.kt)("inlineCode",{parentName:"p"},"if-data")," repository."),(0,i.kt)("h4",{id:"config"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},'cloud-instance-metadata:\nfilepath: https://raw.githubusercontent.com/Green-Software-Foundation/if-data/main/cloud-metdata-azure-instances.csv\nquery: instance-class: "cloud/instance-type"\noutput: "*"\n')),(0,i.kt)("h3",{id:"regex"},"regex"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"regex")," plugin is used once. The instance is named ",(0,i.kt)("inlineCode",{parentName:"p"},"extract-processor-name"),". It parses the response from the csv lookup plugin and extracts the first entry from the returned list."),(0,i.kt)("h4",{id:"config-1"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"extract-processor-name:\n method: Regex\n path: 'builtin'\n config:\n parameter: cpu-model-name\n match: /^([^,])+/g\n output: cpu/name\n")),(0,i.kt)("h2",{id:"manifest"},"Manifest"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: instance-metadata\ndescription:\ntags:\ninitialize:\n plugins:\n cloud-instance-metadata:\n method: CSVLookup\n path: 'builtin'\n config:\n filepath: https://raw.githubusercontent.com/Green-Software-Foundation/if-data/main/cloud-metdata-azure-instances.csv\n query:\n instance-class: 'cloud/instance-type'\n output: '*'\n extract-processor-name:\n method: Regex\n path: 'builtin'\n config:\n parameter: cpu-model-name\n match: /^([^,])+/g\n output: cpu/name\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - cloud-instance-metadata\n - extract-processor-name\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n cloud/provider: gcp\n cloud/region: asia-east\n cloud/instance-type: Standard_A1_v2\n")),(0,i.kt)("p",null,"Now you can run this manifest using:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m instance-metadata.yml -o output.yml\n")),(0,i.kt)("p",null,"Your new ",(0,i.kt)("inlineCode",{parentName:"p"},"output.yml")," file will contain the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: csv-demo\ndescription: null\ntags: null\ninitialize:\n plugins:\n cloud-instance-metadata:\n path: builtin\n method: CSVLookup\n config:\n filepath: >-\n https://raw.githubusercontent.com/Green-Software-Foundation/if-data/main/cloud-metdata-azure-instances.csv\n query:\n instance-class: cloud/instance-type\n output: '*'\n extract-processor-name:\n path: builtin\n method: Regex\n config:\n parameter: cpu-model-name\n match: /^([^,])+/g\n output: cpu/name\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/Code/if/src/index.ts -m manifests/examples/instance-metadata.yml\n environment:\n if-version: 0.6.0\n os: macOS\n os-version: 14.6.1\n node-version: 18.20.4\n date-time: 2024-10-03T15:15:36.328Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.25'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.23.8\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - cloud-instance-metadata\n - extract-processor-name\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n cloud/provider: gcp\n cloud/region: asia-east\n cloud/instance-type: Standard_A1_v2\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n cloud/provider: gcp\n cloud/region: asia-east\n cloud/instance-type: Standard_A1_v2\n cpu-cores-available: 52\n cpu-cores-utilized: 1\n cpu-manufacturer: Intel\n cpu-model-name: >-\n Intel\xae Xeon\xae Platinum 8272CL,Intel\xae Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae\n E5-2673 v4 2.3 GHz,Intel\xae Xeon\xae E5-2673 v3 2.4 GHz\n cpu-tdp: 205\n gpu-count: nan\n gpu-model-name: nan\n gpu-tdp: nan\n memory-available: 2\n cpu/name: Intel\xae Xeon\xae Platinum 8272CL\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0569acb0.3d2a59c9.js b/assets/js/0569acb0.3d2a59c9.js deleted file mode 100644 index 0702de6d..00000000 --- a/assets/js/0569acb0.3d2a59c9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[796],{4137:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>d});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=a.createContext({}),p=function(e){var n=a.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(l.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),m=p(t),d=i,f=m["".concat(l,".").concat(d)]||m[d]||u[d]||o;return t?a.createElement(f,r(r({ref:n},c),{},{components:t})):a.createElement(f,r({ref:n},c))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,r=new Array(o);r[0]=m;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var p=2;p{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var a=t(7462),i=(t(7294),t(4137));const o={"sidebar-position":1},r="Grabbing instance metadata from a CSV file",s={unversionedId:"pipelines/instance-metadata",id:"pipelines/instance-metadata",title:"Grabbing instance metadata from a CSV file",description:"Observations",source:"@site/docs/pipelines/instance-metadata.md",sourceDirName:"pipelines",slug:"/pipelines/instance-metadata",permalink:"/pipelines/instance-metadata",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/instance-metadata.md",tags:[],version:"current",frontMatter:{"sidebar-position":1},sidebar:"tutorialSidebar",previous:{title:"From CPU utilization to carbon emissions",permalink:"/pipelines/cpu-to-carbon"},next:{title:"Software Carbon Intensity (SCI)",permalink:"/pipelines/sci"}},l={},p=[{value:"Observations",id:"observations",level:2},{value:"Impacts",id:"impacts",level:2},{value:"Scope",id:"scope",level:2},{value:"Description",id:"description",level:2},{value:"Tags",id:"tags",level:2},{value:"Common Patterns",id:"common-patterns",level:2},{value:"Assumptions and limitations",id:"assumptions-and-limitations",level:2},{value:"Components",id:"components",level:2},{value:"Plugins",id:"plugins",level:2},{value:"csv-lookup",id:"csv-lookup",level:3},{value:"config",id:"config",level:4},{value:"regex",id:"regex",level:3},{value:"config",id:"config-1",level:4},{value:"Manifest",id:"manifest",level:2}],c={toc:p};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"grabbing-instance-metadata-from-a-csv-file"},"Grabbing instance metadata from a CSV file"),(0,i.kt)("h2",{id:"observations"},"Observations"),(0,i.kt)("p",null,"This manifest requires the following observations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"name of the specific cloud instance being used")),(0,i.kt)("h2",{id:"impacts"},"Impacts"),(0,i.kt)("p",null,"This pipeline looks up metadata associated with the given cloud instance. It does not generate impacts per se, it just retrieves additional data from an external file using the given instance name as a search key."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This pipeline is likely to be used as part of a larger pipeline. All we are doing here is retrieving metadata from an external file. Typicaly, this metadata will be used to feed further plugind to support impactestimates."),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("p",null,"The instance metadata pipeline simply looks up a metadata for a given virtual machine instance name using the ",(0,i.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin from the IF standard library. However, the target dataset can return multiple processor names for a given VM instance where there are multiple possibilitiers. This means we need to create a pipeline that includes the ",(0,i.kt)("inlineCode",{parentName:"p"},"regex")," plugin so parse out just one of the possible values."),(0,i.kt)("p",null,"For this demo we'll just extract the first value if there are multiple available for the ",(0,i.kt)("inlineCode",{parentName:"p"},"processor-name"),"."),(0,i.kt)("h2",{id:"tags"},"Tags"),(0,i.kt)("p",null,"csv, instance-metadata, regex"),(0,i.kt)("h2",{id:"common-patterns"},"Common Patterns"),(0,i.kt)("p",null,"The lookup process described on this page will likely be a common pattern used in other pipelines."),(0,i.kt)("h2",{id:"assumptions-and-limitations"},"Assumptions and limitations"),(0,i.kt)("p",null,"The following are assumed to be true in this manifest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"the target dataset is up to date"),(0,i.kt)("li",{parentName:"ul"},"where there are multiple possible processors associated with an instance name, it is appropriate to select the first in the list.")),(0,i.kt)("h2",{id:"components"},"Components"),(0,i.kt)("p",null,"There is only one component in this example. It represents the entire application. The component pipeline looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"pipeline:\n compute:\n - cloud-instance-metadata\n - extract-processor-name\n")),(0,i.kt)("h2",{id:"plugins"},"Plugins"),(0,i.kt)("h3",{id:"csv-lookup"},"csv-lookup"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin is used once. The instance is named ",(0,i.kt)("inlineCode",{parentName:"p"},"cloud-instance-metadata"),". It targets a csv file in our ",(0,i.kt)("inlineCode",{parentName:"p"},"if-data")," repository. "),(0,i.kt)("h4",{id:"config"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},'cloud-instance-metadata:\nfilepath: https://raw.githubusercontent.com/Green-Software-Foundation/if-data/main/cloud-metdata-azure-instances.csv\nquery: instance-class: "cloud/instance-type"\noutput: "*"\n')),(0,i.kt)("h3",{id:"regex"},"regex"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"regex")," plugin is used once. The instance is named ",(0,i.kt)("inlineCode",{parentName:"p"},"extract-processor-name"),". It parses the response from the csv lookup plugin and extracts the first entry from the returned list."),(0,i.kt)("h4",{id:"config-1"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"extract-processor-name:\nparameter: cpu-model-name\nmatch: /^([^,])+/g\noutput: cpu/name\n")),(0,i.kt)("h2",{id:"manifest"},"Manifest"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},'name: instance-metadata\ndescription:\ntags:\ninitialize:\n plugins:\n cloud-instance-metadata:\n method: CSVLookup\n path: "builtin"\n global-config:\n filepath: https://raw.githubusercontent.com/Green-Software-Foundation/if-data/main/cloud-metdata-azure-instances.csv\n query:\n instance-class: "cloud/instance-type"\n output: "*"\n extract-processor-name:\n method: Regex\n path: "builtin"\n global-config:\n parameter: cpu-model-name\n match: /^([^,])+/g\n output: cpu/name\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - cloud-instance-metadata\n - extract-processor-name\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n cloud/provider: gcp\n cloud/region: asia-east\n cloud/instance-type: Standard_A1_v2\n')),(0,i.kt)("p",null,"Now you can run this manifest using:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m instance-metadata.yml -o output.yml\n")),(0,i.kt)("p",null,"Your new ",(0,i.kt)("inlineCode",{parentName:"p"},"output.yml")," file will contain the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: csv-demo\ndescription: null\ntags: null\ninitialize:\n plugins:\n cloud-instance-metadata:\n path: builtin\n method: CSVLookup\n global-config:\n filepath: >-\n https://raw.githubusercontent.com/Green-Software-Foundation/if-data/main/cloud-metdata-azure-instances.csv\n query:\n instance-class: cloud/instance-type\n output: '*'\n extract-processor-name:\n path: builtin\n method: Regex\n global-config:\n parameter: cpu-model-name\n match: /^([^,])+/g\n output: cpu/name\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/Code/if/src/index.ts -m manifests/examples/instance-metadata.yml\n environment:\n if-version: 0.3.3-beta.0\n os: linux\n os-version: 5.15.0-107-generic\n node-version: 21.4.0\n date-time: 2024-06-06T15:21:50.108Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-unofficial-plugins@v0.3.1'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - cloud-instance-metadata\n - extract-processor-name\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n cloud/provider: gcp\n cloud/region: asia-east\n cloud/instance-type: Standard_A1_v2\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n cloud/provider: gcp\n cloud/region: asia-east\n cloud/instance-type: Standard_A1_v2\n cpu-cores-available: 52\n cpu-cores-utilized: 1\n cpu-manufacturer: Intel\n cpu-model-name: >-\n Intel\xae Xeon\xae Platinum 8272CL,Intel\xae Xeon\xae 8171M 2.1 GHz,Intel\xae Xeon\xae\n E5-2673 v4 2.3 GHz,Intel\xae Xeon\xae E5-2673 v3 2.4 GHz\n cpu-tdp: 205\n gpu-count: nan\n gpu-model-name: nan\n gpu-tdp: nan\n memory-available: 2\n cpu/name: Intel\xae Xeon\xae Platinum 8272CL\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0734eb39.e48e3af1.js b/assets/js/0734eb39.17f20c53.js similarity index 55% rename from assets/js/0734eb39.e48e3af1.js rename to assets/js/0734eb39.17f20c53.js index 2464449b..e01fc452 100644 --- a/assets/js/0734eb39.e48e3af1.js +++ b/assets/js/0734eb39.17f20c53.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[939],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),u=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=u(e.components);return i.createElement(l.Provider,{value:t},e.children)},f={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=u(n),d=r,m=c["".concat(l,".").concat(d)]||c[d]||f[d]||o;return n?i.createElement(m,a(a({ref:t},p),{},{components:n})):i.createElement(m,a({ref:t},p))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,a=new Array(o);a[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,a[1]=s;for(var u=2;u{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>f,frontMatter:()=>o,metadata:()=>s,toc:()=>u});var i=n(7462),r=(n(7294),n(4137));const o={sidebar_position:6},a="How to compare files with `if-diff`",s={unversionedId:"users/how-to-compare-files-with-if-diff",id:"users/how-to-compare-files-with-if-diff",title:"How to compare files with `if-diff`",description:"if-diff is a command line tool that allows you to compare two if-run output files. They either match according to if-diff's matching rules, or they don't. If they match, then if-diff returns a simple success response. If the differ, then if-diff returns a report of the differences it finds.",source:"@site/docs/users/how-to-compare-files-with-if-diff.md",sourceDirName:"users",slug:"/users/how-to-compare-files-with-if-diff",permalink:"/users/how-to-compare-files-with-if-diff",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-compare-files-with-if-diff.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Exporting CSV file with `if-csv`",permalink:"/users/how-to-export-csv-file-with-if-csv"},next:{title:"Verifying IF outputs with `if-check`",permalink:"/users/how-to-verify-files-with-if-check"}},l={},u=[{value:"Why is this useful?",id:"why-is-this-useful",level:2},{value:"Example: output verification",id:"example-output-verification",level:2},{value:"Example: debugging",id:"example-debugging",level:2}],p={toc:u};function f(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"how-to-compare-files-with-if-diff"},"How to compare files with ",(0,r.kt)("inlineCode",{parentName:"h1"},"if-diff")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," is a command line tool that allows you to compare two ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," output files. They either match according to ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff"),"'s matching rules, or they don't. If they match, then ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," returns a simple success response. If the differ, then ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," returns a report of the differences it finds."),(0,r.kt)("h2",{id:"why-is-this-useful"},"Why is this useful?"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," can be used to verify that a given output file was correctly executed and that it was not tampered with after it was computed. Imagine you received an output file from someone, reporting their carbon expenditure. It is better to verify than trust this person's report, so you simply delete the ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs")," block from the file (creating a manifest), run it through ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," and compare the output to the original file you received. All being well, the two files are identical. if not, you can see exactly where the differences are."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," can also be used for debugging your own files. Maybe you have some large manifest files in development and have accidentally introduced some changes that you are now struggling to identify, but are leading to different ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," outcomes. ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," can quickly scan your two files and tell you where the differences are so you can get back in sync."),(0,r.kt)("h2",{id:"example-output-verification"},"Example: output verification"),(0,r.kt)("p",null,"Let's say someone provides you with this output file, ",(0,r.kt)("inlineCode",{parentName:"p"},"given-output-file.yml"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config:\n sum:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n energy: 0.0005\n")),(0,r.kt)("p",null,"This manifest simply sums two components, ",(0,r.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"network/energy")," and assigns the result to ",(0,r.kt)("inlineCode",{parentName:"p"},"energy")," in the outputs array. You receive this file and feel like something's not quite right. So you delete the outputs block to create ",(0,r.kt)("inlineCode",{parentName:"p"},"test-manifest.yml"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sum:\n method: Sum\n path: \"builtin\"\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config:\n sum:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n")),(0,r.kt)("p",null,"Now you want to ",(0,r.kt)("em",{parentName:"p"},"run")," the manifest through ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," and compare the result to the given output file. You can do this by piping the result of ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," directly into ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"if-run -m test-manifest.yml | if-diff --target given-output-file.yml\n")),(0,r.kt)("p",null,"The result is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"Files do not match!\ntree.children.child[0].energy\nsource: 0.002\ntarget: 0.0005\n")),(0,r.kt)("p",null,"Uh oh. It seems there has been some mistake or tampering with the outputs in ",(0,r.kt)("inlineCode",{parentName:"p"},"given-output-file.yml"),". The right result of summing ",(0,r.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"network/energy")," is 0.002, but they reported 0.0005. You can now query that result with the sender and ask them to fix it."),(0,r.kt)("p",null,"Obviously, this is an arbitrary, simplified example, but ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," enables you to do this kind of output verification on very complex manifests where errors are harder to spot by eye and to do it programmatically over large numbers of files."),(0,r.kt)("h2",{id:"example-debugging"},"Example: debugging"),(0,r.kt)("p",null,"Imagine you developed a manifest that was giving you a consistent result, but now when you run it your result is different and you are not sure why. Maybe one of your colleagues changed something and forgot to tell you, maybe you accidentally inserted or removed something while you were working."),(0,r.kt)("p",null,"You could revert to an archived version, but you moved all the components around into a structure you prefer! ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," has you covered. It will step through the files identifying all the functional differences between the files to help you identify the problematic one(s)."),(0,r.kt)("p",null,"You have ",(0,r.kt)("inlineCode",{parentName:"p"},"original-manifest.yml")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"new-manifest.yml"),". Pass them to ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"if-diff --source original-manifest.yml --target new-manifest.yml\n")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," will report each difference it finds. You can fix the difference and run ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," again until you get a success response - since ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," ignores positional differences and only considers differences in ",(0,r.kt)("inlineCode",{parentName:"p"},"context")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"tree")," keys and values, your two manifests will run identically even though you persist your tree reorganization."))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[939],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),u=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=u(e.components);return i.createElement(l.Provider,{value:t},e.children)},f={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},c=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=u(n),d=r,m=c["".concat(l,".").concat(d)]||c[d]||f[d]||o;return n?i.createElement(m,a(a({ref:t},p),{},{components:n})):i.createElement(m,a({ref:t},p))}));function d(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,a=new Array(o);a[0]=c;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,a[1]=s;for(var u=2;u{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>f,frontMatter:()=>o,metadata:()=>s,toc:()=>u});var i=n(7462),r=(n(7294),n(4137));const o={sidebar_position:6},a="How to compare files with `if-diff`",s={unversionedId:"users/how-to-compare-files-with-if-diff",id:"users/how-to-compare-files-with-if-diff",title:"How to compare files with `if-diff`",description:"if-diff is a command line tool that allows you to compare two if-run output files. They either match according to if-diff's matching rules, or they don't. If they match, then if-diff returns a simple success response. If the differ, then if-diff returns a report of the differences it finds.",source:"@site/docs/users/how-to-compare-files-with-if-diff.md",sourceDirName:"users",slug:"/users/how-to-compare-files-with-if-diff",permalink:"/users/how-to-compare-files-with-if-diff",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-compare-files-with-if-diff.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Exporting CSV file with `if-csv`",permalink:"/users/how-to-export-csv-file-with-if-csv"},next:{title:"Verifying IF outputs with `if-check`",permalink:"/users/how-to-verify-files-with-if-check"}},l={},u=[{value:"Why is this useful?",id:"why-is-this-useful",level:2},{value:"Example: output verification",id:"example-output-verification",level:2},{value:"Example: debugging",id:"example-debugging",level:2}],p={toc:u};function f(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"how-to-compare-files-with-if-diff"},"How to compare files with ",(0,r.kt)("inlineCode",{parentName:"h1"},"if-diff")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," is a command line tool that allows you to compare two ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," output files. They either match according to ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff"),"'s matching rules, or they don't. If they match, then ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," returns a simple success response. If the differ, then ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," returns a report of the differences it finds."),(0,r.kt)("h2",{id:"why-is-this-useful"},"Why is this useful?"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," can be used to verify that a given output file was correctly executed and that it was not tampered with after it was computed. Imagine you received an output file from someone, reporting their carbon expenditure. It is better to verify than trust this person's report, so you simply delete the ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs")," block from the file (creating a manifest), run it through ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," and compare the output to the original file you received. All being well, the two files are identical. if not, you can see exactly where the differences are."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," can also be used for debugging your own files. Maybe you have some large manifest files in development and have accidentally introduced some changes that you are now struggling to identify, but are leading to different ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," outcomes. ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," can quickly scan your two files and tell you where the differences are so you can get back in sync."),(0,r.kt)("h2",{id:"example-output-verification"},"Example: output verification"),(0,r.kt)("p",null,"Let's say someone provides you with this output file, ",(0,r.kt)("inlineCode",{parentName:"p"},"given-output-file.yml"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n energy: 0.0005\n")),(0,r.kt)("p",null,"This manifest simply sums two components, ",(0,r.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"network/energy")," and assigns the result to ",(0,r.kt)("inlineCode",{parentName:"p"},"energy")," in the outputs array. You receive this file and feel like something's not quite right. So you delete the outputs block to create ",(0,r.kt)("inlineCode",{parentName:"p"},"test-manifest.yml"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n")),(0,r.kt)("p",null,"Now you want to ",(0,r.kt)("em",{parentName:"p"},"run")," the manifest through ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," and compare the result to the given output file. You can do this by piping the result of ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," directly into ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"if-run -m test-manifest.yml | if-diff --target given-output-file.yml\n")),(0,r.kt)("p",null,"The result is:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"Files do not match!\ntree.children.child[0].energy\nsource: 0.002\ntarget: 0.0005\n")),(0,r.kt)("p",null,"Uh oh. It seems there has been some mistake or tampering with the outputs in ",(0,r.kt)("inlineCode",{parentName:"p"},"given-output-file.yml"),". The right result of summing ",(0,r.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"network/energy")," is 0.002, but they reported 0.0005. You can now query that result with the sender and ask them to fix it."),(0,r.kt)("p",null,"Obviously, this is an arbitrary, simplified example, but ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," enables you to do this kind of output verification on very complex manifests where errors are harder to spot by eye and to do it programmatically over large numbers of files."),(0,r.kt)("h2",{id:"example-debugging"},"Example: debugging"),(0,r.kt)("p",null,"Imagine you developed a manifest that was giving you a consistent result, but now when you run it your result is different and you are not sure why. Maybe one of your colleagues changed something and forgot to tell you, maybe you accidentally inserted or removed something while you were working."),(0,r.kt)("p",null,"You could revert to an archived version, but you moved all the components around into a structure you prefer! ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," has you covered. It will step through the files identifying all the functional differences between the files to help you identify the problematic one(s)."),(0,r.kt)("p",null,"You have ",(0,r.kt)("inlineCode",{parentName:"p"},"original-manifest.yml")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"new-manifest.yml"),". Pass them to ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"if-diff --source original-manifest.yml --target new-manifest.yml\n")),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," will report each difference it finds. You can fix the difference and run ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," again until you get a success response - since ",(0,r.kt)("inlineCode",{parentName:"p"},"if-diff")," ignores positional differences and only considers differences in ",(0,r.kt)("inlineCode",{parentName:"p"},"context")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"tree")," keys and values, your two manifests will run identically even though you persist your tree reorganization."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0a19367a.f438f3be.js b/assets/js/0a19367a.0015d164.js similarity index 53% rename from assets/js/0a19367a.f438f3be.js rename to assets/js/0a19367a.0015d164.js index 01a27e38..fc264be3 100644 --- a/assets/js/0a19367a.f438f3be.js +++ b/assets/js/0a19367a.0015d164.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[109],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=u(n),f=r,d=m["".concat(s,".").concat(f)]||m[f]||c[f]||i;return n?a.createElement(d,o(o({ref:t},p),{},{components:n})):a.createElement(d,o({ref:t},p))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var a=n(7462),r=(n(7294),n(4137));const i={sidebar_position:1},o="Quick start",l={unversionedId:"users/quick-start",id:"users/quick-start",title:"Quick start",description:"This page will provide the basic instructions for getting up and running with Impact Framework.",source:"@site/docs/users/quick-start.md",sourceDirName:"users",slug:"/users/quick-start",permalink:"/users/quick-start",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/quick-start.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Users",permalink:"/users/"},next:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"}},s={},u=[{value:"1: Install Impact Framework",id:"1-install-impact-framework",level:2},{value:"2: Create a manifest file",id:"2-create-a-manifest-file",level:2},{value:"3: Compute your manifest file",id:"3-compute-your-manifest-file",level:2},{value:"Next steps",id:"next-steps",level:2}],p={toc:u};function c(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"quick-start"},"Quick start"),(0,r.kt)("p",null,"This page will provide the basic instructions for getting up and running with Impact Framework."),(0,r.kt)("h2",{id:"1-install-impact-framework"},"1: Install Impact Framework"),(0,r.kt)("p",null,"Install the Impact Framework globally using npm."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if\n")),(0,r.kt)("p",null,"Read our detailed guide to ",(0,r.kt)("a",{parentName:"p",href:"/users/how-to-install-if"},"installing IF"),"."),(0,r.kt)("h2",{id:"2-create-a-manifest-file"},"2: Create a manifest file"),(0,r.kt)("p",null,"A manifest file contains all the configuration and input data required to measure your application's energy and carbon impacts and should have a ",(0,r.kt)("inlineCode",{parentName:"p"},".yml")," extension. "),(0,r.kt)("p",null,"Open the file, add your data and save the file. The minimal example below runs two snapshot observations through a single plugin - all it does is multiply a value in each element of the ",(0,r.kt)("inlineCode",{parentName:"p"},"input")," data by 2."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},'name: basic-demo\ndescription:\ntags:\ninitialize:\n plugins:\n double-a-value: \n path: \'builtin\'\n method: Coefficient\n config:\n input-parameter: "cpu-utilization"\n coefficient: 2\n output-parameter: "cpu-utilization-doubled"\ntree:\n children:\n child-0:\n defaults:\n cpu/thermal-design-power: 100\n pipeline:\n observe:\n regroup:\n compute:\n - double-a-value\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 1\n cpu/utilization: 20\n - timestamp: 2023-07-06T00:01\n duration: 1\n cpu/utilization: 80\n')),(0,r.kt)("p",null,"Read our detailed guides to ",(0,r.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"writing manifest files"),"."),(0,r.kt)("h2",{id:"3-compute-your-manifest-file"},"3: Compute your manifest file"),(0,r.kt)("p",null,"Run the pipeline by passing the path to your manifest file to the ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," command line tool:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest \n")),(0,r.kt)("p",null,"The output will be printed to the console."),(0,r.kt)("p",null,"\ud83c\udf89",(0,r.kt)("strong",{parentName:"p"},"Congratulations")," \ud83c\udf89! You have just used the Impact Framework to compute a manifest file! Your challenge now is to use these principles to construct manifest files for real applications. Our docs will help! "),(0,r.kt)("h2",{id:"next-steps"},"Next steps"),(0,r.kt)("p",null,"Now you know how to use the ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," you can start building more complex pipelines of plugins and more complicated manifest files. Your overall aim is to create a manifest file that accurately represents a real software application, and a plugin pipeline that yields an environmental metric that's important to you (e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon"),")."),(0,r.kt)("p",null,"Experiment by adding more plugins to the pipeline and observe how each plugin enriches each element in the ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," array with new values."),(0,r.kt)("p",null,"You can also configure ",(0,r.kt)("inlineCode",{parentName:"p"},"if")," to save your output data to another ",(0,r.kt)("inlineCode",{parentName:"p"},"yaml")," file. To do this, add the ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," flag and the path to the output file where the results are saved."),(0,r.kt)("p",null,"The command is then as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest --output \n")),(0,r.kt)("p",null,"Explore our user documentation for walkthrough guides to common Impact Framework tasks:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/users/how-to-install-if"},"How to install Impact Framework")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/users/how-to-import-plugins"},"How to load plugins")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/users/how-to-write-manifests"},"How to write manifest files"))))}c.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[109],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},p=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),m=u(n),f=r,d=m["".concat(s,".").concat(f)]||m[f]||c[f]||i;return n?a.createElement(d,o(o({ref:t},p),{},{components:n})):a.createElement(d,o({ref:t},p))}));function f(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var a=n(7462),r=(n(7294),n(4137));const i={sidebar_position:1},o="Quick start",l={unversionedId:"users/quick-start",id:"users/quick-start",title:"Quick start",description:"This page will provide the basic instructions for getting up and running with Impact Framework.",source:"@site/docs/users/quick-start.md",sourceDirName:"users",slug:"/users/quick-start",permalink:"/users/quick-start",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/quick-start.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Users",permalink:"/users/"},next:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"}},s={},u=[{value:"1: Install Impact Framework",id:"1-install-impact-framework",level:2},{value:"2: Create a manifest file",id:"2-create-a-manifest-file",level:2},{value:"3: Compute your manifest file",id:"3-compute-your-manifest-file",level:2},{value:"Next steps",id:"next-steps",level:2}],p={toc:u};function c(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"quick-start"},"Quick start"),(0,r.kt)("p",null,"This page will provide the basic instructions for getting up and running with Impact Framework."),(0,r.kt)("h2",{id:"1-install-impact-framework"},"1: Install Impact Framework"),(0,r.kt)("p",null,"Install the Impact Framework globally using npm."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"npm install -g @grnsft/if\n")),(0,r.kt)("p",null,"Read our detailed guide to ",(0,r.kt)("a",{parentName:"p",href:"/users/how-to-install-if"},"installing IF"),"."),(0,r.kt)("h2",{id:"2-create-a-manifest-file"},"2: Create a manifest file"),(0,r.kt)("p",null,"A manifest file contains all the configuration and input data required to measure your application's energy and carbon impacts and should have a ",(0,r.kt)("inlineCode",{parentName:"p"},".yml")," extension."),(0,r.kt)("p",null,"Open the file, add your data and save the file. The minimal example below runs two snapshot observations through a single plugin - all it does is multiply a value in each element of the ",(0,r.kt)("inlineCode",{parentName:"p"},"input")," data by 2."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},'name: basic-demo\ndescription:\ntags:\ninitialize:\n plugins:\n double-a-value: \n path: \'builtin\'\n method: Coefficient\n config:\n input-parameter: "cpu-utilization"\n coefficient: 2\n output-parameter: "cpu-utilization-doubled"\n\ntree:\n children:\n child-0:\n defaults:\n cpu/thermal-design-power: 100\n pipeline:\n observe:\n regroup:\n compute:\n - double-a-value\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 1\n cpu/utilization: 20\n - timestamp: 2023-07-06T00:01\n duration: 1\n cpu/utilization: 80\n')),(0,r.kt)("p",null,"Read our detailed guides to ",(0,r.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"writing manifest files"),"."),(0,r.kt)("h2",{id:"3-compute-your-manifest-file"},"3: Compute your manifest file"),(0,r.kt)("p",null,"Run the pipeline by passing the path to your manifest file to the ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," command line tool:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest \n")),(0,r.kt)("p",null,"The output will be printed to the console."),(0,r.kt)("p",null,"\ud83c\udf89",(0,r.kt)("strong",{parentName:"p"},"Congratulations")," \ud83c\udf89! You have just used the Impact Framework to compute a manifest file! Your challenge now is to use these principles to construct manifest files for real applications. Our docs will help! "),(0,r.kt)("h2",{id:"next-steps"},"Next steps"),(0,r.kt)("p",null,"Now you know how to use the ",(0,r.kt)("inlineCode",{parentName:"p"},"if-run")," you can start building more complex pipelines of plugins and more complicated manifest files. Your overall aim is to create a manifest file that accurately represents a real software application, and a plugin pipeline that yields an environmental metric that's important to you (e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon"),")."),(0,r.kt)("p",null,"Experiment by adding more plugins to the pipeline and observe how each plugin enriches each element in the ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," array with new values."),(0,r.kt)("p",null,"You can also configure ",(0,r.kt)("inlineCode",{parentName:"p"},"if")," to save your output data to another ",(0,r.kt)("inlineCode",{parentName:"p"},"yaml")," file. To do this, add the ",(0,r.kt)("inlineCode",{parentName:"p"},"--output")," flag and the path to the output file where the results are saved."),(0,r.kt)("p",null,"The command is then as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest --output \n")),(0,r.kt)("p",null,"Explore our user documentation for walkthrough guides to common Impact Framework tasks:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/users/how-to-install-if"},"How to install Impact Framework")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/users/how-to-import-plugins"},"How to load plugins")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/users/how-to-write-manifests"},"How to write manifest files"))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/244c9605.8859fe18.js b/assets/js/244c9605.8859fe18.js new file mode 100644 index 00000000..9fbd7a7a --- /dev/null +++ b/assets/js/244c9605.8859fe18.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[426],{4137:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>d});var r=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function a(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=r.createContext({}),u=function(e){var n=r.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=u(e.components);return r.createElement(s.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},c=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),c=u(t),d=i,g=c["".concat(s,".").concat(d)]||c[d]||m[d]||o;return t?r.createElement(g,a(a({ref:n},p),{},{components:t})):r.createElement(g,a({ref:n},p))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,a=new Array(o);a[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var u=2;u{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>a,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var r=t(7462),i=(t(7294),t(4137));const o={"sidebar-position":2},a="How to make plugins production ready",l={unversionedId:"developers/how-to-refine-plugins",id:"developers/how-to-refine-plugins",title:"How to make plugins production ready",description:"Our How to build plugins guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.",source:"@site/docs/developers/how-to-refine-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-refine-plugins",permalink:"/developers/how-to-refine-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-refine-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"How to create an exhaust script",permalink:"/developers/how-to-create-exhaust-script"},next:{title:"How to submit plugins",permalink:"/developers/how-to-submit-plugins"}},s={},u=[{value:"1. Naming conventions",id:"1-naming-conventions",level:2},{value:"2. Plugin code",id:"2-plugin-code",level:2},{value:"Imports",id:"imports",level:3},{value:"Comments",id:"comments",level:3},{value:"Error handling",id:"error-handling",level:3},{value:"Validation",id:"validation",level:3},{value:"Code Modularity",id:"code-modularity",level:3},{value:"3. Unit tests",id:"3-unit-tests",level:2},{value:"4. Linting",id:"4-linting",level:2},{value:"Summary",id:"summary",level:2}],p={toc:u};function m(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,r.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-make-plugins-production-ready"},"How to make plugins production ready"),(0,i.kt)("p",null,"Our ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-build-plugins"},"How to build plugins")," guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers."),(0,i.kt)("h2",{id:"1-naming-conventions"},"1. Naming conventions"),(0,i.kt)("p",null,"We prefer not to use abbreviations of contractions in parameter names. Using fully descriptive names makes the code more readable, which in turn helps reviewers and anyone else aiming to understand how the plugin works. It also helps to avoid ambiguity and naming collisions within and across plugins. Your name should describe what an element does as precisely as practically possible."),(0,i.kt)("p",null,"For example, we prefer ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"e-cpu")," and we prefer ",(0,i.kt)("inlineCode",{parentName:"p"},"functionalUnit")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"funcUnit"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"fUnit"),", or any other abbreviation."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"In Typescript code")," we use lower Camel case (",(0,i.kt)("inlineCode",{parentName:"p"},"likeThis"),") for variable and function names and Pascal/Upper Camel case for class, type, enum, and interface names (",(0,i.kt)("inlineCode",{parentName:"p"},"LikeThis"),")."),(0,i.kt)("p",null,"For example:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sci")," is the name for the SCI value normalized per second."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"energy")," is the name for the array of energy metrics available to be summed in the ",(0,i.kt)("inlineCode",{parentName:"li"},"sci-e")," plugin")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"In yaml files"),", we prefer to use kebab-case (",(0,i.kt)("inlineCode",{parentName:"p"},"like-this"),") for field names. For example:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"network/energy")," is the field name for the energy consumed by networking for an application"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"functional-unit")," is the unit in which to express an SCI value.")),(0,i.kt)("p",null,"Global constants can be given capitalized names, such as ",(0,i.kt)("inlineCode",{parentName:"p"},"TIME_UNITS_IN_SECONDS"),"."),(0,i.kt)("h2",{id:"2-plugin-code"},"2. Plugin code"),(0,i.kt)("h3",{id:"imports"},"Imports"),(0,i.kt)("p",null,"We prefer the following ordering of imports in your plugin code:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Node built-in modules (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import fs from 'fs';"),")"),(0,i.kt)("li",{parentName:"ol"},"External modules (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import {z} from 'zod';"),")"),(0,i.kt)("li",{parentName:"ol"},"Internal modules (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import config from 'src/config';"),")"),(0,i.kt)("li",{parentName:"ol"},"Interfaces (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import {PluginInterface} from '@grnsft/if-core/types';"),")"),(0,i.kt)("li",{parentName:"ol"},"Types (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import {PluginParams} from '@grnsft/if-core/types';"),")")),(0,i.kt)("h3",{id:"comments"},"Comments"),(0,i.kt)("p",null,"Each logical unit in the code should be preceded by an appropriate explanatory comment. Sometimes it is useful to include short comments inside a function that clarifies the purpose of a particular statement. Here's an example from our codebase:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Calculates the energy consumption for a single input.\n */\nconst calculateEnergy = (input: PluginParams) => {\n const {\n 'memory/capacity': totalMemory,\n 'memory/utilization': memoryUtil,\n 'energy-per-gb': energyPerGB,\n } = input;\n\n // GB * kWh/GB == kWh\n return totalMemory * (memoryUtil / 100) * energyPerGB;\n};\n")),(0,i.kt)("h3",{id:"error-handling"},"Error handling"),(0,i.kt)("p",null,"We use custom errors across our codebase to make it as easy as possible to understand the root cause of a problem.\nYou can use our error handlers by importing ",(0,i.kt)("inlineCode",{parentName:"p"},"if-core")," as a dependency of your plugin. This provides you with our error handling code and predefined list of error classes that you can invoke. This gives you tight integration with IF, because the framework can recognize those error classes and automatically incorporate them into the framework's error handling routines."),(0,i.kt)("p",null,"Just import ",(0,i.kt)("inlineCode",{parentName:"p"},"ERRORS")," from ",(0,i.kt)("inlineCode",{parentName:"p"},"if-core")," and use the error classes that are appropriate for your use-case."),(0,i.kt)("p",null,"e.g."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"import {ERRORS} from '@grnsft/if-core/util';\n\nconst {MissingInputDataError} = ERRORS;\n\n...\n\nthrow new MissingInputDataError(\"my-plugin is missing my-parameter from inputs[0]\");\n")),(0,i.kt)("h3",{id:"validation"},"Validation"),(0,i.kt)("p",null,"We recommend using ",(0,i.kt)("inlineCode",{parentName:"p"},"inputValidation")," property from ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," for validation to ensure the integrity of input data. Validate input parameters against expected types, ranges, or constraints to prevent runtime errors and ensure data consistency."),(0,i.kt)("p",null,"You need to use ",(0,i.kt)("inlineCode",{parentName:"p"},"zod")," schema or ",(0,i.kt)("inlineCode",{parentName:"p"},"InputValidatorFunction"),". Here's an example from our codebase:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When using function with ",(0,i.kt)("inlineCode",{parentName:"li"},"InputValidatorFunction")," type.")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// `inputValidation` from plugin definition\ninputValidation: (input: PluginParams, config: ConfigParams) => {\n const inputData = {\n 'input-parameter': input[config['input-parameter']],\n };\n const validationSchema = z.record(z.string(), z.number());\n validate(validationSchema, inputData);\n\n return input;\n};\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When using ",(0,i.kt)("inlineCode",{parentName:"li"},"zod")," schema")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// `inputValidation` from plugin definition\ninputValidation: z.object({\n duration: z.number().gt(0),\n vCPUs: z.number().gt(0).default(1),\n memory: z.number().gt(0).default(16),\n ssd: z.number().gte(0).default(0),\n hdd: z.number().gte(0).default(0),\n gpu: z.number().gte(0).default(0),\n 'usage-ratio': z.number().gt(0).default(1),\n time: z.number().gt(0).optional(),\n});\n")),(0,i.kt)("h3",{id:"code-modularity"},"Code Modularity"),(0,i.kt)("p",null,"Break down complex functionality into smaller, manageable methods with well-defined responsibilities.\nEncapsulate related functionality into private methods to promote code reusability and maintainability."),(0,i.kt)("h2",{id:"3-unit-tests"},"3. Unit tests"),(0,i.kt)("p",null,"Your plugin should have unit tests with 100% coverage. We use ",(0,i.kt)("inlineCode",{parentName:"p"},"jest")," to handle unit testing. We strive to have one ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," per function. Each possible outcome from each function is separated using ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," with a precise and descriptive message."),(0,i.kt)("p",null,"Here's an example that covers plugin initialization and the happy path for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," function."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import { ERRORS } from '@grnsft/if-core/utils';\n\nimport { Sum } from '../../../if-run/builtins/sum';\n\nconst { InputValidationError, WrongArithmeticExpressionError } = ERRORS;\n\ndescribe('builtins/sum: ', () => {\n describe('Sum: ', () => {\n const config = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const parametersMetadata = {};\n const sum = Sum(config, parametersMetadata, {});\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n\n describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n });\n });\n});\n")),(0,i.kt)("p",null,"We have a ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-write-unit-tests"},"dedicated page")," explaining in more detail how to write great unit tests for Impact Framework plugins."),(0,i.kt)("h2",{id:"4-linting"},"4. Linting"),(0,i.kt)("p",null,"We use ESLint to format our code. We use a very simple configuration file (",(0,i.kt)("inlineCode",{parentName:"p"},"eslintrc.json"),"), as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n "extends": "./node_modules/gts/",\n "rules": {\n "@typescript-eslint/no-explicit-any": ["off"]\n }\n}\n')),(0,i.kt)("p",null,"For our repositories we use Github CI to enforce the linting rules for any pull requests."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"On this page, we have outlined best practices for refining your plugins so that they conform to our expected norms. This will help you write clean, efficient, and understandable code!"))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/244c9605.d6c3fa12.js b/assets/js/244c9605.d6c3fa12.js deleted file mode 100644 index 98ee713a..00000000 --- a/assets/js/244c9605.d6c3fa12.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[426],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var r=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function a(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=r.createContext({}),p=function(e){var n=r.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},u=function(e){var n=p(e.components);return r.createElement(s.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},m=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(t),d=i,g=m["".concat(s,".").concat(d)]||m[d]||c[d]||o;return t?r.createElement(g,a(a({ref:n},u),{},{components:t})):r.createElement(g,a({ref:n},u))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,a=new Array(o);a[0]=m;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var r=t(7462),i=(t(7294),t(4137));const o={"sidebar-position":2},a="How to make plugins production ready",l={unversionedId:"developers/how-to-refine-plugins",id:"developers/how-to-refine-plugins",title:"How to make plugins production ready",description:"Our How to build plugins guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.",source:"@site/docs/developers/how-to-refine-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-refine-plugins",permalink:"/developers/how-to-refine-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-refine-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"How to create an exhaust script",permalink:"/developers/how-to-create-exhaust-script"},next:{title:"How to submit plugins",permalink:"/developers/how-to-submit-plugins"}},s={},p=[{value:"1. Naming conventions",id:"1-naming-conventions",level:2},{value:"2. Plugin code",id:"2-plugin-code",level:2},{value:"Imports",id:"imports",level:3},{value:"Comments",id:"comments",level:3},{value:"Error handling",id:"error-handling",level:3},{value:"Validation",id:"validation",level:3},{value:"Code Modularity",id:"code-modularity",level:3},{value:"3. Unit tests",id:"3-unit-tests",level:2},{value:"4. Linting",id:"4-linting",level:2},{value:"Summary",id:"summary",level:2}],u={toc:p};function c(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-make-plugins-production-ready"},"How to make plugins production ready"),(0,i.kt)("p",null,"Our ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-build-plugins"},"How to build plugins")," guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers."),(0,i.kt)("h2",{id:"1-naming-conventions"},"1. Naming conventions"),(0,i.kt)("p",null,"We prefer not to use abbreviations of contractions in parameter names. Using fully descriptive names makes the code more readable, which in turn helps reviewers and anyone else aiming to understand how the plugin works. It also helps to avoid ambiguity and naming collisions within and across plugins. Your name should describe what an element does as precisely as practically possible."),(0,i.kt)("p",null,"For example, we prefer ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"e-cpu")," and we prefer ",(0,i.kt)("inlineCode",{parentName:"p"},"functionalUnit")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"funcUnit"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"fUnit"),", or any other abbreviation."),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"In Typescript code")," we use lower Camel case (",(0,i.kt)("inlineCode",{parentName:"p"},"likeThis"),") for variable and function names and Pascal/Upper Camel case for class, type, enum, and interface names (",(0,i.kt)("inlineCode",{parentName:"p"},"LikeThis"),")."),(0,i.kt)("p",null,"For example:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sci")," is the name for the SCI value normalized per second."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"energy")," is the name for the array of energy metrics available to be summed in the ",(0,i.kt)("inlineCode",{parentName:"li"},"sci-e")," plugin")),(0,i.kt)("p",null,(0,i.kt)("strong",{parentName:"p"},"In yaml files"),", we prefer to use kebab-case (",(0,i.kt)("inlineCode",{parentName:"p"},"like-this"),") for field names. For example:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"network/energy")," is the field name for the energy consumed by networking for an application"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"functional-unit")," is the unit in which to express an SCI value.")),(0,i.kt)("p",null,"Global constants can be given capitalized names, such as ",(0,i.kt)("inlineCode",{parentName:"p"},"TIME_UNITS_IN_SECONDS"),"."),(0,i.kt)("h2",{id:"2-plugin-code"},"2. Plugin code"),(0,i.kt)("h3",{id:"imports"},"Imports"),(0,i.kt)("p",null,"We prefer the following ordering of imports in your plugin code:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Node built-in modules (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import fs from 'fs';"),")"),(0,i.kt)("li",{parentName:"ol"},"External modules (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import {z} from 'zod';"),")"),(0,i.kt)("li",{parentName:"ol"},"Internal modules (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import config from 'src/config';"),")"),(0,i.kt)("li",{parentName:"ol"},"Interfaces (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import type {PluginInterface} from 'interfaces'"),")"),(0,i.kt)("li",{parentName:"ol"},"Types (e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"import {PluginParams} from '../../types/common'"),";)")),(0,i.kt)("h3",{id:"comments"},"Comments"),(0,i.kt)("p",null,"Each logical unit in the code should be preceded by an appropriate explanatory comment. Sometimes it is useful to include short comments inside a function that clarifies the purpose of a particular statement. Here's an example from our codebase:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"}," /**\n * Calculates the energy consumption for a single input.\n */\n const calculateEnergy = (input: PluginParams) => {\n const {\n 'memory/capacity': totalMemory,\n 'memory/utilization': memoryUtil,\n 'energy-per-gb': energyPerGB,\n } = input;\n\n // GB * kWh/GB == kWh\n return totalMemory * (memoryUtil / 100) * energyPerGB;\n };\n")),(0,i.kt)("h3",{id:"error-handling"},"Error handling"),(0,i.kt)("p",null,"We use custom errors across our codebase to make it as easy as possible to understand the root cause of a problem.\nYou can use our error handlers by importing ",(0,i.kt)("inlineCode",{parentName:"p"},"if-core")," as a dependency of your plugin. This provides you with our error handling code and predefined list of error classes that you can invoke. This gives you tight integration with IF, because the framework can recognize those error classes and automatically incorporate them into the framework's error handling routines."),(0,i.kt)("p",null,"Just import ",(0,i.kt)("inlineCode",{parentName:"p"},"ERRORS")," from ",(0,i.kt)("inlineCode",{parentName:"p"},"if-core")," and use the error classes that are appropriate for your use-case."),(0,i.kt)("p",null,"e.g."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"import {ERRORS} from '@grnsft/if-core/util';\n\nconst {MissingInputDataError} = ERRORS;\n\n... \n\nthrow new MissingInputDataError(\"my-plugin is missing my-parameter from inputs[0]\");\n")),(0,i.kt)("h3",{id:"validation"},"Validation"),(0,i.kt)("p",null,"We recommend using validation techniques to ensure the integrity of input data. Validate input parameters against expected types, ranges, or constraints to prevent runtime errors and ensure data consistency."),(0,i.kt)("p",null,"We use ",(0,i.kt)("inlineCode",{parentName:"p"},"zod")," to validate data. Here's an example from our codebase:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Checks for required fields in input.\n */\nconst validateInput = (input: PluginParams) => {\n const schema = z\n .object({\n 'cpu/name': z.string(),\n })\n .refine(allDefined, {\n message: '`cpu/name` should be present.',\n });\n\n return validate>(schema, input);\n}\n")),(0,i.kt)("h3",{id:"code-modularity"},"Code Modularity"),(0,i.kt)("p",null,"Break down complex functionality into smaller, manageable methods with well-defined responsibilities.\nEncapsulate related functionality into private methods to promote code reusability and maintainability."),(0,i.kt)("h2",{id:"3-unit-tests"},"3. Unit tests"),(0,i.kt)("p",null,"Your plugin should have unit tests with 100% coverage. We use ",(0,i.kt)("inlineCode",{parentName:"p"},"jest")," to handle unit testing. We strive to have one ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," per function. Each possible outcome from each function is separated using ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," with a precise and descriptive message."),(0,i.kt)("p",null,"Here's an example that covers plugin initialization and the happy path for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," function."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import {Sum} from '../../../../lib';\n\nimport {ERRORS} from '@grnsft/if-core/util/';\n\nconst {InputValidationError} = ERRORS;\n\ndescribe('lib/sum: ', () => {\n describe('Sum: ', () => {\n const globalConfig = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(globalConfig);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n\n describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n }\n })\n})\n")),(0,i.kt)("p",null,"We have a ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-write-unit-tests"},"dedicated page")," explaining in more detail how to write great unit tests for Impact Framework plugins."),(0,i.kt)("h2",{id:"4-linting"},"4. Linting"),(0,i.kt)("p",null,"We use ESLint to format our code. We use a very simple configuration file (",(0,i.kt)("inlineCode",{parentName:"p"},"eslintrc.json"),"), as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n "extends": "./node_modules/gts/",\n "rules": {\n "@typescript-eslint/no-explicit-any": ["off"]\n }\n}\n')),(0,i.kt)("p",null,"For our repositories we use Github CI to enforce the linting rules for any pull requests."),(0,i.kt)("h2",{id:"summary"},"Summary"),(0,i.kt)("p",null,"On this page, we have outlined best practices for refining your plugins so that they conform to our expected norms. This will help you write clean, efficient, and understandable code!"))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/28f080da.36b2c0f7.js b/assets/js/28f080da.36b2c0f7.js new file mode 100644 index 00000000..7872c021 --- /dev/null +++ b/assets/js/28f080da.36b2c0f7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[806],{4137:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>c});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),u=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},s=function(e){var n=u(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),d=u(t),c=i,g=d["".concat(p,".").concat(c)]||d[c]||m[c]||o;return t?a.createElement(g,r(r({ref:n},s),{},{components:t})):a.createElement(g,r({ref:n},s))}));function c(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,r=new Array(o);r[0]=d;var l={};for(var p in n)hasOwnProperty.call(n,p)&&(l[p]=n[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,r[1]=l;for(var u=2;u{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var a=t(7462),i=(t(7294),t(4137));const o={"sidebar-position":1},r="How to build plugins",l={unversionedId:"developers/how-to-build-plugins",id:"developers/how-to-build-plugins",title:"How to build plugins",description:"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.",source:"@site/docs/developers/how-to-build-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-build-plugins",permalink:"/developers/how-to-build-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-build-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":1},sidebar:"tutorialSidebar",previous:{title:"Developers",permalink:"/developers/"},next:{title:"How to create an exhaust script",permalink:"/developers/how-to-create-exhaust-script"}},p={},u=[{value:"Step 1: Use our template repository",id:"step-1-use-our-template-repository",level:2},{value:"Step 2: Writing your plugin code",id:"step-2-writing-your-plugin-code",level:2},{value:"Plugin interface",id:"plugin-interface",level:3},{value:"Config",id:"config",level:3},{value:"Parameter metadata",id:"parameter-metadata",level:3},{value:"Mapping",id:"mapping",level:3},{value:"Plugin example",id:"plugin-example",level:3},{value:"PluginFactoryParams",id:"pluginfactoryparams",level:3},{value:"Step 3: Install your plugin",id:"step-3-install-your-plugin",level:2},{value:"Step 4: Load your plugin into IF",id:"step-4-load-your-plugin-into-if",level:2},{value:"Step 5: Publishing your plugin",id:"step-5-publishing-your-plugin",level:2},{value:"Summary of steps",id:"summary-of-steps",level:2},{value:"Next steps",id:"next-steps",level:2},{value:"Appendix: Walk-through of the Sci plugin",id:"appendix-walk-through-of-the-sci-plugin",level:2},{value:"Managing errors",id:"managing-errors",level:2}],s={toc:u};function m(e){let{components:n,...o}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,o,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-build-plugins"},"How to build plugins"),(0,i.kt)("p",null,"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.\nTo help developers write Typescript plugins to integrate easily into IF, we provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," interface. Here's an overview of the stages you need to follow to integrate your plugin:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"create a Typescript file that implements the ",(0,i.kt)("inlineCode",{parentName:"li"},"PluginFactory")," from ",(0,i.kt)("a",{parentName:"li",href:"https://github.com/Green-Software-Foundation/if-core"},(0,i.kt)("inlineCode",{parentName:"a"},"if-core"))),(0,i.kt)("li",{parentName:"ul"},"install the plugin"),(0,i.kt)("li",{parentName:"ul"},"initialize and invoke the plugin in your manifest file")),(0,i.kt)("h2",{id:"step-1-use-our-template-repository"},"Step 1: Use our template repository"),(0,i.kt)("p",null,"Instead of building up your plugin repository and all the configuration from scratch, you can use our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugin-template"},"plugin template repository"),". To use the template, visit the Github repository and click the ",(0,i.kt)("inlineCode",{parentName:"p"},"Use this template")," button. You will have the option to ",(0,i.kt)("inlineCode",{parentName:"p"},"create a new repository")," under your own account. Then, you can clone that repository to your local machine."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"use our template repository",src:t(8312).Z,width:"3024",height:"1184"})),(0,i.kt)("p",null,"Inside that repository, all you have to do is run ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install typescript")," in the template folder, rename the project in ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," and write your plugin code inside ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". All the configuration and setup is taken care of for you."),(0,i.kt)("h2",{id:"step-2-writing-your-plugin-code"},"Step 2: Writing your plugin code"),(0,i.kt)("p",null,"Now your project is setup, you can focus on your plugin logic. The entry point for your plugin is ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". In this guide it is assumed that all your plugin logic is in ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," but depending on the copmplexity of your plugin you might want to split the code across multiple files. ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," should always be your entry point, though."),(0,i.kt)("p",null,"The following sections describe the rules your plugin code should conform to. We also have an ",(0,i.kt)("a",{parentName:"p",href:"#appendix-walk-through-of-the-sum-plugin"},"appendix")," that deep dives a real plugin."),(0,i.kt)("h3",{id:"plugin-interface"},"Plugin interface"),(0,i.kt)("p",null,"Your plugin must implement the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," interface, which is a higher-order function that takes a ",(0,i.kt)("inlineCode",{parentName:"p"},"params")," object of type ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactoryParams"),'. This factory function returns another function (referred to as the "inner function") that manages the plugin\u2019s ',(0,i.kt)("inlineCode",{parentName:"p"},"config"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"parametersMetadata"),", and ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping"),"."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," is structured as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export const PluginFactory =\n (params: PluginFactoryParams) =>\n (\n config: C = {} as C,\n parametersMetadata: PluginParametersMetadata,\n mapping: MappingParams\n ) => ({\n metadata: {\n inputs: {...params.metadata.inputs, ...parametersMetadata?.inputs},\n outputs: parametersMetadata?.outputs || params.metadata.outputs,\n },\n execute: async (inputs: PluginParams[]) => {\n // Generic plugin functionality goes here\n // E.g., mapping, arithmetic operations, validation\n // Process inputs and mapping logic\n });\n });\n")),(0,i.kt)("p",null,"The inner function returned by the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," handles the following parameters:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"strong"},"config")),": An object of type ",(0,i.kt)("inlineCode",{parentName:"li"},"ConfigParams"),". This parameter holds the configuration settings for the plugin and defaults to an empty object (",(0,i.kt)("inlineCode",{parentName:"li"},"{}"),")."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"strong"},"parametersMetadata")),": An object of type ",(0,i.kt)("inlineCode",{parentName:"li"},"PluginParametersMetadata")," that contains metadata describing the plugin\u2019s parameters."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"strong"},"mapping")),": A ",(0,i.kt)("inlineCode",{parentName:"li"},"MappingParams")," object that outlines how plugin parameters are mapped.")),(0,i.kt)("h3",{id:"config"},"Config"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"config")," object is passed as an argument to your plugin and can be handled as shown in the example above. The structure of the config depends on what is defined in the manifest file. For example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sci")," plugin has access to ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," fields in its global configuration, as defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"Initialize")," block of the manifest file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sum:\n method: Sci\n path: 'builtin'\n config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\n")),(0,i.kt)("h3",{id:"parameter-metadata"},"Parameter metadata"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"parameter-metadata")," is passed as an argument to the plugin as the config. It contains information about the ",(0,i.kt)("inlineCode",{parentName:"p"},"description"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"unit")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"aggregation-method")," of the parameters of the inputs and outputs that defined in the manifest."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sum:\n method: Sci\n path: 'builtin'\n config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy-sum'\n parameter-metadata:\n inputs:\n cpu/energy:\n description: energy consumed by the cpu\n unit: kWh\n aggregation-method:\n time: sum\n component: sum\n network/energy:\n description: energy consumed by data ingress and egress\n unit: kWh\n aggregation-method:\n time: sum\n component: sum\n outputs:\n energy-sum:\n description: sum of energy components\n unit: kWh\n aggregation-method:\n time: sum\n component: sum\n")),(0,i.kt)("h3",{id:"mapping"},"Mapping"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," is an optional argument passed to the plugin. Its purpose is to rename the arguments expected or returned from the plugin as part of the plugin's execution, avoiding the need to use additional plugins to rename parameters."),(0,i.kt)("p",null,"For example, your plugin might expect ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and your input data has the parameter ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy")," returned from another plugin. Instead of using an additional plugin to rename the parameter and add a new one, you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," to:"),(0,i.kt)("p",null,"a) rename the output from the first plugin so that ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," is returned instead of the default ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy")),(0,i.kt)("p",null,"b) instruct the second plugin to accept ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy")," instead of the default ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," config is an object with key-value pairs, where the ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," is the 'original' parameter name that the plugin uses, and the ",(0,i.kt)("inlineCode",{parentName:"p"},"value")," is the 'new' name that you want to use instead.\nThe ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," block is an optional and allows mapping the input and output parameters of the plugin. The structure of the ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," block is:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sci\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sci:\n kind: plugin\n method: Sci\n path: 'builtin'\n config:\n functional-unit: requests\n mapping:\n sci: if-sci\ntree:\n children:\n child:\n pipeline:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n")),(0,i.kt)("p",null,"In the ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs"),", the ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," value returned by the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sci")," plugin will be named ",(0,i.kt)("inlineCode",{parentName:"p"},"if-sci"),"."),(0,i.kt)("h3",{id:"plugin-example"},"Plugin example"),(0,i.kt)("p",null,"Here\u2019s a minimal example of a plugin that sums inputs based on the configuration:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export const Plugin = PluginFactory({\n metadata: {\n inputs: {\n // Define your input parameters here\n },\n outputs: {\n // Define your output parameters here\n },\n },\n configValidation: (config: ConfigParams) => {\n // Implement validation logic for config here\n },\n inputValidation: (input: PluginParams, config: ConfigParams) => {\n // Implement validation logic for inputs here\n },\n implementation: async (inputs: PluginParams[], config: ConfigParams) => {\n // Implement plugin logic here\n // e.g., summing input parameters\n },\n allowArithmeticExpressions: [],\n});\n\nconst plugin = Plugin(config, parametersMetadata, mapping);\nconst result = await plugin.execute(inputs);\n")),(0,i.kt)("h3",{id:"pluginfactoryparams"},"PluginFactoryParams"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," interface requires the mandatory parameters defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactoryParams")," interface:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export interface PluginFactoryParams {\n implementation: (\n inputs: PluginParams[],\n config: C,\n mapping?: MappingParams\n ) => Promise;\n metadata?: PluginParametersMetadata;\n configValidation?: z.ZodSchema | ConfigValidatorFunction;\n inputValidation?: z.ZodSchema | InputValidatorFunction;\n allowArithmeticExpressions?: string[];\n}\n")),(0,i.kt)("p",null,"Additional Notes"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Implement"),": You should implement ",(0,i.kt)("inlineCode",{parentName:"li"},"implementation")," function. It should contains the primary logic to generate outputs."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Validation"),": You should define appropriate ",(0,i.kt)("inlineCode",{parentName:"li"},"zod")," schemas or validation functions for both config and inputs. This ensures that invalid data is caught early and handled appropriately."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Arithmetic Expressions"),": By including configuration, input, and output parameters of the plugin in the ",(0,i.kt)("inlineCode",{parentName:"li"},"allowArithmeticExpressions")," array, you enable dynamic evaluation of mathematical expressions within parameter values. This eliminates the need for manual pre-calculation and allows basic mathematical operations to be embedded directly within parameter values in manifest files. More details ",(0,i.kt)("a",{parentName:"li",href:"/reference/features"},"here.")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Mapping"),": Ensure your plugin correctly handles the mapping of parameters. This is essential when working with dynamic input and output configurations.")),(0,i.kt)("h2",{id:"step-3-install-your-plugin"},"Step 3: Install your plugin"),(0,i.kt)("p",null,"Now your plugin code is written, you can install it to make it available to IF."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm run build\n")),(0,i.kt)("p",null,"Then use ",(0,i.kt)("inlineCode",{parentName:"p"},"npm link")," to create a package that can be installed into IF:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link\n")),(0,i.kt)("h2",{id:"step-4-load-your-plugin-into-if"},"Step 4: Load your plugin into IF"),(0,i.kt)("p",null,"Now your plugin is ready to run in IF. First install your plugin by navigating to the ",(0,i.kt)("inlineCode",{parentName:"p"},"if")," project folder and running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin\n")),(0,i.kt)("p",null,"replacing ",(0,i.kt)("inlineCode",{parentName:"p"},"new-plugin")," with your plugin name as defined in the plugin's ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json"),". If you are not sure, the name can be checked by running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm ls -g --depth=0 --link=true"),"."),(0,i.kt)("p",null,"Your plugin is now ready to be run in IF. All that remains is to add your plugin to your manifest file. This means adding it to the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize block")," and adding it to the component pipelines where you want your plugin to be executed. For example, an ",(0,i.kt)("inlineCode",{parentName:"p"},"initilize")," block might look as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n new-plugin:\n method: YourFunctionName\n path: 'new-plugin'\n config:\n something: true\n")),(0,i.kt)("p",null,"Run your manifest uisng"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm run if-run -- --manifest \n")),(0,i.kt)("p",null,"If you have to link more than one local plugin, for example to test your plugin in a pipeline, you can do so with"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin --save\n")),(0,i.kt)("p",null,"This will create an entry like ",(0,i.kt)("inlineCode",{parentName:"p"},'"new-plugin": "file:path/to/your/plugin"')," in the ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," which links to your local plugin. This way, multiple plugins can be linked at once. Of course, these changes should not be committed, but they can be helpful for local testing."),(0,i.kt)("h2",{id:"step-5-publishing-your-plugin"},"Step 5: Publishing your plugin"),(0,i.kt)("p",null,"Now you have run your plugin locally and you are happy with how it works, you can make it public by publishing it to a public Github repository. Now all you have to do to use it in a manifest file is ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install")," it and pass the path to the Github repository in the plugin ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block."),(0,i.kt)("p",null,"For example, for a plugin saved in ",(0,i.kt)("inlineCode",{parentName:"p"},"github.com/my-repo/new-plugin")," you can do the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"npm install https://github.com/my-repo/new-plugin\n")),(0,i.kt)("p",null,"Then, in your manifest file, provide the path in the plugin instantiation. You also need to specify which function the plugin instantiates. Let's say you are using the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sci")," plugin from the example above:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n new-plugin:\n method: FunctionName\n path: https://github.com/my-repo/new-plugin\ntree:\n children:\n child:\n inputs:\n")),(0,i.kt)("p",null,"Now, when you run the manifest file, it will load the plugin automatically."),(0,i.kt)("p",null,"You can run this using the globally installed IF as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest \n")),(0,i.kt)("h2",{id:"summary-of-steps"},"Summary of steps"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Copy our template repository and update ",(0,i.kt)("inlineCode",{parentName:"li"},"package.json")),(0,i.kt)("li",{parentName:"ul"},"Add your plugin code to ",(0,i.kt)("inlineCode",{parentName:"li"},"index.ts")),(0,i.kt)("li",{parentName:"ul"},"Build and link the plugin using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm run build && npm link")),(0,i.kt)("li",{parentName:"ul"},"Load your plugin into ",(0,i.kt)("inlineCode",{parentName:"li"},"if")," using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm link")),(0,i.kt)("li",{parentName:"ul"},"Initialize your plugin and add it to a pipeline in your manifest file."),(0,i.kt)("li",{parentName:"ul"},"Publish your plugin to Github")),(0,i.kt)("p",null,"You should also create unit tests for your plugin to demonstrate correct execution and handling of corner cases."),(0,i.kt)("h2",{id:"next-steps"},"Next steps"),(0,i.kt)("p",null,"You can read our more advanced guide on ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-refine-plugins"},"how to refine your plugins"),"."),(0,i.kt)("h2",{id:"appendix-walk-through-of-the-sci-plugin"},"Appendix: Walk-through of the Sci plugin"),(0,i.kt)("p",null,"To demonstrate how to build a plugin that conforms to the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory"),", let's examine the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin implements the following logic:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"sum whatever is provided in the ",(0,i.kt)("inlineCode",{parentName:"li"},"input-parameters")," field from ",(0,i.kt)("inlineCode",{parentName:"li"},"config"),"."),(0,i.kt)("li",{parentName:"ul"},"append the result to each element in the output array with the name provided as ",(0,i.kt)("inlineCode",{parentName:"li"},"output-parameter")," in ",(0,i.kt)("inlineCode",{parentName:"li"},"config"),".")),(0,i.kt)("p",null,"Let's look at how you would implement this from scratch:"),(0,i.kt)("p",null,"The plugin must be a function conforming to ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export const Sum = PluginFactory({\n configValidation: z.object({\n 'input-parameters': z.array(z.string()),\n 'output-parameter': z.string().min(1),\n }),\n inputValidation: (input: PluginParams, config: ConfigParams) => {\n return validate(validationSchema, inputData);\n },\n implementation: async (inputs: PluginParams[], config: ConfigParams) => {},\n allowArithmeticExpressions: [],\n});\n")),(0,i.kt)("p",null,"Your plugin now has the basic structure required for IF integration. Your next task is to add code to the body of ",(0,i.kt)("inlineCode",{parentName:"p"},"implementation")," to enable the actual plugin logic to be implemented."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"implementation")," function should grab the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," (the values to sum) from ",(0,i.kt)("inlineCode",{parentName:"p"},"config"),". It should then iterate over the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, get the values for each of the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and append them to the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, using the name from the ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," value in ",(0,i.kt)("inlineCode",{parentName:"p"},"config"),". Here's what this can look like, with the actual calculation pushed to a separate function, ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"{\n implementation: async (inputs: PluginParams[], config: ConfigParams) => {\n const {\n 'input-parameters': inputParameters,\n 'output-parameter': outputParameter,\n } = config;\n\n return inputs.map((input) => {\n const calculatedResult = calculateSum(input, inputParameters);\n\n return {\n ...input,\n [outputParameter]: calculatedResult,\n };\n });\n };\n}\n")),(0,i.kt)("p",null,"Now we just need to define what happens in ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum")," - this can be a simple ",(0,i.kt)("inlineCode",{parentName:"p"},"reduce"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Calculates the sum of the energy components.\n */\nconst calculateSum = (input: PluginParams, inputParameters: string[]) =>\n inputParameters.reduce(\n (accumulator, metricToSum) => accumulator + input[metricToSum],\n 0\n );\n")),(0,i.kt)("p",null,"Note that this example did not include any validation or error handling - you will likely want to add some for a real plugin."),(0,i.kt)("h2",{id:"managing-errors"},"Managing errors"),(0,i.kt)("p",null,"The IF framework provides its own set of error classes, making your task as a plugin builder much simpler! These are available to you in the ",(0,i.kt)("inlineCode",{parentName:"p"},"if-core")," package that comes bundled with IF. You can import the appropriate error classes and add custom messages.\nThe ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-core"},"If Core")," repository contains the ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginFactory")," interface, utility functions, and a set of error classes that can be fully integrated with the IF framework. Detailed information on each error class can be found in the ",(0,i.kt)("a",{parentName:"p",href:"/reference/errors"},"Errors Reference"),"."),(0,i.kt)("p",null,"Now you are ready to run your plugin using the ",(0,i.kt)("inlineCode",{parentName:"p"},"if-run")," CLI tool!"))}m.isMDXComponent=!0},8312:(e,n,t)=>{t.d(n,{Z:()=>a});const a=t.p+"assets/images/template-repo-6cc54a91c4b00717cf92334e56c8ec1b.png"}}]); \ No newline at end of file diff --git a/assets/js/28f080da.e3b41090.js b/assets/js/28f080da.e3b41090.js deleted file mode 100644 index 68080747..00000000 --- a/assets/js/28f080da.e3b41090.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[806],{4137:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>g});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function l(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),u=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},s=function(e){var n=u(e.components);return a.createElement(p.Provider,{value:n},e.children)},m={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,l=e.originalType,p=e.parentName,s=o(e,["components","mdxType","originalType","parentName"]),d=u(t),g=i,c=d["".concat(p,".").concat(g)]||d[g]||m[g]||l;return t?a.createElement(c,r(r({ref:n},s),{},{components:t})):a.createElement(c,r({ref:n},s))}));function g(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var l=t.length,r=new Array(l);r[0]=d;var o={};for(var p in n)hasOwnProperty.call(n,p)&&(o[p]=n[p]);o.originalType=e,o.mdxType="string"==typeof e?e:i,r[1]=o;for(var u=2;u{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>m,frontMatter:()=>l,metadata:()=>o,toc:()=>u});var a=t(7462),i=(t(7294),t(4137));const l={"sidebar-position":1},r="How to build plugins",o={unversionedId:"developers/how-to-build-plugins",id:"developers/how-to-build-plugins",title:"How to build plugins",description:"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.",source:"@site/docs/developers/how-to-build-plugins.md",sourceDirName:"developers",slug:"/developers/how-to-build-plugins",permalink:"/developers/how-to-build-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-build-plugins.md",tags:[],version:"current",frontMatter:{"sidebar-position":1},sidebar:"tutorialSidebar",previous:{title:"Developers",permalink:"/developers/"},next:{title:"How to create an exhaust script",permalink:"/developers/how-to-create-exhaust-script"}},p={},u=[{value:"Step 1: Use our template repository",id:"step-1-use-our-template-repository",level:2},{value:"Step 2: Writing your plugin code",id:"step-2-writing-your-plugin-code",level:2},{value:"The plugin interface",id:"the-plugin-interface",level:3},{value:"Global config",id:"global-config",level:3},{value:"Parameter metadata",id:"parameter-metadata",level:3},{value:"Mapping",id:"mapping",level:3},{value:"Methods",id:"methods",level:3},{value:"execute",id:"execute",level:4},{value:"Params",id:"params",level:4},{value:"Returns",id:"returns",level:4},{value:"What are PluginParams?",id:"what-are-pluginparams",level:3},{value:"What are PluginParams?",id:"what-are-pluginparams-1",level:2},{value:"Step 3: Install your plugin",id:"step-3-install-your-plugin",level:2},{value:"Step 4: Load your plugin into IF",id:"step-4-load-your-plugin-into-if",level:2},{value:"Step 5: Publishing your plugin",id:"step-5-publishing-your-plugin",level:2},{value:"Summary of steps",id:"summary-of-steps",level:2},{value:"Next steps",id:"next-steps",level:2},{value:"Appendix: Walk-through of the Sum plugin",id:"appendix-walk-through-of-the-sum-plugin",level:2},{value:"Managing errors",id:"managing-errors",level:2}],s={toc:u};function m(e){let{components:n,...l}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,l,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-build-plugins"},"How to build plugins"),(0,i.kt)("p",null,"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.\nTo help developers write Typescript plugins to integrate easily into IF, we provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"ExecutePlugin")," interface. Here's an overview of the stages you need to follow to integrate your plugin:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"create a Typescript file that implements the ",(0,i.kt)("inlineCode",{parentName:"li"},"ExecutePlugin")),(0,i.kt)("li",{parentName:"ul"},"install the plugin"),(0,i.kt)("li",{parentName:"ul"},"initialize and invoke the plugin in your manifest file")),(0,i.kt)("h2",{id:"step-1-use-our-template-repository"},"Step 1: Use our template repository"),(0,i.kt)("p",null,"Instead of building up your plugin repository and all the configuration from scratch, you can use our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugin-template"},"plugin template repository"),". To use the template, visit the Github repository and click the ",(0,i.kt)("inlineCode",{parentName:"p"},"Use this template")," button. You will have the option to ",(0,i.kt)("inlineCode",{parentName:"p"},"create a new repository")," under your own account. Then, you can clone that repository to your local machine."),(0,i.kt)("p",null,(0,i.kt)("img",{alt:"use our template repository",src:t(8312).Z,width:"3024",height:"1184"})),(0,i.kt)("p",null,"Inside that repository, all you have to do is run ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install typescript")," in the template folder, rename the project in ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," and write your plugin code inside ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". All the configuration and setup is taken care of for you."),(0,i.kt)("h2",{id:"step-2-writing-your-plugin-code"},"Step 2: Writing your plugin code"),(0,i.kt)("p",null,"Now your project is setup, you can focus on your plugin logic. The entry point for your plugin is ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts"),". In this guide it is assumed that all your plugin logic is in ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," but depending on the copmplexity of your plugin you might want to split the code across multiple files. ",(0,i.kt)("inlineCode",{parentName:"p"},"index.ts")," should always be your entry point, though."),(0,i.kt)("p",null,"The following sections describe the rules your plugin code should conform to. We also have an ",(0,i.kt)("a",{parentName:"p",href:"#appendix-walk-through-of-the-sum-plugin"},"appendix")," that deep dives a real plugin."),(0,i.kt)("h3",{id:"the-plugin-interface"},"The plugin interface"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"ExecutePlugin")," is structured as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export type ExecutePlugin = {\n execute: (\n inputs: PluginParams[],\n config?: Record\n ) => PluginParams[];\n metadata: {\n kind: string;\n inputs?: ParameterMetadata;\n outputs?: ParameterMetadata;\n };\n [key: string]: any;\n};\n")),(0,i.kt)("p",null,"The interface requires an execute function where your plugin logic is implemented. It should also return metadata. This can include any relevant metadata you want to include, with a minimum requirement being ",(0,i.kt)("inlineCode",{parentName:"p"},"kind: execute"),"."),(0,i.kt)("h3",{id:"global-config"},"Global config"),(0,i.kt)("p",null,"Global config is passed as an argument to the plugin. In your plugin code you can handle it as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"// Here's the function definition - notice that global config is passed in here!\nexport const Plugin = (\n globalConfig: YourConfig,\n parametersMetadata: PluginParametersMetadata,\n mapping: MappingParams\n): ExecutePlugin => {\n // in here you have access to globalConfig[your-params]\n};\n")),(0,i.kt)("p",null,"The parameters available to you in ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig")," depends upon the parameters you pass in the manifest file. For example, the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin has access to ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," in its global config, and it is defined in the ",(0,i.kt)("inlineCode",{parentName:"p"},"Initialize")," block in the manifest file as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\n")),(0,i.kt)("h3",{id:"parameter-metadata"},"Parameter metadata"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"parameter-metadata")," is passed as an argument to the plugin as the global config. It contains information about the ",(0,i.kt)("inlineCode",{parentName:"p"},"description"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"unit")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"aggregation-method")," of the parameters of the inputs and outputs that defined in the manifest."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy-sum'\n parameter-metadata:\n inputs:\n cpu/energy:\n description: energy consumed by the cpu\n unit: kWh\n aggregation-method: sum\n network/energy:\n description: energy consumed by data ingress and egress\n unit: kWh\n aggregation-method: sum\n outputs:\n energy-sum:\n description: sum of energy components\n unit: kWh\n aggregation-method: sum\n")),(0,i.kt)("h3",{id:"mapping"},"Mapping"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," is an optional argument passed to the plugin. Its purpose is to rename the arguments expected or returned from the plugin as part of the plugin's execution, avoiding the need to use additional plugins to rename parameters. "),(0,i.kt)("p",null,"For example, your plugin might expect ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and your input data has the parameter ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy")," returned from another plugin. Instead of using an additional plugin to rename the parameter and add a new one, you can use ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," to:"),(0,i.kt)("p",null,"a) rename the output from the first plugin so that ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," is returned instead of the default ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy")),(0,i.kt)("p",null,"b) instruct the second plugin to accept ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy")," instead of the default ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," config is an object with key-value pairs, where the ",(0,i.kt)("inlineCode",{parentName:"p"},"key")," is the 'original' parameter name that the plugin uses, and the ",(0,i.kt)("inlineCode",{parentName:"p"},"value")," is the 'new' name that you want to use instead.\nThe ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," block is an optional and allows mapping the input and output parameters of the plugin. The structure of the ",(0,i.kt)("inlineCode",{parentName:"p"},"mapping")," block is:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sci\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sci:\n kind: plugin\n method: Sci\n path: 'builtin'\n config:\n functional-unit: requests\n mapping:\n sci: if-sci\ntree:\n children:\n child:\n pipeline:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n")),(0,i.kt)("p",null,"In the ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs"),", the ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," value returned by the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sci")," plugin will be named ",(0,i.kt)("inlineCode",{parentName:"p"},"if-sci"),"."),(0,i.kt)("h3",{id:"methods"},"Methods"),(0,i.kt)("h4",{id:"execute"},"execute"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," is where the main calculation logic of the plugin is implemented. It always takes ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," (an array of ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams"),") as an argument and returns an updated set of ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,i.kt)("h4",{id:"params"},"Params"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Param"),(0,i.kt)("th",{parentName:"tr",align:null},"Type"),(0,i.kt)("th",{parentName:"tr",align:null},"Purpose"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"inputs")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"PluginParams[]")),(0,i.kt)("td",{parentName:"tr",align:null},"Array of data provided in the ",(0,i.kt)("inlineCode",{parentName:"td"},"inputs")," field of a component in a manifest file")))),(0,i.kt)("h4",{id:"returns"},"Returns"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"Return value"),(0,i.kt)("th",{parentName:"tr",align:null},"Type"),(0,i.kt)("th",{parentName:"tr",align:null},"Purpose"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"outputs")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"Promise")),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"Promise")," resolving to an array of updated ",(0,i.kt)("inlineCode",{parentName:"td"},"PluginParams[]"))))),(0,i.kt)("h3",{id:"what-are-pluginparams"},"What are ",(0,i.kt)("inlineCode",{parentName:"h3"},"PluginParams"),"?"),(0,i.kt)("h2",{id:"what-are-pluginparams-1"},"What are ",(0,i.kt)("inlineCode",{parentName:"h2"},"PluginParams"),"?"),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams")," are a fundamental data type in the Impact Framework. The type is defined as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export type PluginParams = {\n [key: string]: any;\n};\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"PluginParams")," type therefore defines an array of key-value pairs."),(0,i.kt)("h2",{id:"step-3-install-your-plugin"},"Step 3: Install your plugin"),(0,i.kt)("p",null,"Now your plugin code is written, you can install it to make it available to IF."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm run build\n")),(0,i.kt)("p",null,"Then use ",(0,i.kt)("inlineCode",{parentName:"p"},"npm link")," to create a package that can be installed into IF:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link\n")),(0,i.kt)("h2",{id:"step-4-load-your-plugin-into-if"},"Step 4: Load your plugin into IF"),(0,i.kt)("p",null,"Now your plugin is ready to run in IF. First install your plugin by navigating to the ",(0,i.kt)("inlineCode",{parentName:"p"},"if")," project folder and running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin\n")),(0,i.kt)("p",null,"replacing ",(0,i.kt)("inlineCode",{parentName:"p"},"new-plugin")," with your plugin name as defined in the plugin's ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json"),". If you are not sure, the name can be checked by running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm ls -g --depth=0 --link=true"),"."),(0,i.kt)("p",null,"Your plugin is now ready to be run in IF. All that remains is to add your plugin to your manifest file. This means adding it to the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize block")," and adding it to the component pipelines where you want your plugin to be executed. For example, an ",(0,i.kt)("inlineCode",{parentName:"p"},"initilize")," block might look as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n new-plugin:\n method: YourFunctionName\n path: 'new-plugin'\n global-config:\n something: true\n")),(0,i.kt)("p",null,"Run your manifest uisng"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm run if-run -- --manifest \n")),(0,i.kt)("p",null,"If you have to link more than one local plugin, for example to test your plugin in a pipeline, you can do so with"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm link new-plugin --save\n")),(0,i.kt)("p",null,"This will create an entry like ",(0,i.kt)("inlineCode",{parentName:"p"},'"new-plugin": "file:path/to/your/plugin"')," in the ",(0,i.kt)("inlineCode",{parentName:"p"},"package.json")," which links to your local plugin. This way, multiple plugins can be linked at once. Of course, these changes should not be committed, but they can be helpful for local testing."),(0,i.kt)("h2",{id:"step-5-publishing-your-plugin"},"Step 5: Publishing your plugin"),(0,i.kt)("p",null,"Now you have run your plugin locally and you are happy with how it works, you can make it public by publishing it to a public Github repository. Now all you have to do to use it in a manifest file is ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install")," it and pass the path to the Github repository in the plugin ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block."),(0,i.kt)("p",null,"For example, for a plugin saved in ",(0,i.kt)("inlineCode",{parentName:"p"},"github.com/my-repo/new-plugin")," you can do the following:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"npm install https://github.com/my-repo/new-plugin\n")),(0,i.kt)("p",null,"Then, in your manifest file, provide the path in the plugin instantiation. You also need to specify which function the plugin instantiates. Let's say you are using the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin from the example above:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n - name: new-plugin\n kind: plugin\n method: FunctionName\n path: https://github.com/my-repo/new-plugin\ntree:\n children:\n child:\n config:\n inputs:\n")),(0,i.kt)("p",null,"Now, when you run the manifest file, it will load the plugin automatically."),(0,i.kt)("p",null,"You can run this using the globally installed IF as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest \n")),(0,i.kt)("h2",{id:"summary-of-steps"},"Summary of steps"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Copy our template repository and update ",(0,i.kt)("inlineCode",{parentName:"li"},"package.json")),(0,i.kt)("li",{parentName:"ul"},"Add your plugin code to ",(0,i.kt)("inlineCode",{parentName:"li"},"index.ts")),(0,i.kt)("li",{parentName:"ul"},"Build and link the plugin using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm run build && npm link")),(0,i.kt)("li",{parentName:"ul"},"Load your plugin into ",(0,i.kt)("inlineCode",{parentName:"li"},"if")," using ",(0,i.kt)("inlineCode",{parentName:"li"},"npm link")),(0,i.kt)("li",{parentName:"ul"},"Initialize your plugin and add it to a pipeline in your manifest file."),(0,i.kt)("li",{parentName:"ul"},"Publish your plugin to Github")),(0,i.kt)("p",null,"You should also create unit tests for your plugin to demonstrate correct execution and handling of corner cases."),(0,i.kt)("h2",{id:"next-steps"},"Next steps"),(0,i.kt)("p",null,"You can read our more advanced guide on ",(0,i.kt)("a",{parentName:"p",href:"/developers/how-to-refine-plugins"},"how to refine your plugins"),"."),(0,i.kt)("h2",{id:"appendix-walk-through-of-the-sum-plugin"},"Appendix: Walk-through of the Sum plugin"),(0,i.kt)("p",null,"To demonstrate how to build a plugin that conforms to the ",(0,i.kt)("inlineCode",{parentName:"p"},"ExecutePlugin"),", let's examine the ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," plugin implements the following logic:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"sum whatever is provided in the ",(0,i.kt)("inlineCode",{parentName:"li"},"input-parameters")," field from ",(0,i.kt)("inlineCode",{parentName:"li"},"globalConfig"),"."),(0,i.kt)("li",{parentName:"ul"},"append the result to each element in the output array with the name provided as ",(0,i.kt)("inlineCode",{parentName:"li"},"output-parameter")," in ",(0,i.kt)("inlineCode",{parentName:"li"},"globalConfig"),".")),(0,i.kt)("p",null,"Let's look at how you would implement this from scratch:"),(0,i.kt)("p",null,"The plugin must be a function conforming to ",(0,i.kt)("inlineCode",{parentName:"p"},"ExecutePlugin"),". You can call the function ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum"),", and inside the body you can add the signature for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," method:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"export const Sum = (\n globalConfig: SumConfig,\n parametersMetadata: PluginParametersMetadata\n): ExecutePlugin => {\n const errorBuilder = buildErrorMessage(Sum.name);\n const metadata = {\n kind: 'execute',\n inputs: parametersMetadata?.inputs,\n outputs: parametersMetadata?.outputs,\n };\n\n /**\n * Calculate the sum of each input.\n */\n const execute = async (inputs: PluginParams[]): Promise => {};\n\n return {\n metadata,\n execute,\n };\n};\n")),(0,i.kt)("p",null,"Your plugin now has the basic structure required for IF integration. Your next task is to add code to the body of ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," to enable the actual plugin logic to be implemented."),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"execute")," function should grab the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," (the values to sum) from ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig"),". it should then iterate over the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, get the values for each of the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameters")," and append them to the ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, using the name from the ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," value in ",(0,i.kt)("inlineCode",{parentName:"p"},"globalConfig"),". Here's what this can look like, with the actual calculation pushed to a separate function, ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Calculate the sum of each input.\n */\nconst execute = async (inputs: PluginParams[]): Promise => {\n const inputParameters = globalConfig['input-parameters'];\n const outputParameter = globalConfig['output-parameter'];\n\n return inputs.map((input) => {\n return {\n ...input,\n [outputParameter]: calculateSum(input, inputParameters),\n };\n });\n\n return {\n metadata,\n execute,\n };\n};\n")),(0,i.kt)("p",null,"Now we just need to define what happens in ",(0,i.kt)("inlineCode",{parentName:"p"},"calculateSum")," - this can be a simple ",(0,i.kt)("inlineCode",{parentName:"p"},"reduce"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"/**\n * Calculates the sum of the energy components.\n */\nconst calculateSum = (input: PluginParams, inputParameters: string[]) =>\n inputParameters.reduce(\n (accumulator, metricToSum) => accumulator + input[metricToSum],\n 0\n );\n")),(0,i.kt)("p",null,"Note that this example did not include any validation or error handling - you will likely want to add some for a real plugin."),(0,i.kt)("h2",{id:"managing-errors"},"Managing errors"),(0,i.kt)("p",null,"If framework provides it's own set of error classes which will make user's live much more easier!\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-core"},"If Core")," plugin has a set of error classes which can be used for having full integration with the IF framework. More details about each error class can be found at ",(0,i.kt)("a",{parentName:"p",href:"/reference/errors"},"Errors Reference")),(0,i.kt)("p",null,"Now you are ready to run your plugin using the ",(0,i.kt)("inlineCode",{parentName:"p"},"if-run")," CLI tool!"))}m.isMDXComponent=!0},8312:(e,n,t)=>{t.d(n,{Z:()=>a});const a=t.p+"assets/images/template-repo-6cc54a91c4b00717cf92334e56c8ec1b.png"}}]); \ No newline at end of file diff --git a/assets/js/3dce4fd2.12640d16.js b/assets/js/3dce4fd2.4524ae0a.js similarity index 53% rename from assets/js/3dce4fd2.12640d16.js rename to assets/js/3dce4fd2.4524ae0a.js index df569812..ab25456a 100644 --- a/assets/js/3dce4fd2.12640d16.js +++ b/assets/js/3dce4fd2.4524ae0a.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[278],{4137:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>m});var r=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function s(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=r.createContext({}),c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},p=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},f=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,s=e.originalType,l=e.parentName,p=a(e,["components","mdxType","originalType","parentName"]),f=c(t),m=i,d=f["".concat(l,".").concat(m)]||f[m]||u[m]||s;return t?r.createElement(d,o(o({ref:n},p),{},{components:t})):r.createElement(d,o({ref:n},p))}));function m(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var s=t.length,o=new Array(s);o[0]=f;var a={};for(var l in n)hasOwnProperty.call(n,l)&&(a[l]=n[l]);a.originalType=e,a.mdxType="string"==typeof e?e:i,o[1]=a;for(var c=2;c{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>s,metadata:()=>a,toc:()=>c});var r=t(7462),i=(t(7294),t(4137));const s={sidebar_position:5},o="Exporting CSV file with `if-csv`",a={unversionedId:"users/how-to-export-csv-file-with-if-csv",id:"users/how-to-export-csv-file-with-if-csv",title:"Exporting CSV file with `if-csv`",description:"IF includes a command line tool called if-csv which is designed to export CSV files based on a specified manifest file and metric.",source:"@site/docs/users/how-to-export-csv-file-with-if-csv.md",sourceDirName:"users",slug:"/users/how-to-export-csv-file-with-if-csv",permalink:"/users/how-to-export-csv-file-with-if-csv",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-export-csv-file-with-if-csv.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"},next:{title:"How to compare files with `if-diff`",permalink:"/users/how-to-compare-files-with-if-diff"}},l={},c=[{value:"Example:",id:"example",level:2}],p={toc:c};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,r.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"exporting-csv-file-with-if-csv"},"Exporting CSV file with ",(0,i.kt)("inlineCode",{parentName:"h1"},"if-csv")),(0,i.kt)("p",null,"IF includes a command line tool called ",(0,i.kt)("inlineCode",{parentName:"p"},"if-csv")," which is designed to export CSV files based on a specified manifest file and metric."),(0,i.kt)("h2",{id:"example"},"Example:"),(0,i.kt)("p",null,"Let's execute this manifest file. This manifest simply sums two components, ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"network/energy")," and assigns the result to ",(0,i.kt)("inlineCode",{parentName:"p"},"energy")," in the outputs array."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags: null\ninitialize:\n plugins:\n sum:\n path: builtin\n method: Sum\n global-config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\n outputs:\n - yaml\nexecution:\n command: >-\n /Users/manushak/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /Users/manushak/Documents/Projects/Green-Software/if/src/if-run/index.ts -m\n ./manifests/test.yaml -o ./manifests/re-test\n environment:\n if-version: 0.5.0\n os: macOS\n os-version: 13.6.7\n node-version: 18.20.0\n date-time: 2024-07-09T16:00:58.218Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.10'\n - '@grnsft/if-plugins@v0.3.2 extraneous -> file:../../../if-models'\n - >-\n @grnsft/if-unofficial-plugins@v0.3.0 extraneous ->\n file:../../../if-unofficial-models\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config:\n sum: null\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n energy: 0.002\n")),(0,i.kt)("p",null,"To generate a CSV file in the provided path, run the following command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./sum.yaml -p energy -o ./output-sum\n")),(0,i.kt)("p",null,"To print data in the console, you need to run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./sum.yaml -p energy\n")),(0,i.kt)("p",null,"The output will be:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"Path,2023-08-06T00:00\ntree.children.child.energy,0.002\n")),(0,i.kt)("p",null,"Alternatively, you can pipe the result from ",(0,i.kt)("inlineCode",{parentName:"p"},"if-run"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m ./sum.yaml | if-csv -p energy\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[278],{4137:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>f});var r=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function s(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var l=r.createContext({}),c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},p=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},m=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,s=e.originalType,l=e.parentName,p=a(e,["components","mdxType","originalType","parentName"]),m=c(t),f=i,d=m["".concat(l,".").concat(f)]||m[f]||u[f]||s;return t?r.createElement(d,o(o({ref:n},p),{},{components:t})):r.createElement(d,o({ref:n},p))}));function f(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var s=t.length,o=new Array(s);o[0]=m;var a={};for(var l in n)hasOwnProperty.call(n,l)&&(a[l]=n[l]);a.originalType=e,a.mdxType="string"==typeof e?e:i,o[1]=a;for(var c=2;c{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>s,metadata:()=>a,toc:()=>c});var r=t(7462),i=(t(7294),t(4137));const s={sidebar_position:5},o="Exporting CSV file with `if-csv`",a={unversionedId:"users/how-to-export-csv-file-with-if-csv",id:"users/how-to-export-csv-file-with-if-csv",title:"Exporting CSV file with `if-csv`",description:"IF includes a command line tool called if-csv which is designed to export CSV files based on a specified manifest file and metric.",source:"@site/docs/users/how-to-export-csv-file-with-if-csv.md",sourceDirName:"users",slug:"/users/how-to-export-csv-file-with-if-csv",permalink:"/users/how-to-export-csv-file-with-if-csv",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-export-csv-file-with-if-csv.md",tags:[],version:"current",sidebarPosition:5,frontMatter:{sidebar_position:5},sidebar:"tutorialSidebar",previous:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"},next:{title:"How to compare files with `if-diff`",permalink:"/users/how-to-compare-files-with-if-diff"}},l={},c=[{value:"Example:",id:"example",level:2}],p={toc:c};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,r.Z)({},p,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"exporting-csv-file-with-if-csv"},"Exporting CSV file with ",(0,i.kt)("inlineCode",{parentName:"h1"},"if-csv")),(0,i.kt)("p",null,"IF includes a command line tool called ",(0,i.kt)("inlineCode",{parentName:"p"},"if-csv")," which is designed to export CSV files based on a specified manifest file and metric."),(0,i.kt)("h2",{id:"example"},"Example:"),(0,i.kt)("p",null,"Let's execute this manifest file. This manifest simply sums two components, ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"network/energy")," and assigns the result to ",(0,i.kt)("inlineCode",{parentName:"p"},"energy")," in the outputs array."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags: null\ninitialize:\n plugins:\n sum:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\nexecution:\n command: >-\n /Users/manushak/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /Users/manushak/Documents/Projects/Green-Software/if/src/if-run/index.ts -m\n manifests/examples/test.yaml\n environment:\n if-version: 0.6.0\n os: macOS\n os-version: 14.6.1\n node-version: 18.20.4\n date-time: 2024-10-03T15:23:26.460Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.25'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.23.8\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n energy: 0.002\n")),(0,i.kt)("p",null,"To generate a CSV file in the provided path, run the following command:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./sum.yaml -p energy -o ./output-sum\n")),(0,i.kt)("p",null,"To print data in the console, you need to run:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./sum.yaml -p energy\n")),(0,i.kt)("p",null,"The output will be:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"Path,2023-08-06T00:00\ntree.children.child.energy,0.002\n")),(0,i.kt)("p",null,"Alternatively, you can pipe the result from ",(0,i.kt)("inlineCode",{parentName:"p"},"if-run"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m ./sum.yaml | if-csv -p energy\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/5c5a410f.879d0746.js b/assets/js/5c5a410f.879d0746.js deleted file mode 100644 index b52eac92..00000000 --- a/assets/js/5c5a410f.879d0746.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[468],{4137:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>m});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var u=i.createContext({}),s=function(e){var n=i.useContext(u),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},p=function(e){var n=s(e.components);return i.createElement(u.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},d=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=s(t),m=a,h=d["".concat(u,".").concat(m)]||d[m]||c[m]||o;return t?i.createElement(h,r(r({ref:n},p),{},{components:t})):i.createElement(h,r({ref:n},p))}));function m(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=d;var l={};for(var u in n)hasOwnProperty.call(n,u)&&(l[u]=n[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var i=t(7462),a=(t(7294),t(4137));const o={"sidebar-position":2},r="Manifest File",l={unversionedId:"major-concepts/manifest-file",id:"major-concepts/manifest-file",title:"Manifest File",description:"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:",source:"@site/docs/major-concepts/manifest-file.md",sourceDirName:"major-concepts",slug:"/major-concepts/manifest-file",permalink:"/major-concepts/manifest-file",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/manifest-file.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"},next:{title:"Plugins",permalink:"/major-concepts/plugins"}},u={},s=[{value:"Structure of a manifest file",id:"structure-of-a-manifest-file",level:2},{value:"Overview",id:"overview",level:3},{value:"Context",id:"context",level:3},{value:"Metadata",id:"metadata",level:4},{value:"Initialize",id:"initialize",level:4},{value:"Execution (auto-generated)",id:"execution-auto-generated",level:4},{value:"Explain",id:"explain",level:3},{value:"Tree",id:"tree",level:3},{value:"Defaults",id:"defaults",level:4},{value:"Inputs",id:"inputs",level:4},{value:"Creating input data",id:"creating-input-data",level:2},{value:"Regrouping a manifest file",id:"regrouping-a-manifest-file",level:2},{value:"Computing a manifest file",id:"computing-a-manifest-file",level:2},{value:"Running combinations of phases",id:"running-combinations-of-phases",level:2},{value:"Outputs",id:"outputs",level:2}],p={toc:s};function c(e){let{components:n,...o}=e;return(0,a.kt)("wrapper",(0,i.Z)({},p,o,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"manifest-file"},"Manifest File"),(0,a.kt)("p",null,"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"They contain all the necessary configurations for Impact Framework"),(0,a.kt)("li",{parentName:"ul"},"They define your application architecture"),(0,a.kt)("li",{parentName:"ul"},"They hold your input data"),(0,a.kt)("li",{parentName:"ul"},"They are shareable, portable and human-readable"),(0,a.kt)("li",{parentName:"ul"},"They can be used as verifiable audits form your application")),(0,a.kt)("p",null,"The manifest is a ",(0,a.kt)("a",{parentName:"p",href:"https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/"},"yaml")," file with a particular structure.\nIt can be thought of as an ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("em",{parentName:"strong"},"executable audit"))," because the file itself can be shared with others and re-executed to verify your environmental impact calculations."),(0,a.kt)("p",null,"It is a formal report detailing not just the end impact but all the assumptions, inputs, and plugins used in calculating the impact."),(0,a.kt)("p",null,"This is possible because ",(0,a.kt)("em",{parentName:"p"},"all the configuration and data required to run Impact Framework is contained in the manifest file"),"."),(0,a.kt)("p",null,"Anyone can download Impact Framework and execute a manifest file to verify the results."),(0,a.kt)("h2",{id:"structure-of-a-manifest-file"},"Structure of a manifest file"),(0,a.kt)("h3",{id:"overview"},"Overview"),(0,a.kt)("p",null,"Manifest files can be simple or very intricate, depending on the plugin pipeline you want to use and the complexity of your application. However, all manifest files conform to a basic structure that looks as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\ninitialize:\n plugins:\n :\n method:\n path:\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n config:\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n")),(0,a.kt)("p",null,"Everything above the ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," is collectively referred to as the ",(0,a.kt)("inlineCode",{parentName:"p"},"context"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," contains the input data and is structured according to the architecture of the application being examined, with individual components being nodes in the tree. Individual components can be grouped under parent nodes."),(0,a.kt)("h3",{id:"context"},"Context"),(0,a.kt)("h4",{id:"metadata"},"Metadata"),(0,a.kt)("p",null,"The global metadata includes the ",(0,a.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"description"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"tags")," that can be used to describe the nature of the manifest file. For example, you might name the file ",(0,a.kt)("inlineCode",{parentName:"p"},"Carbon Jan 2024")," or similar. A short description might briefly outline the scope of the manifest file, e.g. ",(0,a.kt)("inlineCode",{parentName:"p"},"company x's carbon emissions due to web serves from Jab 24 - July 24"),". Tags can be used to group manifest files (we do not explicitly use this field for anything currently)."),(0,a.kt)("h4",{id:"initialize"},"Initialize"),(0,a.kt)("p",null,"The initialize section is where you define which plugins will be used in your manifest file and provide the global configuration for them. Below is sample for initialization:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n :\n method: \n")),(0,a.kt)("p",null,"Where required values are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"method"),": the name of the function exported by the plugin."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin code. For example, for a plugin from our standard library, this value would be ",(0,a.kt)("inlineCode",{parentName:"li"},"builtin"))),(0,a.kt)("p",null,"There is also an optional ",(0,a.kt)("inlineCode",{parentName:"p"},"global-config")," field that can be used to set ",(0,a.kt)("em",{parentName:"p"},"global")," configuration that is common to a plugin wherever it is invoked across the entire manifest file."),(0,a.kt)("p",null,"Impact Framework uses the ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," section to instantiate each plugin. A plugin cannot be invoked elsewhere in the manifest file unless it is included in this section."),(0,a.kt)("p",null,"There is also the option to provide a mapping to the plugin in the initialize block. Its purpose is to rename the arguments expected or returned from the plugin as part of the plugin's execution, avoiding the need to use additional plugins to rename parameters."),(0,a.kt)("p",null,"For example, your plugin might expect cpu/energy and your input data has the parameter cpu-energy returned from another plugin. Instead of using an additional plugin to rename the parameter and add a new one, you can use mapping to:"),(0,a.kt)("p",null,"a) rename the output from the first plugin so that cpu/energy is returned instead of the default cpu-energy"),(0,a.kt)("p",null,"b) instruct the second plugin to accept cpu-energy instead of the default cpu/energy"),(0,a.kt)("p",null,"e.g."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sci:\n kind: plugin\n method: Sci\n path: 'builtin'\n config:\n functional-unit: requests\n mapping:\n sci: if-sci\n")),(0,a.kt)("p",null,"In the outputs, the ",(0,a.kt)("inlineCode",{parentName:"p"},"sci")," value returned by the Sci plugin will be named if-sci."),(0,a.kt)("p",null,"You can also add information to the plugin's initialize section about parameter metadata if you wish to add or override the metadata hardcoded into the plugin. This is what will be reported by the ",(0,a.kt)("inlineCode",{parentName:"p"},"explainer")," feature if you enable it. E.g."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},'plugins:\n "sum-carbon":\n path: "builtin"\n method: Sum\n global-config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n parameter-metadata:\n inputs:\n carbon-operational:\n description: "carbon emitted due to an application\'s execution"\n unit: "gCO2eq"\n aggregation-method: \'sum\',\n embodied-carbon:\n description: "carbon emitted during the production, distribution and disposal of a hardware component, scaled by the fraction of the component\'s lifespan being allocated to the application under investigation"\n unit: "gCO2eq"\n aggregation-method: \'sum\'\n')),(0,a.kt)("h4",{id:"execution-auto-generated"},"Execution (auto-generated)"),(0,a.kt)("p",null,"This section is auto generated by IF at runtime. You don't have to include this section in your manifest. The ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," node contains all the necessary information to rebuild the environment, which can support debugging or verifying output files."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"execution:\n status: success\n command: if-run --manifest examples/basic.yml\n environment:\n if-version: v0.3.2\n os: ubuntu\n os-version: 22.04.6\n node-version: v21.4.0\n date-time: 2023-12-12T00:00:00.000Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - ...\n error: 'InputValidationError: \"duration\" parameter is required. Error code: invalid_type'. ## appears when execution failed\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"status"),": execution state: ",(0,a.kt)("inlineCode",{parentName:"li"},"success")," (indicating that IF successfully executed this manifest) or ",(0,a.kt)("inlineCode",{parentName:"li"},"fail")," (indicating that IF encountered a problem that halted execution)."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"command"),": exact command which was used to run the framework to execute this manifest (it may include full path to tools in place of aliases such as ",(0,a.kt)("inlineCode",{parentName:"li"},"run"),")"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"environment"),": information about the environment the manifest was executed in, including the local operating system, Node.js version, time, and dependencies."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"error"),": this field only appears if execution failed. The error message returned by IF is captured here.")),(0,a.kt)("h3",{id:"explain"},"Explain"),(0,a.kt)("p",null,"This section is autogenerated at runtime. It is a list of all the parameter metadata that IF can scrape from your plugin instances. It looks as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"explain:\n carbon:\n plugins:\n - sci\n unit: gCO2eq\n description: >-\n total carbon emissions attributed to an application's usage as the sum\n of embodied and operational carbon\n aggregation-method: 'sum'\n requests:\n plugins:\n - sci\n unit: requests\n description: number of requests made to application in the given timestep\n aggregation-method: 'sum'\n sci:\n plugins:\n - sci\n unit: gCO2eq/request\n description: >-\n software carbon intensity expressed as a rate of carbon emission per\n request\n aggregation-method: 'sum'\n")),(0,a.kt)("h3",{id:"tree"},"Tree"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," section of a manifest file defines the topology of all the components being measured. The shape of the ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," defines the grouping of components. It describes the architecture of the application being studied and contains all the usage observations for each component. The tree has individual components such as leaves, intermediate nodes representing groupings, and the top level is the root."),(0,a.kt)("p",null,(0,a.kt)("img",{src:t(403).Z,width:"614",height:"569"})),(0,a.kt)("p",null,"For example, a web application could be organized as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"tree:\n children:\n front-end:\n children:\n build-pipeline:\n children:\n vercel:\n github-pages:\n backend-database:\n children:\n server1:\n server2:\n server3:\n front-end:\n networking:\n")),(0,a.kt)("p",null,"This example has a relatively straightforward structure with a maximum of 3 levels of nesting. You can continue to nest components to any depth."),(0,a.kt)("p",null,"Each component has some configuration, some input data, and a plugin pipeline."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"pipeline"),": a list of plugins that should be executed for a specific component. This is broken down into three subsections representing distinct phases of execution that can be triggered independently using command line flags. These subsections are:",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"observe"),": the plugins that generate input data"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"regroup"),": configuration for regrouping input data by given keys"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"compute"),": the plugins that operate over input data and generate output data"))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"defaults"),": fallback values that IF defaults to if they are not present in an input observation."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"inputs"),": an array of ",(0,a.kt)("inlineCode",{parentName:"li"},"observation")," data, with each ",(0,a.kt)("inlineCode",{parentName:"li"},"observation")," containing usage data for a given timestep.")),(0,a.kt)("p",null,"If a component ",(0,a.kt)("em",{parentName:"p"},"does not")," include its own ",(0,a.kt)("inlineCode",{parentName:"p"},"pipeline")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"default")," values, they are inherited from the closest parent."),(0,a.kt)("p",null,"Here's an example of a moderately complex tree:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child-0:\n children:\n child-0-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config: null\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n child-0-2:\n children:\n child-0-2-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config: null\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n")),(0,a.kt)("h4",{id:"defaults"},"Defaults"),(0,a.kt)("p",null,"Defaults are fallback values that are only used if a given value is missing in the inputs array. For example, if you have a value that could feasibly be missing in a given timestep, perhaps because your plugin relies on a third party API that can fail, you can provide a value in ",(0,a.kt)("inlineCode",{parentName:"p"},"defaults")," that can be used as a fallback value."),(0,a.kt)("p",null,"The values in defaults are applied to every timestep where the given value is missing. This means that as well as acting as a fallback ",(0,a.kt)("inlineCode",{parentName:"p"},"defaults")," can be used as a convenience tool for efficiently adding a constant value to every timestep in your inputs array."),(0,a.kt)("h4",{id:"inputs"},"Inputs"),(0,a.kt)("p",null,"Every component includes an ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," field that gets read into plugins as an array. ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," are divided into ",(0,a.kt)("inlineCode",{parentName:"p"},"observations"),", each having a ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"duration"),". Every ",(0,a.kt)("inlineCode",{parentName:"p"},"observation")," refers to an element in ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," representing some snapshot in time."),(0,a.kt)("p",null,"Each plugin takes the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array and applies some calculation or transformation to each ",(0,a.kt)("inlineCode",{parentName:"p"},"observation")," in the array."),(0,a.kt)("p",null,"Observations can include any type of data, including human judgment, assumptions, other plugins, APIs, survey data or telemetry."),(0,a.kt)("p",null,"The separation of timestamps in the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array determines the temporal granularity of your impact calculations. The more frequent your observations, the more accurate your impact assessment."),(0,a.kt)("h2",{id:"creating-input-data"},"Creating input data"),(0,a.kt)("p",null,"The plugins in the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," part of the pipeline generate ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data. The manifest file should not have ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data when the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," phase is executed. Plugins in this phase ",(0,a.kt)("em",{parentName:"p"},"only")," generate input data, they can never generate output data. If you run the observe phase on its own (by running ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --observe"),") then your manifest will be returned populated with input data according to the plugins you included in your ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," pipeline."),(0,a.kt)("h2",{id:"regrouping-a-manifest-file"},"Regrouping a manifest file"),(0,a.kt)("p",null,"The second phase of manifest execution is ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup"),". This reorganizes existing ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data into a new structure using keys provided in the ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," config in the manifest. For example, a manifest with the following ",(0,a.kt)("inlineCode",{parentName:"p"},"tree"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline:\n observe:\n regroup:\n - cloud/instance-type\n - cloud/region\n compute:\n children:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 12\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 1\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 9\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 12\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 1\n")),(0,a.kt)("p",null,"generates the following output when ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --regroup")," is executed:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline:\n observe: null\n regroup:\n - cloud/instance-type\n - cloud/region\n compute: null\n children:\n A1:\n children:\n uk-west:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 12\n uk-east:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 9\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 12\n B1:\n children:\n uk-west:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 1\n uk-east:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 1\n")),(0,a.kt)("h2",{id:"computing-a-manifest-file"},"Computing a manifest file"),(0,a.kt)("p",null,"Impact Framework computes manifest files. For each component in the tree, the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array is passed to each plugin in the ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," pipeline in sequence."),(0,a.kt)("p",null,"In order for the ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," phase to execute correctly, the manifest needs to have ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data available."),(0,a.kt)("p",null,"Each plugin ",(0,a.kt)("em",{parentName:"p"},"enriches")," the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array in some specific way, typically by adding a new ",(0,a.kt)("inlineCode",{parentName:"p"},"key-value")," pair to each observation in the array. For example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin takes in CPU utilization expressed as a percentage as an input and appends ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," expressed in kWh. ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," is then available to be passed as an input to, for example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-e")," plugin."),(0,a.kt)("p",null,"This implies a sequence of plugins where the inputs for some plugins must either be present in the original manifest file or be outputs of the preceding plugins in the pipeline."),(0,a.kt)("p",null,"There are also plugins and built-in features that can synchronize time series of ",(0,a.kt)("inlineCode",{parentName:"p"},"observations")," across an entire tree and aggregate data across time or across components."),(0,a.kt)("h2",{id:"running-combinations-of-phases"},"Running combinations of phases"),(0,a.kt)("p",null,"It is possible to run each phase of the execution individually, or together. You can choose to ",(0,a.kt)("em",{parentName:"p"},"only")," run the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," phases of the manifest execution. This saves you from having to re-execute entire manifests every time you want to tweak something, making it a greener way to use IF."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," executes all the phases together, including ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute"),". It generates yaml output data. However, you can run individual phases by passing ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"--regroup")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"--compute")," flags on the command line. For example, to run ",(0,a.kt)("em",{parentName:"p"},"only")," the compute phase:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --compute\n")),(0,a.kt)("p",null,"Maybe you only want to generate a static file that contains input data but don't want to run the full compute pipeline right now. You can run with ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run --observe\n")),(0,a.kt)("p",null,"You can also combine the command, e.g. if you have a file with inputs and you want to run ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," but not ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --regroup --compute\n")),(0,a.kt)("h2",{id:"outputs"},"Outputs"),(0,a.kt)("p",null,"When Impact Framework computes a manifest file, it appends new data to the manifest file and the final result is an enriched manifest that includes all the configuration and contextual data, the input data, and the results of executing each plugin. This means the output file is completely auditable - the original manifest file can be recovered simply by deleting the ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," sections of the output file."),(0,a.kt)("p",null,"IF generates yaml output data. Any other output formats have to be generated by separate exhaust scripts that take IF's yaml output as their input."),(0,a.kt)("p",null,"Here's an example output file:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags: null\ninitialize:\n plugins:\n sum:\n path: builtin\n method: Sum\n global-config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/Code/if/src/index.ts -m\n /home/user/Code/if/manifests/plugins/sum/success.yml\n environment:\n if-version: 0.3.3-beta.0\n os: linux\n os-version: 5.15.0-105-generic\n node-version: 21.4.0\n date-time: 2024-05-31T09:18:48.895Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config:\n sum: null\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n energy: 0.002\n")))}c.isMDXComponent=!0},403:(e,n,t)=>{t.d(n,{Z:()=>i});const i=t.p+"assets/images/3f18767c1a55cee416e3de70314609e3-c7fa9feaf0993c3ed2b5a34b8b82432c.png"}}]); \ No newline at end of file diff --git a/assets/js/5c5a410f.e81f04e7.js b/assets/js/5c5a410f.e81f04e7.js new file mode 100644 index 00000000..8dc25ab7 --- /dev/null +++ b/assets/js/5c5a410f.e81f04e7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[468],{4137:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>m});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var u=i.createContext({}),p=function(e){var n=i.useContext(u),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},s=function(e){var n=p(e.components);return i.createElement(u.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},d=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,u=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),d=p(t),m=a,h=d["".concat(u,".").concat(m)]||d[m]||c[m]||o;return t?i.createElement(h,r(r({ref:n},s),{},{components:t})):i.createElement(h,r({ref:n},s))}));function m(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=d;var l={};for(var u in n)hasOwnProperty.call(n,u)&&(l[u]=n[u]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>p});var i=t(7462),a=(t(7294),t(4137));const o={"sidebar-position":2},r="Manifest File",l={unversionedId:"major-concepts/manifest-file",id:"major-concepts/manifest-file",title:"Manifest File",description:"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:",source:"@site/docs/major-concepts/manifest-file.md",sourceDirName:"major-concepts",slug:"/major-concepts/manifest-file",permalink:"/major-concepts/manifest-file",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/manifest-file.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"},next:{title:"Plugins",permalink:"/major-concepts/plugins"}},u={},p=[{value:"Structure of a manifest file",id:"structure-of-a-manifest-file",level:2},{value:"Overview",id:"overview",level:3},{value:"Context",id:"context",level:3},{value:"Metadata",id:"metadata",level:4},{value:"Initialize",id:"initialize",level:4},{value:"Execution (auto-generated)",id:"execution-auto-generated",level:4},{value:"Explain",id:"explain",level:3},{value:"Tree",id:"tree",level:3},{value:"Defaults",id:"defaults",level:4},{value:"Inputs",id:"inputs",level:4},{value:"Creating input data",id:"creating-input-data",level:2},{value:"Regrouping a manifest file",id:"regrouping-a-manifest-file",level:2},{value:"Computing a manifest file",id:"computing-a-manifest-file",level:2},{value:"Running combinations of phases",id:"running-combinations-of-phases",level:2},{value:"Outputs",id:"outputs",level:2}],s={toc:p};function c(e){let{components:n,...o}=e;return(0,a.kt)("wrapper",(0,i.Z)({},s,o,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"manifest-file"},"Manifest File"),(0,a.kt)("p",null,"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"They contain all the necessary configurations for Impact Framework"),(0,a.kt)("li",{parentName:"ul"},"They define your application architecture"),(0,a.kt)("li",{parentName:"ul"},"They hold your input data"),(0,a.kt)("li",{parentName:"ul"},"They are shareable, portable and human-readable"),(0,a.kt)("li",{parentName:"ul"},"They can be used as verifiable audits form your application")),(0,a.kt)("p",null,"The manifest is a ",(0,a.kt)("a",{parentName:"p",href:"https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/"},"yaml")," file with a particular structure.\nIt can be thought of as an ",(0,a.kt)("strong",{parentName:"p"},(0,a.kt)("em",{parentName:"strong"},"executable audit"))," because the file itself can be shared with others and re-executed to verify your environmental impact calculations."),(0,a.kt)("p",null,"It is a formal report detailing not just the end impact but all the assumptions, inputs, and plugins used in calculating the impact."),(0,a.kt)("p",null,"This is possible because ",(0,a.kt)("em",{parentName:"p"},"all the configuration and data required to run Impact Framework is contained in the manifest file"),"."),(0,a.kt)("p",null,"Anyone can download Impact Framework and execute a manifest file to verify the results."),(0,a.kt)("h2",{id:"structure-of-a-manifest-file"},"Structure of a manifest file"),(0,a.kt)("h3",{id:"overview"},"Overview"),(0,a.kt)("p",null,"Manifest files can be simple or very intricate, depending on the plugin pipeline you want to use and the complexity of your application. However, all manifest files conform to a basic structure that looks as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\ninitialize:\n plugins:\n :\n method:\n path:\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n")),(0,a.kt)("p",null,"Everything above the ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," is collectively referred to as the ",(0,a.kt)("inlineCode",{parentName:"p"},"context"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," contains the input data and is structured according to the architecture of the application being examined, with individual components being nodes in the tree. Individual components can be grouped under parent nodes."),(0,a.kt)("h3",{id:"context"},"Context"),(0,a.kt)("h4",{id:"metadata"},"Metadata"),(0,a.kt)("p",null,"The global metadata includes the ",(0,a.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"description"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"tags")," that can be used to describe the nature of the manifest file. For example, you might name the file ",(0,a.kt)("inlineCode",{parentName:"p"},"Carbon Jan 2024")," or similar. A short description might briefly outline the scope of the manifest file, e.g. ",(0,a.kt)("inlineCode",{parentName:"p"},"company x's carbon emissions due to web serves from Jab 24 - July 24"),". Tags can be used to group manifest files (we do not explicitly use this field for anything currently)."),(0,a.kt)("h4",{id:"initialize"},"Initialize"),(0,a.kt)("p",null,"The initialize section is where you define which plugins will be used in your manifest file and provide the configuration for them. Below is sample for initialization:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n :\n method: \n")),(0,a.kt)("p",null,"Where required values are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"method"),": the name of the function exported by the plugin."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin code. For example, for a plugin from our standard library, this value would be ",(0,a.kt)("inlineCode",{parentName:"li"},"builtin"))),(0,a.kt)("p",null,"There is also an optional ",(0,a.kt)("inlineCode",{parentName:"p"},"config")," field that can be used to set ",(0,a.kt)("em",{parentName:"p"},"config")," that is common to a plugin wherever it is invoked across the entire manifest file."),(0,a.kt)("p",null,"Impact Framework uses the ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," section to instantiate each plugin. A plugin cannot be invoked elsewhere in the manifest file unless it is included in this section."),(0,a.kt)("p",null,"There is also the option to provide a mapping to the plugin in the initialize block. Its purpose is to rename the arguments expected or returned from the plugin as part of the plugin's execution, avoiding the need to use additional plugins to rename parameters."),(0,a.kt)("p",null,"For example, your plugin might expect cpu/energy and your input data has the parameter cpu-energy returned from another plugin. Instead of using an additional plugin to rename the parameter and add a new one, you can use mapping to:"),(0,a.kt)("p",null,"a) rename the output from the first plugin so that cpu/energy is returned instead of the default cpu-energy"),(0,a.kt)("p",null,"b) instruct the second plugin to accept cpu-energy instead of the default cpu/energy"),(0,a.kt)("p",null,"e.g."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sci:\n kind: plugin\n method: Sci\n path: 'builtin'\n config:\n functional-unit: requests\n mapping:\n sci: if-sci\n")),(0,a.kt)("p",null,"In the outputs, the ",(0,a.kt)("inlineCode",{parentName:"p"},"sci")," value returned by the Sci plugin will be named if-sci."),(0,a.kt)("p",null,"You can also add information to the plugin's initialize section about parameter metadata if you wish to add or override the metadata hardcoded into the plugin. This is what will be reported by the ",(0,a.kt)("inlineCode",{parentName:"p"},"explainer")," feature if you enable it. E.g."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"plugins:\n sum-carbon:\n path: 'builtin'\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n parameter-metadata:\n inputs:\n carbon-operational:\n description: \"carbon emitted due to an application's execution\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n embodied-carbon:\n description: \"carbon emitted during the production, distribution and disposal of a hardware component, scaled by the fraction of the component's lifespan being allocated to the application under investigation\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n")),(0,a.kt)("h4",{id:"execution-auto-generated"},"Execution (auto-generated)"),(0,a.kt)("p",null,"This section is auto generated by IF at runtime. You don't have to include this section in your manifest. The ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," node contains all the necessary information to rebuild the environment, which can support debugging or verifying output files."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"execution:\n status: success\n command: if-run --manifest examples/basic.yml\n environment:\n if-version: v0.3.2\n os: ubuntu\n os-version: 22.04.6\n node-version: v21.4.0\n date-time: 2023-12-12T00:00:00.000Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - ...\n error: 'InputValidationError: \"duration\" parameter is required. Error code: invalid_type'. ## appears when execution failed\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"status"),": execution state: ",(0,a.kt)("inlineCode",{parentName:"li"},"success")," (indicating that IF successfully executed this manifest) or ",(0,a.kt)("inlineCode",{parentName:"li"},"fail")," (indicating that IF encountered a problem that halted execution)."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"command"),": exact command which was used to run the framework to execute this manifest (it may include full path to tools in place of aliases such as ",(0,a.kt)("inlineCode",{parentName:"li"},"run"),")"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"environment"),": information about the environment the manifest was executed in, including the local operating system, Node.js version, time, and dependencies."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("strong",{parentName:"li"},"error"),": this field only appears if execution failed. The error message returned by IF is captured here.")),(0,a.kt)("h3",{id:"explain"},"Explain"),(0,a.kt)("p",null,"This section is autogenerated at runtime. It is a list of all the parameter metadata that IF can scrape from your plugin instances. It looks as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"explain:\n carbon:\n plugins:\n - sci\n unit: gCO2eq\n description: >-\n total carbon emissions attributed to an application's usage as the sum\n of embodied and operational carbon\n aggregation-method:\n time: sum\n component: sum\n requests:\n plugins:\n - sci\n unit: requests\n description: number of requests made to application in the given timestep\n aggregation-method:\n time: sum\n component: sum\n sci:\n plugins:\n - sci\n unit: gCO2eq/request\n description: >-\n software carbon intensity expressed as a rate of carbon emission per\n request\n aggregation-method:\n time: sum\n component: sum\n")),(0,a.kt)("h3",{id:"tree"},"Tree"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," section of a manifest file defines the topology of all the components being measured. The shape of the ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," defines the grouping of components. It describes the architecture of the application being studied and contains all the usage observations for each component. The tree has individual components such as leaves, intermediate nodes representing groupings, and the top level is the root."),(0,a.kt)("p",null,(0,a.kt)("img",{src:t(403).Z,width:"614",height:"569"})),(0,a.kt)("p",null,"For example, a web application could be organized as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"tree:\n children:\n front-end:\n children:\n build-pipeline:\n children:\n vercel:\n github-pages:\n backend-database:\n children:\n server1:\n server2:\n server3:\n front-end:\n networking:\n")),(0,a.kt)("p",null,"This example has a relatively straightforward structure with a maximum of 3 levels of nesting. You can continue to nest components to any depth."),(0,a.kt)("p",null,"Each component has some configuration, some input data, and a plugin pipeline."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"pipeline"),": a list of plugins that should be executed for a specific component. This is broken down into three subsections representing distinct phases of execution that can be triggered independently using command line flags. These subsections are:",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"observe"),": the plugins that generate input data"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"regroup"),": configuration for regrouping input data by given keys"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"compute"),": the plugins that operate over input data and generate output data"))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"defaults"),": fallback values that IF defaults to if they are not present in an input observation."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"inputs"),": an array of ",(0,a.kt)("inlineCode",{parentName:"li"},"observation")," data, with each ",(0,a.kt)("inlineCode",{parentName:"li"},"observation")," containing usage data for a given timestep.")),(0,a.kt)("p",null,"If a component ",(0,a.kt)("em",{parentName:"p"},"does not")," include its own ",(0,a.kt)("inlineCode",{parentName:"p"},"pipeline")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"default")," values, they are inherited from the closest parent."),(0,a.kt)("p",null,"Here's an example of a moderately complex tree:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child-0:\n children:\n child-0-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n child-0-2:\n children:\n child-0-2-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n defaults: null\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 10\n cpu-util: 50\n energy-network: 0.000811\n energy: 0.000811\n")),(0,a.kt)("h4",{id:"defaults"},"Defaults"),(0,a.kt)("p",null,"Defaults are fallback values that are only used if a given value is missing in the inputs array. For example, if you have a value that could feasibly be missing in a given timestep, perhaps because your plugin relies on a third party API that can fail, you can provide a value in ",(0,a.kt)("inlineCode",{parentName:"p"},"defaults")," that can be used as a fallback value."),(0,a.kt)("p",null,"The values in defaults are applied to every timestep where the given value is missing. This means that as well as acting as a fallback ",(0,a.kt)("inlineCode",{parentName:"p"},"defaults")," can be used as a convenience tool for efficiently adding a constant value to every timestep in your inputs array."),(0,a.kt)("h4",{id:"inputs"},"Inputs"),(0,a.kt)("p",null,"Every component includes an ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," field that gets read into plugins as an array. ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," are divided into ",(0,a.kt)("inlineCode",{parentName:"p"},"observations"),", each having a ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"duration"),". Every ",(0,a.kt)("inlineCode",{parentName:"p"},"observation")," refers to an element in ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," representing some snapshot in time."),(0,a.kt)("p",null,"Each plugin takes the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array and applies some calculation or transformation to each ",(0,a.kt)("inlineCode",{parentName:"p"},"observation")," in the array."),(0,a.kt)("p",null,"Observations can include any type of data, including human judgment, assumptions, other plugins, APIs, survey data or telemetry."),(0,a.kt)("p",null,"The separation of timestamps in the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array determines the temporal granularity of your impact calculations. The more frequent your observations, the more accurate your impact assessment."),(0,a.kt)("h2",{id:"creating-input-data"},"Creating input data"),(0,a.kt)("p",null,"The plugins in the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," part of the pipeline generate ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data. The manifest file should not have ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data when the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," phase is executed. Plugins in this phase ",(0,a.kt)("em",{parentName:"p"},"only")," generate input data, they can never generate output data. If you run the observe phase on its own (by running ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --observe"),") then your manifest will be returned populated with input data according to the plugins you included in your ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," pipeline."),(0,a.kt)("h2",{id:"regrouping-a-manifest-file"},"Regrouping a manifest file"),(0,a.kt)("p",null,"The second phase of manifest execution is ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup"),". This reorganizes existing ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data into a new structure using keys provided in the ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," config in the manifest. For example, a manifest with the following ",(0,a.kt)("inlineCode",{parentName:"p"},"tree"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline:\n observe:\n regroup:\n - cloud/instance-type\n - cloud/region\n compute:\n children:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 12\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 1\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 9\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 12\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 1\n")),(0,a.kt)("p",null,"generates the following output when ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --regroup")," is executed:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n my-app:\n pipeline:\n observe: null\n regroup:\n - cloud/instance-type\n - cloud/region\n compute: null\n children:\n A1:\n children:\n uk-west:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 99\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 12\n uk-east:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 9\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 23\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: A1\n cloud/region: uk-east\n cpu/utilization: 12\n B1:\n children:\n uk-west:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-west\n cpu/utilization: 1\n uk-east:\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 11\n - timestamp: 2023-07-06T05:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 67\n - timestamp: 2023-07-06T10:00\n duration: 300\n cloud/instance-type: B1\n cloud/region: uk-east\n cpu/utilization: 1\n")),(0,a.kt)("h2",{id:"computing-a-manifest-file"},"Computing a manifest file"),(0,a.kt)("p",null,"Impact Framework computes manifest files. For each component in the tree, the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array is passed to each plugin in the ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," pipeline in sequence."),(0,a.kt)("p",null,"In order for the ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," phase to execute correctly, the manifest needs to have ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data available."),(0,a.kt)("p",null,"Each plugin ",(0,a.kt)("em",{parentName:"p"},"enriches")," the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array in some specific way, typically by adding a new ",(0,a.kt)("inlineCode",{parentName:"p"},"key-value")," pair to each observation in the array. For example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin takes in CPU utilization expressed as a percentage as an input and appends ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," expressed in kWh. ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," is then available to be passed as an input to, for example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-e")," plugin."),(0,a.kt)("p",null,"This implies a sequence of plugins where the inputs for some plugins must either be present in the original manifest file or be outputs of the preceding plugins in the pipeline."),(0,a.kt)("p",null,"There are also plugins and built-in features that can synchronize time series of ",(0,a.kt)("inlineCode",{parentName:"p"},"observations")," across an entire tree and aggregate data across time or across components."),(0,a.kt)("h2",{id:"running-combinations-of-phases"},"Running combinations of phases"),(0,a.kt)("p",null,"It is possible to run each phase of the execution individually, or together. You can choose to ",(0,a.kt)("em",{parentName:"p"},"only")," run the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," phases of the manifest execution. This saves you from having to re-execute entire manifests every time you want to tweak something, making it a greener way to use IF."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," executes all the phases together, including ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute"),". It generates yaml output data. However, you can run individual phases by passing ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"--regroup")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"--compute")," flags on the command line. For example, to run ",(0,a.kt)("em",{parentName:"p"},"only")," the compute phase:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --compute\n")),(0,a.kt)("p",null,"Maybe you only want to generate a static file that contains input data but don't want to run the full compute pipeline right now. You can run with ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run --observe\n")),(0,a.kt)("p",null,"You can also combine the command, e.g. if you have a file with inputs and you want to run ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," but not ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --regroup --compute\n")),(0,a.kt)("h2",{id:"outputs"},"Outputs"),(0,a.kt)("p",null,"When Impact Framework computes a manifest file, it appends new data to the manifest file and the final result is an enriched manifest that includes all the configuration and contextual data, the input data, and the results of executing each plugin. This means the output file is completely auditable - the original manifest file can be recovered simply by deleting the ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," sections of the output file."),(0,a.kt)("p",null,"IF generates yaml output data. Any other output formats have to be generated by separate exhaust scripts that take IF's yaml output as their input."),(0,a.kt)("p",null,"Here's an example output file:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags: null\ninitialize:\n plugins:\n sum:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/Code/if/src/index.ts -m\n /home/user/Code/if/manifests/plugins/sum/success.yml\n environment:\n if-version: 0.3.3-beta.0\n os: linux\n os-version: 5.15.0-105-generic\n node-version: 21.4.0\n date-time: 2024-05-31T09:18:48.895Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n energy: 0.002\n")))}c.isMDXComponent=!0},403:(e,n,t)=>{t.d(n,{Z:()=>i});const i=t.p+"assets/images/3f18767c1a55cee416e3de70314609e3-c7fa9feaf0993c3ed2b5a34b8b82432c.png"}}]); \ No newline at end of file diff --git a/assets/js/68ffc9f1.5917f2f6.js b/assets/js/68ffc9f1.5917f2f6.js deleted file mode 100644 index f6868feb..00000000 --- a/assets/js/68ffc9f1.5917f2f6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[644],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var p=i.createContext({}),s=function(e){var n=i.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=s(e.components);return i.createElement(p.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(t),d=a,h=m["".concat(p,".").concat(d)]||m[d]||c[d]||o;return t?i.createElement(h,r(r({ref:n},u),{},{components:t})):i.createElement(h,r({ref:n},u))}));function d(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=m;var l={};for(var p in n)hasOwnProperty.call(n,p)&&(l[p]=n[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var i=t(7462),a=(t(7294),t(4137));const o={sidebar_position:4},r="How to write a manifest file",l={unversionedId:"users/how-to-write-manifests",id:"users/how-to-write-manifests",title:"How to write a manifest file",description:"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app.",source:"@site/docs/users/how-to-write-manifests.md",sourceDirName:"users",slug:"/users/how-to-write-manifests",permalink:"/users/how-to-write-manifests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-write-manifests.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"How to load plugins",permalink:"/users/how-to-import-plugins"},next:{title:"Exporting CSV file with `if-csv`",permalink:"/users/how-to-export-csv-file-with-if-csv"}},p={},s=[{value:"Structure of a manifest",id:"structure-of-a-manifest",level:2},{value:"Project metadata",id:"project-metadata",level:3},{value:"Initialize",id:"initialize",level:3},{value:"Tree",id:"tree",level:3},{value:"Inputs",id:"inputs",level:3},{value:"More complex manifests",id:"more-complex-manifests",level:2},{value:"Complex pipelines",id:"complex-pipelines",level:3},{value:"Complex applications",id:"complex-applications",level:3},{value:"Choosing which plugins to run",id:"choosing-which-plugins-to-run",level:2},{value:"Adding real-life inputs",id:"adding-real-life-inputs",level:2},{value:"Running a manifest",id:"running-a-manifest",level:2}],u={toc:s};function c(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"how-to-write-a-manifest-file"},"How to write a manifest file"),(0,a.kt)("p",null,"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app."),(0,a.kt)("h2",{id:"structure-of-a-manifest"},"Structure of a manifest"),(0,a.kt)("p",null,"The basic structure of a manifest is as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\ninitialize:\n plugins:\n :\n method:\n path:\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n defaults:\n inputs:\n")),(0,a.kt)("h3",{id:"project-metadata"},"Project metadata"),(0,a.kt)("p",null,"The file starts with some metadata about the project. There are no strict specifications for what to put in these fields, they are for you to keep track of your manifest files and to help other users to understand your use case."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\n")),(0,a.kt)("h3",{id:"initialize"},"Initialize"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," fields are where you specify each individual plugin that will be initialized in your pipeline. The plugins can be initialized in any order, but can only be invoked elsewhere in the manifest if they have been initialized first here. In each case, you will need to provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"path")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"method")," (and ",(0,a.kt)("inlineCode",{parentName:"p"},"global-config")," if your plugin requires it):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sci-m:\n path: ''\n method:\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"name")," is the name you want this plugin instance to be recognized as by Impact Framework."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"path")," defines where IF should look for the installed plugin. For example, for our standard library of plugins you would specify ",(0,a.kt)("inlineCode",{parentName:"li"},"builtin"),", for other installed plugins you use the name of the directory they are installed into in ",(0,a.kt)("inlineCode",{parentName:"li"},"node_modules"),"."),(0,a.kt)("li",{parentName:"ul"},"For the ",(0,a.kt)("inlineCode",{parentName:"li"},"method")," field, you should provide the name of the function exported by your plugin. For example, for the ",(0,a.kt)("inlineCode",{parentName:"li"},"sum")," plugin, the correct value is ",(0,a.kt)("inlineCode",{parentName:"li"},"Sum"),".")),(0,a.kt)("h3",{id:"tree"},"Tree"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," fields are where you define the various components of your application. Each component is defined as ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),", where each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),"'s output is summed to give the overall impact. Each ",(0,a.kt)("inlineCode",{parentName:"p"},"child")," can have its own plugin pipeline and its own configuration, but when none is provided, it is inherited from the tree-level configuration."),(0,a.kt)("p",null,"In the following example, there is only one component but the plugin pipeline contains two plugins; ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m"),". Neither requires any ",(0,a.kt)("inlineCode",{parentName:"p"},"config")," data, but certain information is required in ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - teads-curve\n - sci-m\n defaults:\n inputs:\n - timestamp: '2023-11-02T10:35:31.820Z'\n duration: 3600\n total-embodied-emissions: 1533.12\n time-reserved: 1\n expected-lifespan: 3\n resources-reserved: 1\n total-resources: 8\n")),(0,a.kt)("h3",{id:"inputs"},"Inputs"),(0,a.kt)("p",null,"The most granular level of the manifest file are the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),". This is where you can add specific data for each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),". Inputs must always include a ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"duration"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n cpu-util: 45\n")),(0,a.kt)("p",null,"You now have a simple manifest file that will use the plugin config and input data to run the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m")," plugins. The output data will be appended to the manifest under a new ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," field and saved as an output file."),(0,a.kt)("h2",{id:"more-complex-manifests"},"More complex manifests"),(0,a.kt)("h3",{id:"complex-pipelines"},"Complex pipelines"),(0,a.kt)("p",null,"Whilst the manifest file we looked at above works perfectly well, it will only return the most basic output data. Most users will want to calculate an SCI score, which implies a number of additional steps:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"operational-carbon")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"embodied-carbon")," must appear as inputs."),(0,a.kt)("li",{parentName:"ul"},"This means that ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," will need to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-m")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," in the plugin pipeline."),(0,a.kt)("li",{parentName:"ul"},"In most cases, ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," will have to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," to ensure ",(0,a.kt)("inlineCode",{parentName:"li"},"energy")," is available to be piped to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o"),"."),(0,a.kt)("li",{parentName:"ul"},"The inputs to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," will most likely be coming from a plugin such as ",(0,a.kt)("inlineCode",{parentName:"li"},"teads-curve")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"boavizta"),"."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," plugin also requires ",(0,a.kt)("inlineCode",{parentName:"li"},"functional-unit")," information so it can convert the estimated ",(0,a.kt)("inlineCode",{parentName:"li"},"carbon")," into a useful unit."),(0,a.kt)("li",{parentName:"ul"},"You may also wish to grab your ",(0,a.kt)("inlineCode",{parentName:"li"},"input")," data by querying a metrics API on a virtual machine.")),(0,a.kt)("p",null,"The example below gives you the full pipeline implemented in a manifest. There are also several other executable example manifests in ",(0,a.kt)("inlineCode",{parentName:"p"},"if/manifests/examples")," that you can run for yourself."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: pipeline-with-aggregate\ndescription: a full pipeline with the aggregate feature enabled\ntags:\naggregation:\n metrics:\n - 'carbon'\n type: 'both'\ninitialize:\n plugins:\n 'interpolate':\n method: Interpolation\n path: 'builtin'\n global-config:\n method: linear\n x: [0, 10, 50, 100]\n y: [0.12, 0.32, 0.75, 1.02]\n input-parameter: 'cpu/utilization'\n output-parameter: 'cpu-factor'\n parameter-metadata:\n inputs:\n cpu/utilization:\n description: refers to CPU utilization\n unit: percentage\n outputs:\n cpu-factor:\n description: the factor of cpu\n unit: kWh\n 'cpu-factor-to-wattage':\n method: Multiply\n path: builtin\n global-config:\n input-parameters: ['cpu-factor', 'cpu/thermal-design-power']\n output-parameter: 'cpu-wattage'\n parameter-metadata:\n inputs:\n cpu-factor:\n description: the factor of cpu\n unit: kWh\n cpu/thermal-design-power:\n description: thermal design power for a processor\n unit: kwh\n outputs:\n cpu-wattage:\n description: cpu in Wattage\n unit: wattage\n 'wattage-times-duration':\n method: Multiply\n path: builtin\n global-config:\n input-parameters: ['cpu-wattage', 'duration']\n output-parameter: 'cpu-wattage-times-duration'\n 'wattage-to-energy-kwh':\n method: Divide\n path: 'builtin'\n global-config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n 'calculate-vcpu-ratio':\n method: Divide\n path: 'builtin'\n global-config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n 'correct-cpu-energy-for-vcpu-ratio':\n method: Divide\n path: 'builtin'\n global-config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu-energy-kwh\n 'sci-embodied':\n path: 'builtin'\n method: SciEmbodied\n 'operational-carbon':\n method: Multiply\n path: builtin\n global-config:\n input-parameters: ['cpu-energy-kwh', 'grid/carbon-intensity']\n output-parameter: 'carbon-operational'\n 'sci':\n path: 'builtin'\n method: Sci\n global-config:\n functional-unit-time: 1 sec\n functional-unit: requests # factor to convert per time to per f.unit\n parameter-metadata:\n inputs:\n carbon:\n description: an amount of carbon emitted into the atmosphere\n unit: gCO2e\n requests:\n description: factor to convert per time to per f.unit\n unit: number\n outputs:\n sci:\n description: carbon expressed in terms of the given functional unit\n unit: gCO2e\n 'sum-carbon':\n path: 'builtin'\n method: Sum\n global-config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n 'time-sync':\n method: TimeSync\n path: 'builtin'\n global-config:\n start-time: '2023-12-12T00:00:00.000Z'\n end-time: '2023-12-12T00:01:00.000Z'\n interval: 5\n allow-padding: true\ntree:\n children:\n child-1:\n pipeline:\n observe:\n regroup:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - sci-embodied\n - operational-carbon\n - sum-carbon\n - time-sync\n # - sci\n config:\n group-by:\n group:\n - cloud/region\n - cloud/instance-type\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n vcpus-total: 8\n vcpus-allocated: 1\n inputs:\n - timestamp: '2023-12-12T00:00:00.000Z'\n cloud/instance-type: A1\n cloud/region: uk-west\n duration: 1\n cpu/utilization: 10\n requests: 10\n - timestamp: '2023-12-12T00:00:01.000Z'\n duration: 5\n cpu/utilization: 20\n cloud/instance-type: A1\n cloud/region: uk-west\n requests: 5\n - timestamp: '2023-12-12T00:00:06.000Z'\n duration: 7\n cpu/utilization: 15\n cloud/instance-type: A1\n cloud/region: uk-west\n requests: 15\n - timestamp: '2023-12-12T00:00:13.000Z'\n duration: 30\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 15\n requests: 30\n")),(0,a.kt)("h3",{id:"complex-applications"},"Complex applications"),(0,a.kt)("p",null,"The manifest examples provided so far have only had a single component. However, Impact Framework can handle any number of nested ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),"."),(0,a.kt)("p",null,"In this way, you can combine complex plugin pipelines and application architectures to calculate the energy and carbon outputs of complicated systems."),(0,a.kt)("h2",{id:"choosing-which-plugins-to-run"},"Choosing which plugins to run"),(0,a.kt)("p",null,"The plugins are designed to be composable, but they each have specific input requirements that must be met in order for the plugins to run correctly. For example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin requires ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/thermal-design-power")," to be available in the manifest. If it is not there, the plugin cannot use it to calculate ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy"),"."),(0,a.kt)("p",null,"It is also possible to leapfrog some plugins if you have access to high-level data. For example, perhaps you already know the energy being used by your CPU. In this case, there is no need to run ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve"),", you can simply provide ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," as an ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," and omit ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," from the plugin pipeline."),(0,a.kt)("p",null,"We have deliberately made the plugins modular and composable so that you can be creative in developing new plugins to replace those provided as part of IF."),(0,a.kt)("h2",{id:"adding-real-life-inputs"},"Adding real-life inputs"),(0,a.kt)("p",null,"The examples above already include inputs for the components. However, you may want to input real-life data into the manifest file."),(0,a.kt)("p",null,"There is no one-size-fits-all solution for getting data into the manifest file. This is because there are so many possible sources for your input data, all of which have their own particular requirements related to authorization, API request syntax and return types. Therefore, the approach taken by IF is to have specific plugins for specific services."),(0,a.kt)("p",null,"The recommended method for integrating data is to use the plugin system of the Impact Framework. You can either use an existing specific importer plugin or write your own."),(0,a.kt)("p",null,"There are already some community plugins available, including plugins for fetching data from Kubernetes, GCP, and third-party data aggregators like Datadog."),(0,a.kt)("p",null,"If there is no fitting plugin available yet, we encourage you to write and add one for your specific use case. See ",(0,a.kt)("a",{parentName:"p",href:"./developers/"},"developer documentation")," for more information on how to build a plugin. There is a ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-unofficial-plugins/blob/main/src/lib/azure-importer/README.md"},"Azure-Importer")," you can as a prototype and starting point for your own development.\nIf you already have external scripts you might have a look at the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/shell/README.md"},"shell plugin")," to integrate them with the Impact Framework."),(0,a.kt)("p",null,"If you just need data for testing purposes, you can use the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if-plugins/blob/main/src/lib/mock-observations/README.md"},"mock-observation")," plugin."),(0,a.kt)("h2",{id:"running-a-manifest"},"Running a manifest"),(0,a.kt)("p",null,"You run a manifest by providing its path to our command line tool and a path to save the results file to. You can run a manifest named ",(0,a.kt)("inlineCode",{parentName:"p"},"my-manifest.yml")," using the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest my-manifest.yml\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/68ffc9f1.d410a3f8.js b/assets/js/68ffc9f1.d410a3f8.js new file mode 100644 index 00000000..a4865356 --- /dev/null +++ b/assets/js/68ffc9f1.d410a3f8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[644],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var p=i.createContext({}),s=function(e){var n=i.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=s(e.components);return i.createElement(p.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(t),d=a,h=m["".concat(p,".").concat(d)]||m[d]||c[d]||o;return t?i.createElement(h,r(r({ref:n},u),{},{components:t})):i.createElement(h,r({ref:n},u))}));function d(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=m;var l={};for(var p in n)hasOwnProperty.call(n,p)&&(l[p]=n[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var i=t(7462),a=(t(7294),t(4137));const o={sidebar_position:4},r="How to write a manifest file",l={unversionedId:"users/how-to-write-manifests",id:"users/how-to-write-manifests",title:"How to write a manifest file",description:"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app.",source:"@site/docs/users/how-to-write-manifests.md",sourceDirName:"users",slug:"/users/how-to-write-manifests",permalink:"/users/how-to-write-manifests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-write-manifests.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"How to load plugins",permalink:"/users/how-to-import-plugins"},next:{title:"Exporting CSV file with `if-csv`",permalink:"/users/how-to-export-csv-file-with-if-csv"}},p={},s=[{value:"Structure of a manifest",id:"structure-of-a-manifest",level:2},{value:"Project metadata",id:"project-metadata",level:3},{value:"Initialize",id:"initialize",level:3},{value:"Tree",id:"tree",level:3},{value:"Inputs",id:"inputs",level:3},{value:"More complex manifests",id:"more-complex-manifests",level:2},{value:"Complex pipelines",id:"complex-pipelines",level:3},{value:"Complex applications",id:"complex-applications",level:3},{value:"Choosing which plugins to run",id:"choosing-which-plugins-to-run",level:2},{value:"Adding real-life inputs",id:"adding-real-life-inputs",level:2},{value:"Running a manifest",id:"running-a-manifest",level:2}],u={toc:s};function c(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"how-to-write-a-manifest-file"},"How to write a manifest file"),(0,a.kt)("p",null,"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app."),(0,a.kt)("h2",{id:"structure-of-a-manifest"},"Structure of a manifest"),(0,a.kt)("p",null,"The basic structure of a manifest is as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\ninitialize:\n plugins:\n :\n method:\n path:\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n defaults:\n inputs:\n")),(0,a.kt)("h3",{id:"project-metadata"},"Project metadata"),(0,a.kt)("p",null,"The file starts with some metadata about the project. There are no strict specifications for what to put in these fields, they are for you to keep track of your manifest files and to help other users to understand your use case."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name:\ndescription:\ntags:\n")),(0,a.kt)("h3",{id:"initialize"},"Initialize"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," fields are where you specify each individual plugin that will be initialized in your pipeline. The plugins can be initialized in any order, but can only be invoked elsewhere in the manifest if they have been initialized first here. In each case, you will need to provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"name"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"path")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"method")," (and ",(0,a.kt)("inlineCode",{parentName:"p"},"config")," if your plugin requires it):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n sci-m:\n path: ''\n method:\n")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"name")," is the name you want this plugin instance to be recognized as by Impact Framework."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"path")," defines where IF should look for the installed plugin. For example, for our standard library of plugins you would specify ",(0,a.kt)("inlineCode",{parentName:"li"},"builtin"),", for other installed plugins you use the name of the directory they are installed into in ",(0,a.kt)("inlineCode",{parentName:"li"},"node_modules"),"."),(0,a.kt)("li",{parentName:"ul"},"For the ",(0,a.kt)("inlineCode",{parentName:"li"},"method")," field, you should provide the name of the function exported by your plugin. For example, for the ",(0,a.kt)("inlineCode",{parentName:"li"},"sum")," plugin, the correct value is ",(0,a.kt)("inlineCode",{parentName:"li"},"Sum"),".")),(0,a.kt)("h3",{id:"tree"},"Tree"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," fields are where you define the various components of your application. Each component is defined as ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),", where each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),"'s output is summed to give the overall impact. Each ",(0,a.kt)("inlineCode",{parentName:"p"},"child")," can have its own plugin pipeline and its own configuration, but when none is provided, it is inherited from the tree-level configuration."),(0,a.kt)("p",null,"In the following example, there is only one component but the plugin pipeline contains two plugins; ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m"),". Neither requires any ",(0,a.kt)("inlineCode",{parentName:"p"},"config")," data, but certain information is required in ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - teads-curve\n - sci-m\n defaults:\n inputs:\n - timestamp: '2023-11-02T10:35:31.820Z'\n duration: 3600\n total-embodied-emissions: 1533.12\n time-reserved: 1\n expected-lifespan: 3\n resources-reserved: 1\n total-resources: 8\n")),(0,a.kt)("h3",{id:"inputs"},"Inputs"),(0,a.kt)("p",null,"The most granular level of the manifest file are the ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs"),". This is where you can add specific data for each ",(0,a.kt)("inlineCode",{parentName:"p"},"child"),". Inputs must always include a ",(0,a.kt)("inlineCode",{parentName:"p"},"timestamp")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"duration"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n cpu-util: 45\n")),(0,a.kt)("p",null,"You now have a simple manifest file that will use the plugin config and input data to run the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"sci-m")," plugins. The output data will be appended to the manifest under a new ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," field and saved as an output file."),(0,a.kt)("h2",{id:"more-complex-manifests"},"More complex manifests"),(0,a.kt)("h3",{id:"complex-pipelines"},"Complex pipelines"),(0,a.kt)("p",null,"Whilst the manifest file we looked at above works perfectly well, it will only return the most basic output data. Most users will want to calculate an SCI score, which implies a number of additional steps:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"operational-carbon")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"embodied-carbon")," must appear as inputs."),(0,a.kt)("li",{parentName:"ul"},"This means that ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," will need to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-m")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," in the plugin pipeline."),(0,a.kt)("li",{parentName:"ul"},"In most cases, ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o")," will have to be preceded by ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," to ensure ",(0,a.kt)("inlineCode",{parentName:"li"},"energy")," is available to be piped to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-o"),"."),(0,a.kt)("li",{parentName:"ul"},"The inputs to ",(0,a.kt)("inlineCode",{parentName:"li"},"sci-e")," will most likely be coming from a plugin such as ",(0,a.kt)("inlineCode",{parentName:"li"},"teads-curve")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"boavizta"),"."),(0,a.kt)("li",{parentName:"ul"},"The ",(0,a.kt)("inlineCode",{parentName:"li"},"sci")," plugin also requires ",(0,a.kt)("inlineCode",{parentName:"li"},"functional-unit")," information so it can convert the estimated ",(0,a.kt)("inlineCode",{parentName:"li"},"carbon")," into a useful unit."),(0,a.kt)("li",{parentName:"ul"},"You may also wish to grab your ",(0,a.kt)("inlineCode",{parentName:"li"},"input")," data by querying a metrics API on a virtual machine.")),(0,a.kt)("p",null,"The example below gives you the full pipeline implemented in a manifest. There are also several other executable example manifests in ",(0,a.kt)("inlineCode",{parentName:"p"},"if/manifests/examples")," that you can run for yourself."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: pipeline-with-aggregate\ndescription: a full pipeline with the aggregate feature enabled\ntags:\naggregation:\n metrics:\n - 'carbon'\n type: 'both'\ninitialize:\n plugins:\n 'interpolate':\n method: Interpolation\n path: 'builtin'\n config:\n method: linear\n x: [0, 10, 50, 100]\n y: [0.12, 0.32, 0.75, 1.02]\n input-parameter: 'cpu/utilization'\n output-parameter: 'cpu-factor'\n parameter-metadata:\n inputs:\n cpu/utilization:\n description: refers to CPU utilization\n unit: percentage\n aggregation-method:\n time: avg\n component: avg\n outputs:\n cpu-factor:\n description: the factor of cpu\n unit: kWh\n aggregation-method:\n time: avg\n component: avg\n 'cpu-factor-to-wattage':\n method: Multiply\n path: builtin\n config:\n input-parameters: ['cpu-factor', 'cpu/thermal-design-power']\n output-parameter: 'cpu-wattage'\n parameter-metadata:\n inputs:\n cpu-factor:\n description: the factor of cpu\n unit: kWh\n aggregation-method:\n time: avg\n component: avg\n cpu/thermal-design-power:\n description: thermal design power for a processor\n unit: kwh\n aggregation-method:\n time: avg\n component: avg\n outputs:\n cpu-wattage:\n description: cpu in Wattage\n unit: wattage\n aggregation-method:\n time: sum\n component: sum\n 'wattage-times-duration':\n method: Multiply\n path: builtin\n config:\n input-parameters: ['cpu-wattage', 'duration']\n output-parameter: 'cpu-wattage-times-duration'\n 'wattage-to-energy-kwh':\n method: Divide\n path: 'builtin'\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n 'calculate-vcpu-ratio':\n method: Divide\n path: 'builtin'\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n 'correct-cpu-energy-for-vcpu-ratio':\n method: Divide\n path: 'builtin'\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu-energy-kwh\n 'sci-embodied':\n path: 'builtin'\n method: SciEmbodied\n 'operational-carbon':\n method: Multiply\n path: builtin\n config:\n input-parameters: ['cpu-energy-kwh', 'grid/carbon-intensity']\n output-parameter: 'carbon-operational'\n 'sci':\n path: 'builtin'\n method: Sci\n config:\n functional-unit-time: 1 sec\n functional-unit: requests # factor to convert per time to per f.unit\n parameter-metadata:\n inputs:\n carbon:\n description: an amount of carbon emitted into the atmosphere\n unit: gCO2e\n aggregation-method:\n time: sum\n component: sum\n requests:\n description: factor to convert per time to per f.unit\n unit: number\n aggregation-method:\n time: sum\n component: sum\n outputs:\n sci:\n description: carbon expressed in terms of the given functional unit\n unit: gCO2e\n aggregation-method:\n time: avg\n component: sum\n 'sum-carbon':\n path: 'builtin'\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n 'time-sync':\n method: TimeSync\n path: 'builtin'\n config:\n start-time: '2023-12-12T00:00:00.000Z'\n end-time: '2023-12-12T00:01:00.000Z'\n interval: 5\n allow-padding: true\ntree:\n children:\n child-1:\n pipeline:\n observe:\n regroup:\n - cloud/region\n - cloud/instance-type\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - sci-embodied\n - operational-carbon\n - sum-carbon\n - time-sync\n # - sci\n defaults:\n cpu/thermal-design-power: 100\n grid/carbon-intensity: 800\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n vcpus-total: 8\n vcpus-allocated: 1\n inputs:\n - timestamp: '2023-12-12T00:00:00.000Z'\n cloud/instance-type: A1\n cloud/region: uk-west\n duration: 1\n cpu/utilization: 10\n requests: 10\n - timestamp: '2023-12-12T00:00:01.000Z'\n duration: 5\n cpu/utilization: 20\n cloud/instance-type: A1\n cloud/region: uk-west\n requests: 5\n - timestamp: '2023-12-12T00:00:06.000Z'\n duration: 7\n cpu/utilization: 15\n cloud/instance-type: A1\n cloud/region: uk-west\n requests: 15\n - timestamp: '2023-12-12T00:00:13.000Z'\n duration: 30\n cloud/instance-type: A1\n cloud/region: uk-west\n cpu/utilization: 15\n requests: 30\n")),(0,a.kt)("h3",{id:"complex-applications"},"Complex applications"),(0,a.kt)("p",null,"The manifest examples provided so far have only had a single component. However, Impact Framework can handle any number of nested ",(0,a.kt)("inlineCode",{parentName:"p"},"children"),"."),(0,a.kt)("p",null,"In this way, you can combine complex plugin pipelines and application architectures to calculate the energy and carbon outputs of complicated systems."),(0,a.kt)("h2",{id:"choosing-which-plugins-to-run"},"Choosing which plugins to run"),(0,a.kt)("p",null,"The plugins are designed to be composable, but they each have specific input requirements that must be met in order for the plugins to run correctly. For example, the ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," plugin requires ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/thermal-design-power")," to be available in the manifest. If it is not there, the plugin cannot use it to calculate ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy"),"."),(0,a.kt)("p",null,"It is also possible to leapfrog some plugins if you have access to high-level data. For example, perhaps you already know the energy being used by your CPU. In this case, there is no need to run ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve"),", you can simply provide ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," as an ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," and omit ",(0,a.kt)("inlineCode",{parentName:"p"},"teads-curve")," from the plugin pipeline."),(0,a.kt)("p",null,"We have deliberately made the plugins modular and composable so that you can be creative in developing new plugins to replace those provided as part of IF."),(0,a.kt)("h2",{id:"adding-real-life-inputs"},"Adding real-life inputs"),(0,a.kt)("p",null,"The examples above already include inputs for the components. However, you may want to input real-life data into the manifest file."),(0,a.kt)("p",null,"There is no one-size-fits-all solution for getting data into the manifest file. This is because there are so many possible sources for your input data, all of which have their own particular requirements related to authorization, API request syntax and return types. Therefore, the approach taken by IF is to have specific plugins for specific services."),(0,a.kt)("p",null,"The recommended method for integrating data is to use the plugin system of the Impact Framework. You can either use an existing specific importer plugin or write your own."),(0,a.kt)("p",null,"There are already some community plugins available, including plugins for fetching data from Kubernetes, GCP, and third-party data aggregators like Datadog."),(0,a.kt)("p",null,"If there is no fitting plugin available yet, we encourage you to write and add one for your specific use case. See ",(0,a.kt)("a",{parentName:"p",href:"./developers/"},"developer documentation")," for more information on how to build a plugin."),(0,a.kt)("p",null,"If you already have external scripts you might have a look at the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/src/if-run/builtins/shell/README.md"},"shell plugin")," to integrate them with the Impact Framework."),(0,a.kt)("p",null,"If you just need data for testing purposes, you can use the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/src/if-run/builtins/mock-observations/README.md"},"mock-observation")," plugin."),(0,a.kt)("h2",{id:"running-a-manifest"},"Running a manifest"),(0,a.kt)("p",null,"You run a manifest by providing its path to our command line tool and a path to save the results file to. You can run a manifest named ",(0,a.kt)("inlineCode",{parentName:"p"},"my-manifest.yml")," using the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest my-manifest.yml\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/75e434b4.f78cefdd.js b/assets/js/75e434b4.f78cefdd.js deleted file mode 100644 index fe4c7215..00000000 --- a/assets/js/75e434b4.f78cefdd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[229],{4137:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),u=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=u(n),f=a,d=m["".concat(p,".").concat(f)]||m[f]||c[f]||i;return n?r.createElement(d,o(o({ref:t},s),{},{components:n})):r.createElement(d,o({ref:t},s))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>u});var r=n(7462),a=(n(7294),n(4137));const i={},o="Plugins",l={unversionedId:"reference/plugins",id:"reference/plugins",title:"Plugins",description:"Impact Framework works by executing pipelines of plugins over input data. Those plugins are re-useable units of code that can be thought of as Lego bricks - simple blocks of code that can be assembled into complex workflows.",source:"@site/docs/reference/plugins.md",sourceDirName:"reference",slug:"/reference/plugins",permalink:"/reference/plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/plugins.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"IF features",permalink:"/reference/features"},next:{title:"FAQs",permalink:"/FAQ"}},p={},u=[{value:"built-in",id:"built-in",level:3}],s={toc:u};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"plugins"},"Plugins"),(0,a.kt)("p",null,"Impact Framework works by executing pipelines of plugins over input data. Those plugins are re-useable units of code that can be thought of as Lego bricks - simple blocks of code that can be assembled into complex workflows."),(0,a.kt)("p",null,"IF comes bundled with a standard library of ",(0,a.kt)("inlineCode",{parentName:"p"},"builtins")," that allow you to do basic and/or generic operations over your data. These include simple arithmetic, regrouping data, calculating SCI scores, and running processes in a spawned shell."),(0,a.kt)("p",null,"Most IF plugins are created and maintained by the community. Anyone can create a plugin and share it so that other iF users can install it and use it in their pipelines."),(0,a.kt)("p",null,"We provide a website where you can search for plugins:"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://explorer.if.greensoftware.foundation"},"IF Explorer")),(0,a.kt)("p",null,"You can also add your own plugins to the Explorer using this ",(0,a.kt)("a",{parentName:"p",href:"https://wiki.greensoftware.foundation/how-to-add-plugins"},"form"),"."),(0,a.kt)("h3",{id:"built-in"},(0,a.kt)("inlineCode",{parentName:"h3"},"built-in")),(0,a.kt)("p",null,"IF builtins all come bundled with IF. Below you will find a list of each builtin along with a brief description of its purpose and a link to its README documentation."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Time Sync"),": Takes a heterogeneous set of time series data that might be offset, discontinuous or irregularly spaces and returns time series conforming to a user defined time grid. E.g. a user can define that all sets of observations should start at some global start time, end at some global end time and have a specific temporal resolution.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Groupby"),": Allows a user to regroup their output data according to given keys.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/sci-embodied"},"SCI-embodied")," - Calculates the embodied carbon for a component.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/sci"},"SCI"),": Calculates the software carbon intensity.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/shell"},"Shell")," - A plugin that enables external code in any language to be run in a child process")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/sum"},"Sum"),": a generic arithmetic plugin that allows you to sum any set of input parameters.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/multiply"},"Multiply"),": a generic arithmetic plugin that allows you to multiply any set of input parameters.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/coefficient"},"Coefficient"),": a generic arithmetic plugin that allows you to multiply any input value by a coefficient.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/mock-observations"},"Mock Observations"),": A plugin for mocking observations (inputs) for testing and demo purposes.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/subtract"},"Subtract"),": a generic plugin for subtracting one value from another")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/divide"},"Divide"),": A generic plugin for doing arithmetic division of two values.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/regex"},"Regex"),": A generic plugin to match part of one string and extract it into another.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/exponent"},"Exponent"),": A generic plugin for raising a value to a power")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/interpolation"},"Interpolation"),": A generic plugin for interpolating between known points.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/copy-param"},"Copy Param"),": A generic plugin for copying a parameter to a new element in the input array, optionally deleting the original. Useful as a way to rename parameters.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/src/if-run/builtins/csv-lookup/README.md"},"CSV lookup"),": A generic plugin for querying data from CSV files.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/src/if-run/builtins/time-converter/README.md"},"Time Converter"),": A gerenric plugin for converting time unit of energy value to another time unit."))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/75e434b4.facb9f0e.js b/assets/js/75e434b4.facb9f0e.js new file mode 100644 index 00000000..561c983d --- /dev/null +++ b/assets/js/75e434b4.facb9f0e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[229],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(n),f=a,d=m["".concat(p,".").concat(f)]||m[f]||c[f]||i;return n?r.createElement(d,o(o({ref:t},u),{},{components:n})):r.createElement(d,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,o[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(7462),a=(n(7294),n(4137));const i={},o="Plugins",l={unversionedId:"reference/plugins",id:"reference/plugins",title:"Plugins",description:"Impact Framework works by executing pipelines of plugins over input data. Those plugins are re-useable units of code that can be thought of as Lego bricks - simple blocks of code that can be assembled into complex workflows.",source:"@site/docs/reference/plugins.md",sourceDirName:"reference",slug:"/reference/plugins",permalink:"/reference/plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/plugins.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"IF features",permalink:"/reference/features"},next:{title:"FAQs",permalink:"/FAQ"}},p={},s=[{value:"built-in",id:"built-in",level:3}],u={toc:s};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"plugins"},"Plugins"),(0,a.kt)("p",null,"Impact Framework works by executing pipelines of plugins over input data. Those plugins are re-useable units of code that can be thought of as Lego bricks - simple blocks of code that can be assembled into complex workflows."),(0,a.kt)("p",null,"IF comes bundled with a standard library of ",(0,a.kt)("inlineCode",{parentName:"p"},"builtins")," that allow you to do basic and/or generic operations over your data. These include simple arithmetic, regrouping data, calculating SCI scores, and running processes in a spawned shell."),(0,a.kt)("p",null,"Most IF plugins are created and maintained by the community. Anyone can create a plugin and share it so that other iF users can install it and use it in their pipelines."),(0,a.kt)("p",null,"We provide a website where you can search for plugins:"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://explorer.if.greensoftware.foundation"},"IF Explorer")),(0,a.kt)("p",null,"You can also add your own plugins to the Explorer using this ",(0,a.kt)("a",{parentName:"p",href:"https://wiki.greensoftware.foundation/how-to-add-plugins"},"form"),"."),(0,a.kt)("h3",{id:"built-in"},(0,a.kt)("inlineCode",{parentName:"h3"},"built-in")),(0,a.kt)("p",null,"IF builtins all come bundled with IF. Below you will find a list of each builtin along with a brief description of its purpose and a link to its README documentation."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins#readme"},"Time Sync"),": Takes a heterogeneous set of time series data that might be offset, discontinuous or irregularly spaces and returns time series conforming to a user defined time grid. E.g. a user can define that all sets of observations should start at some global start time, end at some global end time and have a specific temporal resolution.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/sci-embodied"},"SCI-embodied")," - Calculates the embodied carbon for a component.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/sci"},"SCI"),": Calculates the software carbon intensity.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/shell"},"Shell")," - A plugin that enables external code in any language to be run in a child process")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/sum"},"Sum"),": a generic arithmetic plugin that allows you to sum any set of input parameters.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/multiply"},"Multiply"),": a generic arithmetic plugin that allows you to multiply any set of input parameters.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/coefficient"},"Coefficient"),": a generic arithmetic plugin that allows you to multiply any input value by a coefficient.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/mock-observations"},"Mock Observations"),": A plugin for mocking observations (inputs) for testing and demo purposes.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/subtract"},"Subtract"),": a generic plugin for subtracting one value from another")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/divide"},"Divide"),": A generic plugin for doing arithmetic division of two values.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/regex"},"Regex"),": A generic plugin to match part of one string and extract it into another.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/exponent"},"Exponent"),": A generic plugin for raising a value to a power")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/interpolation"},"Interpolation"),": A generic plugin for interpolating between known points.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/builtins/copy-param"},"Copy Param"),": A generic plugin for copying a parameter to a new element in the input array, optionally deleting the original. Useful as a way to rename parameters.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/src/if-run/builtins/csv-lookup/README.md"},"CSV lookup"),": A generic plugin for querying data from CSV files.")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/src/if-run/builtins/time-converter/README.md"},"Time Converter"),": A gerenric plugin for converting time unit of energy value to another time unit."))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/89d906ef.7d9e8e09.js b/assets/js/89d906ef.7d9e8e09.js deleted file mode 100644 index fd8b420a..00000000 --- a/assets/js/89d906ef.7d9e8e09.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[169],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var c=i.createContext({}),l=function(e){var n=i.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=l(e.components);return i.createElement(c.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=l(t),d=o,f=m["".concat(c,".").concat(d)]||m[d]||p[d]||a;return t?i.createElement(f,r(r({ref:n},u),{},{components:t})):i.createElement(f,r({ref:n},u))}));function d(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,r=new Array(a);r[0]=m;var s={};for(var c in n)hasOwnProperty.call(n,c)&&(s[c]=n[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,r[1]=s;for(var l=2;l{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var i=t(7462),o=(t(7294),t(4137));const a={sidebar_position:6},r="Verifying IF outputs with `if-check`",s={unversionedId:"users/how-to-verify-files-with-if-check",id:"users/how-to-verify-files-with-if-check",title:"Verifying IF outputs with `if-check`",description:"IF includes a command line tool called if-check that can be used to verify the results in a manifest file.",source:"@site/docs/users/how-to-verify-files-with-if-check.md",sourceDirName:"users",slug:"/users/how-to-verify-files-with-if-check",permalink:"/users/how-to-verify-files-with-if-check",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-verify-files-with-if-check.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"How to compare files with `if-diff`",permalink:"/users/how-to-compare-files-with-if-diff"},next:{title:"How to check parameters and units using `explainer`",permalink:"/users/how-to-use-the-explain-feature"}},c={},l=[{value:"Example",id:"example",level:2},{value:"Running IF over multiple manifests",id:"running-if-over-multiple-manifests",level:2},{value:"if-check limitations",id:"if-check-limitations",level:2}],u={toc:l};function p(e){let{components:n,...t}=e;return(0,o.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"verifying-if-outputs-with-if-check"},"Verifying IF outputs with ",(0,o.kt)("inlineCode",{parentName:"h1"},"if-check")),(0,o.kt)("p",null,"IF includes a command line tool called ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," that can be used to verify the results in a manifest file."),(0,o.kt)("p",null,"Imagine that someone provides you with a computed manifest file that they claim demonstrates the environmental impact of their software application."),(0,o.kt)("p",null,"You could trust them and take their results at face value, but there are probably cases where you think someone might have made a mistake or might be massaging their data in a dishonest way. Maybe you just need to demonstrate due diligence in the quality of the data you receive and use. In these cases, you can make use of ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check"),"."),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," is a single command that takes a given manifest file, sets up an environment where it can be executed, executes it, and compares the newly generated results to those in the original file. This means you can independently verify the results in the file using your own local copy of IF."),(0,o.kt)("p",null,"Under the hood, ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," is wrapping calls to ",(0,o.kt)("inlineCode",{parentName:"p"},"if-env")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"if-diff"),", so if you need granular control over the information flow for some reason, you could achieve the same result using separate calls to those commands."),(0,o.kt)("h2",{id:"example"},"Example"),(0,o.kt)("p",null,"Alice is a manifest verifier. She receives a manifest from Bob. Bob is a good actor and has provided a valid file. Alice is a good cypherpunk who verifies everything."),(0,o.kt)("p",null,"Here is Bob's manifest:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"name: bobs-manifest\ndescription:\ntags: null\ninitialize:\n plugins:\n sci:\n path: builtin\n method: Sci\n global-config:\n functional-unit: requests\nexecution:\n command: >-\n /home/bob/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/bob/Code/if/src/index.ts -m manifests/plugins/sci/success.yml -s\n environment:\n if-version: 0.4.0\n os: linux\n os-version: 5.15.0-113-generic\n node-version: 21.4.0\n date-time: 2024-06-27T14:10:29.830Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.9'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n sci: 0.050199999999999995\n child-2:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n sci: 0.0802\n")),(0,o.kt)("p",null,"Alice runs :"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"if-check -m bobs-manifest.yml\n")),(0,o.kt)("p",null,"And receives the response:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"if-check: successfully verified bobs-manifest\n")),(0,o.kt)("p",null,"Charlie also has a copy of Bob's manifest. He wants to trick Alice into thinking his SCI score is lower, so he overwrites the values in the manifest file, making them lower. Charlie's manifest looks like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"# start\nname: charlies-manifest\ndescription:\ntags: null\ninitialize:\n plugins:\n sci:\n path: builtin\n method: Sci\n global-config:\n functional-unit: requests\nexecution:\n command: >-\n /home/charlie/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/charlie/Code/if/src/index.ts -m manifests/plugins/sci/success.yml -s\n environment:\n if-version: 0.4.0\n os: linux\n os-version: 5.15.0-113-generic\n node-version: 21.4.0\n date-time: 2024-06-27T14:10:29.830Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.9'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n sci: 0.020199999999999995\n child-2:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n sci: 0.0102\n")),(0,o.kt)("p",null,"Now, when Alice runs ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check -m charlies-manifest"),", she receives:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"if-check could not verify charlies-manifest. The re-executed file does not match the original.\n\nFiles do not match!\ntree.children.child-1.outputs.0.sci\nsource: 0.050199999999999995\ntarget: 0.020199999999999995\n")),(0,o.kt)("p",null,"Not only can Alice see that the files do not match, she can see which values Charlie manipulated."),(0,o.kt)("h2",{id:"running-if-over-multiple-manifests"},"Running IF over multiple manifests"),(0,o.kt)("p",null,"Alice could also run ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," over any number of manifests in a single command, using the ",(0,o.kt)("inlineCode",{parentName:"p"},"-d")," subcommand. For a folder containing ",(0,o.kt)("inlineCode",{parentName:"p"},"n")," manifests, pass the folder path:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-sh"},"if-check -d /my-folder-of-manifests\n")),(0,o.kt)("p",null,"Each manifest will be run through ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," in sequence."),(0,o.kt)("h2",{id:"if-check-limitations"},(0,o.kt)("inlineCode",{parentName:"h2"},"if-check")," limitations"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," can verify that a manifest is correctly calculated. However, if someone really wanted to use a fraudulent manifest, they could provide fraudulent ",(0,o.kt)("em",{parentName:"p"},"input")," data not ",(0,o.kt)("em",{parentName:"p"},"output")," data. There's little we can really do about this - if someone provides fake input data it is out of IF's remit. This means that although the examples above are good for demonstrating how ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," works, it's more likely to be used to check for bugs and configuration errors than it is to be used to detect fraud."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/89d906ef.e683a8ca.js b/assets/js/89d906ef.e683a8ca.js new file mode 100644 index 00000000..352d9e0d --- /dev/null +++ b/assets/js/89d906ef.e683a8ca.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[169],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>d});var i=t(7294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var c=i.createContext({}),l=function(e){var n=i.useContext(c),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=l(e.components);return i.createElement(c.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=l(t),d=o,f=m["".concat(c,".").concat(d)]||m[d]||p[d]||a;return t?i.createElement(f,r(r({ref:n},u),{},{components:t})):i.createElement(f,r({ref:n},u))}));function d(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,r=new Array(a);r[0]=m;var s={};for(var c in n)hasOwnProperty.call(n,c)&&(s[c]=n[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,r[1]=s;for(var l=2;l{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>l});var i=t(7462),o=(t(7294),t(4137));const a={sidebar_position:6},r="Verifying IF outputs with `if-check`",s={unversionedId:"users/how-to-verify-files-with-if-check",id:"users/how-to-verify-files-with-if-check",title:"Verifying IF outputs with `if-check`",description:"IF includes a command line tool called if-check that can be used to verify the results in a manifest file.",source:"@site/docs/users/how-to-verify-files-with-if-check.md",sourceDirName:"users",slug:"/users/how-to-verify-files-with-if-check",permalink:"/users/how-to-verify-files-with-if-check",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-verify-files-with-if-check.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"How to compare files with `if-diff`",permalink:"/users/how-to-compare-files-with-if-diff"},next:{title:"How to check parameters and units using `explainer`",permalink:"/users/how-to-use-the-explain-feature"}},c={},l=[{value:"Example",id:"example",level:2},{value:"Running IF over multiple manifests",id:"running-if-over-multiple-manifests",level:2},{value:"if-check limitations",id:"if-check-limitations",level:2}],u={toc:l};function p(e){let{components:n,...t}=e;return(0,o.kt)("wrapper",(0,i.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"verifying-if-outputs-with-if-check"},"Verifying IF outputs with ",(0,o.kt)("inlineCode",{parentName:"h1"},"if-check")),(0,o.kt)("p",null,"IF includes a command line tool called ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," that can be used to verify the results in a manifest file."),(0,o.kt)("p",null,"Imagine that someone provides you with a computed manifest file that they claim demonstrates the environmental impact of their software application."),(0,o.kt)("p",null,"You could trust them and take their results at face value, but there are probably cases where you think someone might have made a mistake or might be massaging their data in a dishonest way. Maybe you just need to demonstrate due diligence in the quality of the data you receive and use. In these cases, you can make use of ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check"),"."),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," is a single command that takes a given manifest file, sets up an environment where it can be executed, executes it, and compares the newly generated results to those in the original file. This means you can independently verify the results in the file using your own local copy of IF."),(0,o.kt)("p",null,"Under the hood, ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," is wrapping calls to ",(0,o.kt)("inlineCode",{parentName:"p"},"if-env")," and ",(0,o.kt)("inlineCode",{parentName:"p"},"if-diff"),", so if you need granular control over the information flow for some reason, you could achieve the same result using separate calls to those commands."),(0,o.kt)("h2",{id:"example"},"Example"),(0,o.kt)("p",null,"Alice is a manifest verifier. She receives a manifest from Bob. Bob is a good actor and has provided a valid file. Alice is a good cypherpunk who verifies everything."),(0,o.kt)("p",null,"Here is Bob's manifest:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"name: bobs-manifest\ndescription:\ntags: null\ninitialize:\n plugins:\n sci:\n path: builtin\n method: Sci\n config:\n functional-unit: requests\nexecution:\n command: >-\n /home/bob/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/bob/Code/if/src/index.ts -m manifests/plugins/sci/success.yml -s\n environment:\n if-version: 0.4.0\n os: linux\n os-version: 5.15.0-113-generic\n node-version: 21.4.0\n date-time: 2024-06-27T14:10:29.830Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.9'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n sci: 0.050199999999999995\n child-2:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n sci: 0.0802\n")),(0,o.kt)("p",null,"Alice runs :"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"if-check -m bobs-manifest.yml\n")),(0,o.kt)("p",null,"And receives the response:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"if-check: successfully verified bobs-manifest\n")),(0,o.kt)("p",null,"Charlie also has a copy of Bob's manifest. He wants to trick Alice into thinking his SCI score is lower, so he overwrites the values in the manifest file, making them lower. Charlie's manifest looks like this:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"# start\nname: charlies-manifest\ndescription:\ntags: null\ninitialize:\n plugins:\n sci:\n path: builtin\n method: Sci\n config:\n functional-unit: requests\nexecution:\n command: >-\n /home/charlie/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/charlie/Code/if/src/index.ts -m manifests/plugins/sci/success.yml -s\n environment:\n if-version: 0.4.0\n os: linux\n os-version: 5.15.0-113-generic\n node-version: 21.4.0\n date-time: 2024-06-27T14:10:29.830Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.9'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child-1:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n carbon-embodied: 0.02\n carbon: 5.02\n requests: 100\n sci: 0.020199999999999995\n child-2:\n pipeline:\n observe:\n regroup:\n compute:\n - sci\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n outputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 8\n carbon-operational: 8\n carbon-embodied: 0.02\n carbon: 8.02\n requests: 100\n sci: 0.0102\n")),(0,o.kt)("p",null,"Now, when Alice runs ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check -m charlies-manifest"),", she receives:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yaml"},"if-check could not verify charlies-manifest. The re-executed file does not match the original.\n\nFiles do not match!\ntree.children.child-1.outputs.0.sci\nsource: 0.050199999999999995\ntarget: 0.020199999999999995\n")),(0,o.kt)("p",null,"Not only can Alice see that the files do not match, she can see which values Charlie manipulated."),(0,o.kt)("h2",{id:"running-if-over-multiple-manifests"},"Running IF over multiple manifests"),(0,o.kt)("p",null,"Alice could also run ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," over any number of manifests in a single command, using the ",(0,o.kt)("inlineCode",{parentName:"p"},"-d")," subcommand. For a folder containing ",(0,o.kt)("inlineCode",{parentName:"p"},"n")," manifests, pass the folder path:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-sh"},"if-check -d /my-folder-of-manifests\n")),(0,o.kt)("p",null,"Each manifest will be run through ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," in sequence."),(0,o.kt)("h2",{id:"if-check-limitations"},(0,o.kt)("inlineCode",{parentName:"h2"},"if-check")," limitations"),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," can verify that a manifest is correctly calculated. However, if someone really wanted to use a fraudulent manifest, they could provide fraudulent ",(0,o.kt)("em",{parentName:"p"},"input")," data not ",(0,o.kt)("em",{parentName:"p"},"output")," data. There's little we can really do about this - if someone provides fake input data it is out of IF's remit. This means that although the examples above are good for demonstrating how ",(0,o.kt)("inlineCode",{parentName:"p"},"if-check")," works, it's more likely to be used to check for bugs and configuration errors than it is to be used to detect fraud."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8c7895a0.a4e59a8f.js b/assets/js/8c7895a0.a4e59a8f.js new file mode 100644 index 00000000..9d7b1ed4 --- /dev/null +++ b/assets/js/8c7895a0.a4e59a8f.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[954],{4137:(e,t,a)=>{a.d(t,{Zo:()=>p,kt:()=>h});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var d=n.createContext({}),s=function(e){var t=n.useContext(d),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},p=function(e){var t=s(e.components);return n.createElement(d.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,i=e.originalType,d=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),m=s(a),h=r,g=m["".concat(d,".").concat(h)]||m[h]||u[h]||i;return a?n.createElement(g,l(l({ref:t},p),{},{components:a})):n.createElement(g,l({ref:t},p))}));function h(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=a.length,l=new Array(i);l[0]=m;var o={};for(var d in t)hasOwnProperty.call(t,d)&&(o[d]=t[d]);o.originalType=e,o.mdxType="string"==typeof e?e:r,l[1]=o;for(var s=2;s{a.r(t),a.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>s});var n=a(7462),r=(a(7294),a(4137));const i={},l="Errors",o={unversionedId:"reference/errors",id:"reference/errors",title:"Errors",description:"IF defines a finite set of error classes. All error messages emitted by IF are attached to one of these classes.",source:"@site/docs/reference/errors.md",sourceDirName:"reference",slug:"/reference/errors",permalink:"/reference/errors",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/errors.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Command line tool",permalink:"/reference/cli"},next:{title:"IF features",permalink:"/reference/features"}},d={},s=[{value:"IF errors",id:"if-errors",level:2},{value:"ParseCliParamsError",id:"parsecliparamserror",level:3},{value:"Messages",id:"messages",level:4},{value:"ManifestValidationError",id:"manifestvalidationerror",level:3},{value:"InvalidGroupingError",id:"invalidgroupingerror",level:3},{value:"WriteFileError",id:"writefileerror",level:3},{value:"Messages",id:"messages-1",level:4},{value:"CliSourceFileError",id:"clisourcefileerror",level:3},{value:"Messages",id:"messages-2",level:4},{value:"CliTargetFileError",id:"clitargetfileerror",level:3},{value:"PluginInitializationError",id:"plugininitializationerror",level:3},{value:"InvalidAggregationMethodError",id:"invalidaggregationmethoderror",level:3},{value:"MissingAggregationParamError",id:"missingaggregationparamerror",level:3},{value:"Messages",id:"messages-3",level:4},{value:"MissingPluginMethodError",id:"missingpluginmethoderror",level:2},{value:"MissingPluginPathError",id:"missingpluginpatherror",level:2},{value:"InvalidExhaustPluginError",id:"invalidexhaustpluginerror",level:2},{value:"Plugin Errors",id:"plugin-errors",level:2},{value:"ConfigError",id:"configerror",level:3},{value:"MissingInputDataError",id:"missinginputdataerror",level:3},{value:"ProcessExecutionError",id:"processexecutionerror",level:3},{value:"RegexMismatchError",id:"regexmismatcherror",level:3},{value:"FetchingFileError",id:"fetchingfileerror",level:3},{value:"ReadFileError",id:"readfileerror",level:3},{value:"MissingCSVColumnError",id:"missingcsvcolumnerror",level:3},{value:"QueryDataNotFoundError",id:"querydatanotfounderror",level:3},{value:"InvalidDateInInputError",id:"invaliddateininputerror",level:3},{value:"InvalidPaddingError",id:"invalidpaddingerror",level:3},{value:"InvalidInputError",id:"invalidinputerror",level:3},{value:"ExhaustOutputArgError",id:"exhaustoutputargerror",level:2},{value:"CSVParseError",id:"csvparseerror",level:2},{value:"Capturing errors in manifests",id:"capturing-errors-in-manifests",level:2}],p={toc:s};function u(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,n.Z)({},p,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"errors"},"Errors"),(0,r.kt)("p",null,"IF defines a finite set of error classes. All error messages emitted by IF are attached to one of these classes."),(0,r.kt)("p",null,"This page enumerates the error classes. For each error class, we list the messages associated with them along with the likely causes and possible remedies. This should help you to debug issues you are having with IF."),(0,r.kt)("h2",{id:"if-errors"},"IF errors"),(0,r.kt)("h3",{id:"parsecliparamserror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ParseCliParamsError")),(0,r.kt)("p",null,"Errors of this class are caused by invalid input arguments being passed to the ",(0,r.kt)("a",{parentName:"p",href:"/reference/cli"},"CLI"),"."),(0,r.kt)("h4",{id:"messages"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Unknown option: -")),(0,r.kt)("td",{parentName:"tr",align:null},"Your cli command is not supported by the framework"),(0,r.kt)("td",{parentName:"tr",align:null},"add supported ",(0,r.kt)("inlineCode",{parentName:"td"},"-")," flag to yor run command")))),(0,r.kt)("h3",{id:"manifestvalidationerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ManifestValidationError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ManifestValidationError")," class arise due to a problem in the manifest (yaml) file. Validation of the manifest is done using the ",(0,r.kt)("a",{parentName:"p",href:"https://zod.dev/"},"Zod")," library."),(0,r.kt)("p",null,"The error message will be the error surfaced by Zod, and will include the name of the manifest element that is invalid. Since the set of error messages is very large and all conform to a simple schema, we do not list them exhaustively here, but instead demonstrate using the following example:"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},'ManifestValidationError: "initialize" parameter is required. Error code: invalid_type.')),(0,r.kt)("p",null,"You can infer from the error code that the issue is related to an invalid or missing value encountered during the manifest validation.\nThe message itself indicates that the problematic element is ",(0,r.kt)("inlineCode",{parentName:"p"},"initialize")," and the problem is that it is missing."),(0,r.kt)("p",null,"The remedy for this issue is to add an ",(0,r.kt)("inlineCode",{parentName:"p"},"initialize")," block into the manifest."),(0,r.kt)("h3",{id:"invalidgroupingerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidGroupingError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidGroupingError")," are only emitted by the ",(0,r.kt)("inlineCode",{parentName:"p"},"regroup")," feature. There is only one associated message; it is emitted when the requested groups do not exist in the tree."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Invalid group ${type}.")),(0,r.kt)("td",{parentName:"tr",align:null},"you are requested the feature to regroup the tree based on fields that do not exist"),(0,r.kt)("td",{parentName:"tr",align:null},"Check the spelling of the values passed to ",(0,r.kt)("inlineCode",{parentName:"td"},"regroup")," and ensure the values exist in the tree")))),(0,r.kt)("h3",{id:"writefileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"WriteFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"WriteFileError")," class are caused by problems writing output data to files. Typically, this can occur when the user does not have sufficient permissions to write to a given file."),(0,r.kt)("h4",{id:"messages-1"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Failed to write CSV to ${outputPath}: ${error}")),(0,r.kt)("td",{parentName:"tr",align:null},"There was a problem writing data to file"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided a valid output path and that you have valid permissions to write to that location")))),(0,r.kt)("h3",{id:"clisourcefileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"CliSourceFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"CliSourceFileError")," class are caused by problems with source manifest."),(0,r.kt)("h4",{id:"messages-2"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Manifest is missing.")),(0,r.kt)("td",{parentName:"tr",align:null},"Source manifest is not provided"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided a path to source manifest")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Given source file is not in yaml format.")),(0,r.kt)("td",{parentName:"tr",align:null},"Source file is provided, but format is not a yaml format"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided valid yaml manifest")))),(0,r.kt)("h3",{id:"clitargetfileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"CliTargetFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"CliTargetFileError")," class are caused by problems with target manifest."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Given target file is not in yaml format.")),(0,r.kt)("td",{parentName:"tr",align:null},"Target file is provided, but format is not a yaml format"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided valid yaml manifest")))),(0,r.kt)("h3",{id:"plugininitializationerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"PluginInitializationError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PluginInitializationError")," arise when a plugin is invoked in a pipeline without having been initialized in the ",(0,r.kt)("inlineCode",{parentName:"p"},"initialize")," block of the manifest being executed."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Not initalized plugin: ${name}. Check if ${name} is in 'manifest.initalize.plugins'.")),(0,r.kt)("td",{parentName:"tr",align:null},"a plugin invoked in a pipeline is not initialized"),(0,r.kt)("td",{parentName:"tr",align:null},"ensure all plugins that exist in pipelines across your manifest have been included in the manifest's ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Provided module ${path} is invalid or not found. ${error ?? ''}")),(0,r.kt)("td",{parentName:"tr",align:null},"a plugin invoked in a pipeline is not initialized"),(0,r.kt)("td",{parentName:"tr",align:null},"ensure all plugins that exist in pipelines across your manifest have been included in the manifest's ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block")))),(0,r.kt)("h3",{id:"invalidaggregationmethoderror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidAggregationMethodError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidAggregationMethodError")," class are caused by problems in the configuration of the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregation")," feature."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Aggregation is not possible for given ${metric} since method is 'none'.")),(0,r.kt)("td",{parentName:"tr",align:null},"You are trying to aggregate a metric whose method is set to ",(0,r.kt)("inlineCode",{parentName:"td"},"none")),(0,r.kt)("td",{parentName:"tr",align:null},"Update the aggregation method, or choose a different metric to aggregate.")))),(0,r.kt)("h3",{id:"missingaggregationparamerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"MissingAggregationParamError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingAggregationParamError")," class are caused by problems in the configuration of the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregation")," feature. Typically, the aggregation method may be undefined or you have tried to aggregate a metric that IF cannot find in the input data."),(0,r.kt)("h4",{id:"messages-3"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Aggregation metric ${metric} is not found in inputs[${index}].")),(0,r.kt)("td",{parentName:"tr",align:null},"You are trying to aggegate a metric that doesn't exist in the input data"),(0,r.kt)("td",{parentName:"tr",align:null},"Check that your chosen metric is spelled correctly and that it exists in the input data by the time the ",(0,r.kt)("inlineCode",{parentName:"td"},"aggregate")," feature executes.")))),(0,r.kt)("h2",{id:"missingpluginmethoderror"},(0,r.kt)("inlineCode",{parentName:"h2"},"MissingPluginMethodError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingPluginMethodError")," class are caused by missing information in manifest's ",(0,r.kt)("inlineCode",{parentName:"p"},"initalize.plugins")," section."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Initalization param 'method' is missing.")),(0,r.kt)("td",{parentName:"tr",align:null},"The required ",(0,r.kt)("inlineCode",{parentName:"td"},"method")," field is missing from the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for a given plugin."),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure the ",(0,r.kt)("inlineCode",{parentName:"td"},"method")," field is added to the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for each plugin. The value should be the name of the function exported by the plugin.")))),(0,r.kt)("h2",{id:"missingpluginpatherror"},(0,r.kt)("inlineCode",{parentName:"h2"},"MissingPluginPathError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingPluginPathError")," class are caused by missing information in manifest's ",(0,r.kt)("inlineCode",{parentName:"p"},"initalize.plugins")," section."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Initalization param 'path' is missing.")),(0,r.kt)("td",{parentName:"tr",align:null},"The required ",(0,r.kt)("inlineCode",{parentName:"td"},"path")," field is missing from the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for a given plugin"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure the ",(0,r.kt)("inlineCode",{parentName:"td"},"path")," field is added to the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for each plugin. The value should be the path to the directory in ",(0,r.kt)("inlineCode",{parentName:"td"},"if/node_modules")," for your plugin.")))),(0,r.kt)("h2",{id:"invalidexhaustpluginerror"},(0,r.kt)("inlineCode",{parentName:"h2"},"InvalidExhaustPluginError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidExhaustPluginError")," class are caused by using unsupported exhaust plugin."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Invalid exhaust plugin: ${pluginName}.")),(0,r.kt)("td",{parentName:"tr",align:null},"Unsupported or misspelled plugin was used as output method"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure the ",(0,r.kt)("inlineCode",{parentName:"td"},"pluginName")," corresponds to supported plugins.")))),(0,r.kt)("h2",{id:"plugin-errors"},"Plugin Errors"),(0,r.kt)("p",null,"Plugins can emit their own custom error messages, but we still prefer those messages to be attached to one of a finite set of predefined error classes.\nThose classes are listed in this section."),(0,r.kt)("h3",{id:"configerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ConfigError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigError")," are used when part of the config data provided to a plugin is invalid or missing."),(0,r.kt)("p",null,"For example the ",(0,r.kt)("inlineCode",{parentName:"p"},"Divide")," plugin throws a ",(0,r.kt)("inlineCode",{parentName:"p"},"ConfigError")," when it receives a denominator equal to zero."),(0,r.kt)("p",null,"The message should name the config element that was invalid and describe the reason why. For example:"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},'ConfigError: "denominator" parameter is number must be greater than 0. Error code: too_small.')),(0,r.kt)("h3",{id:"missinginputdataerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"MissingInputDataError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingInputDataError")," class arise because your plugin is not receiving the data it expects in ",(0,r.kt)("inlineCode",{parentName:"p"},"input")," data or config.\nThe specific messages depend on the plugin. It is expected that the messages emitted by each plugin are listed in their own documentation."),(0,r.kt)("p",null,"The example below is a message emitted by the ",(0,r.kt)("inlineCode",{parentName:"p"},"interpolation")," plugin when the ",(0,r.kt)("inlineCode",{parentName:"p"},"method")," given in config is ",(0,r.kt)("em",{parentName:"p"},"not")," one of the expected enum variants:"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"MissingInputDataError: \"interpolation\" parameter is invalid enum value. expected 'spline' | 'linear', received 'dummy'. Error code: invalid_enum_value.")),(0,r.kt)("h3",{id:"processexecutionerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ProcessExecutionError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ProcessExecutionError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"shell")," plugin have faced problems while executing the script you have provided."),(0,r.kt)("h3",{id:"regexmismatcherror"},(0,r.kt)("inlineCode",{parentName:"h3"},"RegexMismatchError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"RegexMismatchError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"regex")," plugin have faced problems while parsing given string with specified ",(0,r.kt)("inlineCode",{parentName:"p"},"regex"),"."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"${input} does not match the ${match} regex expression")),(0,r.kt)("td",{parentName:"tr",align:null},"Given string doesn't contain anything matching given ",(0,r.kt)("inlineCode",{parentName:"td"},"regex")),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that input contains string which can be matched by your ",(0,r.kt)("inlineCode",{parentName:"td"},"regex"),".")))),(0,r.kt)("h3",{id:"fetchingfileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"FetchingFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"FetchingFileError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin have faced problems fetching given ",(0,r.kt)("inlineCode",{parentName:"p"},"url"),"."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Failed fetching the file: ${filepath}.")),(0,r.kt)("td",{parentName:"tr",align:null},"Fetching the file with given URL failed"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that file's url is accessible")))),(0,r.kt)("h3",{id:"readfileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ReadFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ReadFileError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin have faced problems reading given file ",(0,r.kt)("inlineCode",{parentName:"p"},"path"),". The error should include the file path and the system error that was encountered when IF attempted to read data from the file:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Failed reading the file: ${filepath}.")),(0,r.kt)("td",{parentName:"tr",align:null},"Reading the file with given path failed"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that file's path is correct")))),(0,r.kt)("h3",{id:"missingcsvcolumnerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"MissingCSVColumnError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingCSVColumnError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin can't access given csv file column."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"There is no column with the name: ${columnName}.")),(0,r.kt)("td",{parentName:"tr",align:null},"CSV file doens't contain such column"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that specified ",(0,r.kt)("inlineCode",{parentName:"td"},"query")," is correct and contains existing column name.")))),(0,r.kt)("h3",{id:"querydatanotfounderror"},(0,r.kt)("inlineCode",{parentName:"h3"},"QueryDataNotFoundError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"QueryDataNotFoundError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin can't find ",(0,r.kt)("inlineCode",{parentName:"p"},"query")," related data in given CSV file."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"One or more of the given query parameters are not found in the target CSV file column headers.")),(0,r.kt)("td",{parentName:"tr",align:null},"CSV file doens't contain data with given criteria."),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that specified ",(0,r.kt)("inlineCode",{parentName:"td"},"query")," and ",(0,r.kt)("inlineCode",{parentName:"td"},"input")," values have intersection with CSV file's data.")))),(0,r.kt)("h3",{id:"invaliddateininputerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidDateInInputError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidDateInInputError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," plugin can't parse date from inputs."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Unexpected date datatype: ${typeof date}: ${date}")),(0,r.kt)("td",{parentName:"tr",align:null},"Unsupported type for date."),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that dates in inputs are correct timestamps.")))),(0,r.kt)("h3",{id:"invalidpaddingerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidPaddingError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidPaddingError")," class arise when there is misconfiguration of ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," plugin."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Avoiding padding at ${start or end}")),(0,r.kt)("td",{parentName:"tr",align:null},"Error on padding is enabled and config is missing padding configuration."),(0,r.kt)("td",{parentName:"tr",align:null},"Make sure padding is correctly configured.")))),(0,r.kt)("h3",{id:"invalidinputerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidInputError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidInputError")," class arise when there is input timestamps incompatibility while using ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," plugin."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Observation timestamps overlap, please check inputs.")),(0,r.kt)("td",{parentName:"tr",align:null},"Input timestamps have overlap."),(0,r.kt)("td",{parentName:"tr",align:null},"Make sure that input timestamps are continuous.")))),(0,r.kt)("h2",{id:"exhaustoutputargerror"},(0,r.kt)("inlineCode",{parentName:"h2"},"ExhaustOutputArgError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ExhaustOutputArgError")," class arise when there is output path issues while exporting file or exporting criteria misconfiguration."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Output path is required, please make sure output is configured properly.")),(0,r.kt)("td",{parentName:"tr",align:null},"Missed output path."),(0,r.kt)("td",{parentName:"tr",align:null},"Make sure that output path is present in your cli command.")))),(0,r.kt)("h2",{id:"csvparseerror"},(0,r.kt)("inlineCode",{parentName:"h2"},"CSVParseError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"CSVParseError")," occur due to a problem reading CSV file. Typically, this can occur when provided file is not a CSV."),(0,r.kt)("h2",{id:"capturing-errors-in-manifests"},"Capturing errors in manifests"),(0,r.kt)("p",null,"When you run a ",(0,r.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"manifest"),", IF generates output data and either displays it in the console or saves it to a file. If IF or one of the plugins being executed throws an exception, IF can still return an output file, except instead of adding ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs"),", it captures the error message that caused IF to fail in the manifest's ",(0,r.kt)("inlineCode",{parentName:"p"},"execution")," section. Inside the ",(0,r.kt)("inlineCode",{parentName:"p"},"execution")," section, you will find two fields: ",(0,r.kt)("inlineCode",{parentName:"p"},"status")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"error"),". The ",(0,r.kt)("inlineCode",{parentName:"p"},"status")," field is either ",(0,r.kt)("inlineCode",{parentName:"p"},"success")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"fail"),", and the ",(0,r.kt)("inlineCode",{parentName:"p"},"error")," field contains the error message."),(0,r.kt)("p",null,"For example, the following is an output file generated by running a manifest whose ",(0,r.kt)("inlineCode",{parentName:"p"},"input")," data omitted the required ",(0,r.kt)("inlineCode",{parentName:"p"},"duration")," field:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: input-error-missing-duration\ndescription: >-\n a negative test case that fails due to the required `duration` field being\n omitted from input data\ntags:\ninitialize:\n plugins:\n interpolate:\n method: Interpolation\n path: builtin\n config:\n method: linear\n x:\n - 0\n - 10\n - 50\n - 100\n 'y':\n - 0.12\n - 0.32\n - 0.75\n - 1.02\n input-parameter: cpu/utilization\n output-parameter: cpu-factor\nexecution:\n status: fail\n error: >-\n InputValidationError: \"duration\" parameter is required at index 0. Error\n code: invalid_type.\ntree:\n children:\n child-0:\n defaults:\n cpu/thermal-design-power: 100\n pipeline:\n compute:\n - interpolate\n inputs:\n - timestamp: 2023-07-06T00:00\n cpu/utilization: 20\n")),(0,r.kt)("p",null,"No configuration is necessary - this is the default behaviour for IF if the output is configured to save to yaml and the manifest has an error causing IF to fail."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8c7895a0.d84cdd31.js b/assets/js/8c7895a0.d84cdd31.js deleted file mode 100644 index b0beda36..00000000 --- a/assets/js/8c7895a0.d84cdd31.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[954],{4137:(e,t,a)=>{a.d(t,{Zo:()=>p,kt:()=>g});var n=a(7294);function r(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function i(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function l(e){for(var t=1;t=0||(r[a]=e[a]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var d=n.createContext({}),s=function(e){var t=n.useContext(d),a=t;return e&&(a="function"==typeof e?e(t):l(l({},t),e)),a},p=function(e){var t=s(e.components);return n.createElement(d.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,r=e.mdxType,i=e.originalType,d=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),m=s(a),g=r,h=m["".concat(d,".").concat(g)]||m[g]||u[g]||i;return a?n.createElement(h,l(l({ref:t},p),{},{components:a})):n.createElement(h,l({ref:t},p))}));function g(e,t){var a=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=a.length,l=new Array(i);l[0]=m;var o={};for(var d in t)hasOwnProperty.call(t,d)&&(o[d]=t[d]);o.originalType=e,o.mdxType="string"==typeof e?e:r,l[1]=o;for(var s=2;s{a.r(t),a.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>s});var n=a(7462),r=(a(7294),a(4137));const i={},l="Errors",o={unversionedId:"reference/errors",id:"reference/errors",title:"Errors",description:"IF defines a finite set of error classes. All error messages emitted by IF are attached to one of these classes.",source:"@site/docs/reference/errors.md",sourceDirName:"reference",slug:"/reference/errors",permalink:"/reference/errors",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/errors.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Command line tool",permalink:"/reference/cli"},next:{title:"IF features",permalink:"/reference/features"}},d={},s=[{value:"IF errors",id:"if-errors",level:2},{value:"ParseCliParamsError",id:"parsecliparamserror",level:3},{value:"Messages",id:"messages",level:4},{value:"ManifestValidationError",id:"manifestvalidationerror",level:3},{value:"InvalidGroupingError",id:"invalidgroupingerror",level:3},{value:"WriteFileError",id:"writefileerror",level:3},{value:"Messages",id:"messages-1",level:4},{value:"CliSourceFileError",id:"clisourcefileerror",level:3},{value:"Messages",id:"messages-2",level:4},{value:"CliTargetFileError",id:"clitargetfileerror",level:3},{value:"PluginInitializationError",id:"plugininitializationerror",level:3},{value:"InvalidAggregationMethodError",id:"invalidaggregationmethoderror",level:3},{value:"MissingAggregationParamError",id:"missingaggregationparamerror",level:3},{value:"Messages",id:"messages-3",level:4},{value:"MissingPluginMethodError",id:"missingpluginmethoderror",level:2},{value:"MissingPluginPathError",id:"missingpluginpatherror",level:2},{value:"InvalidExhaustPluginError",id:"invalidexhaustpluginerror",level:2},{value:"Plugin Errors",id:"plugin-errors",level:2},{value:"GlobalConfigError",id:"globalconfigerror",level:3},{value:"MissingInputDataError",id:"missinginputdataerror",level:3},{value:"ProcessExecutionError",id:"processexecutionerror",level:3},{value:"RegexMismatchError",id:"regexmismatcherror",level:3},{value:"FetchingFileError",id:"fetchingfileerror",level:3},{value:"ReadFileError",id:"readfileerror",level:3},{value:"MissingCSVColumnError",id:"missingcsvcolumnerror",level:3},{value:"QueryDataNotFoundError",id:"querydatanotfounderror",level:3},{value:"InvalidDateInInputError",id:"invaliddateininputerror",level:3},{value:"InvalidPaddingError",id:"invalidpaddingerror",level:3},{value:"InvalidInputError",id:"invalidinputerror",level:3},{value:"ExhaustOutputArgError",id:"exhaustoutputargerror",level:2},{value:"CSVParseError",id:"csvparseerror",level:2},{value:"Capturing errors in manifests",id:"capturing-errors-in-manifests",level:2}],p={toc:s};function u(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,n.Z)({},p,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"errors"},"Errors"),(0,r.kt)("p",null,"IF defines a finite set of error classes. All error messages emitted by IF are attached to one of these classes."),(0,r.kt)("p",null,"This page enumerates the error classes. For each error class, we list the messages associated with them along with the likely causes and possible remedies. This should help you to debug issues you are having with IF."),(0,r.kt)("h2",{id:"if-errors"},"IF errors"),(0,r.kt)("h3",{id:"parsecliparamserror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ParseCliParamsError")),(0,r.kt)("p",null,"Errors of this class are caused by invalid input arguments being passed to the ",(0,r.kt)("a",{parentName:"p",href:"/reference/cli"},"CLI"),"."),(0,r.kt)("h4",{id:"messages"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Unknown option: -")),(0,r.kt)("td",{parentName:"tr",align:null},"Your cli command is not supported by the framework"),(0,r.kt)("td",{parentName:"tr",align:null},"add supported ",(0,r.kt)("inlineCode",{parentName:"td"},"-")," flag to yor run command")))),(0,r.kt)("h3",{id:"manifestvalidationerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ManifestValidationError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ManifestValidationError")," class arise due to a problem in the manifest (yaml) file. Validation of the manifest is done using the ",(0,r.kt)("a",{parentName:"p",href:"https://zod.dev/"},"Zod")," library."),(0,r.kt)("p",null,"The error message will be the error surfaced by Zod, and will include the name of the manifest element that is invalid. Since the set of error messages is very large and all conform to a simple schema, we do not list them exhaustively here, but instead demonstrate using the following example:"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},'ManifestValidationError: "initialize" parameter is required. Error code: invalid_type.')),(0,r.kt)("p",null,"You can infer from the error code that the issue is related to an invalid or missing value encountered during the manifest validation.\nThe message itself indicates that the problematic element is ",(0,r.kt)("inlineCode",{parentName:"p"},"initialize")," and the problem is that it is missing."),(0,r.kt)("p",null,"The remedy for this issue is to add an ",(0,r.kt)("inlineCode",{parentName:"p"},"initialize")," block into the manifest."),(0,r.kt)("h3",{id:"invalidgroupingerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidGroupingError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidGroupingError")," are only emitted by the ",(0,r.kt)("inlineCode",{parentName:"p"},"group-by")," plugin. There is only one associated message; it is emitted when the requested groups do not exist in the tree."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Invalid group ${type}.")),(0,r.kt)("td",{parentName:"tr",align:null},"you are requested groupby to regroup the tree based on fields that do not exist"),(0,r.kt)("td",{parentName:"tr",align:null},"Check the spelling of the values passed to ",(0,r.kt)("inlineCode",{parentName:"td"},"groupby")," and ensure the values exist in the tree")))),(0,r.kt)("h3",{id:"writefileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"WriteFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"WriteFileError")," class are caused by problems writing output data to files. Typically, this can occur when the user does not have sufficient permissions to write to a given file."),(0,r.kt)("h4",{id:"messages-1"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Failed to write CSV to ${outputPath}: ${error}")),(0,r.kt)("td",{parentName:"tr",align:null},"There was a problem writing data to file"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided a valid output path and that you have valid permissions to write to that location")))),(0,r.kt)("h3",{id:"clisourcefileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"CliSourceFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"CliSourceFileError")," class are caused by problems with source manifest."),(0,r.kt)("h4",{id:"messages-2"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Manifest is missing.")),(0,r.kt)("td",{parentName:"tr",align:null},"Source manifest is not provided"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided a path to source manifest")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Given source file is not in yaml format.")),(0,r.kt)("td",{parentName:"tr",align:null},"Source file is provided, but format is not a yaml format"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided valid yaml manifest")))),(0,r.kt)("h3",{id:"clitargetfileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"CliTargetFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"CliTargetFileError")," class are caused by problems with target manifest."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Given target file is not in yaml format.")),(0,r.kt)("td",{parentName:"tr",align:null},"Target file is provided, but format is not a yaml format"),(0,r.kt)("td",{parentName:"tr",align:null},"check that you have provided valid yaml manifest")))),(0,r.kt)("h3",{id:"plugininitializationerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"PluginInitializationError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"PluginInitializationError")," arise when a plugin is invoked in a pipeline without having been initialized in the ",(0,r.kt)("inlineCode",{parentName:"p"},"initialize")," block of the manifest being executed."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Not initalized plugin: ${name}. Check if ${name} is in 'manifest.initalize.plugins'.")),(0,r.kt)("td",{parentName:"tr",align:null},"a plugin invoked in a pipeline is not initialized"),(0,r.kt)("td",{parentName:"tr",align:null},"ensure all plugins that exist in pipelines across your manifest have been included in the manifest's ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block")),(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Provided module ${path} is invalid or not found. ${error ?? ''}")),(0,r.kt)("td",{parentName:"tr",align:null},"a plugin invoked in a pipeline is not initialized"),(0,r.kt)("td",{parentName:"tr",align:null},"ensure all plugins that exist in pipelines across your manifest have been included in the manifest's ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block")))),(0,r.kt)("h3",{id:"invalidaggregationmethoderror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidAggregationMethodError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidAggregationMethodError")," class are caused by problems in the configuration of the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregation")," feature."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Aggregation is not possible for given ${metric} since method is 'none'.")),(0,r.kt)("td",{parentName:"tr",align:null},"You are trying to aggregate a metric whose method is set to ",(0,r.kt)("inlineCode",{parentName:"td"},"none")),(0,r.kt)("td",{parentName:"tr",align:null},"Update the aggregation method, or choose a different metric to aggregate.")))),(0,r.kt)("h3",{id:"missingaggregationparamerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"MissingAggregationParamError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingAggregationParamError")," class are caused by problems in the configuration of the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregation")," feature. Typically, the aggregation method may be undefined or you have tried to aggregate a metric that IF cannot find in the input data."),(0,r.kt)("h4",{id:"messages-3"},"Messages"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Aggregation metric ${metric} is not found in inputs[${index}].")),(0,r.kt)("td",{parentName:"tr",align:null},"You are trying to aggegate a metric that doesn't exist in the input data"),(0,r.kt)("td",{parentName:"tr",align:null},"Check that your chosen metric is spelled correctly and that it exists in the input data by the time the ",(0,r.kt)("inlineCode",{parentName:"td"},"aggregate")," feature executes.")))),(0,r.kt)("h2",{id:"missingpluginmethoderror"},(0,r.kt)("inlineCode",{parentName:"h2"},"MissingPluginMethodError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingPluginMethodError")," class are caused by missing information in manifest's ",(0,r.kt)("inlineCode",{parentName:"p"},"initalize.plugins")," section."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Initalization param 'method' is missing.")),(0,r.kt)("td",{parentName:"tr",align:null},"The required ",(0,r.kt)("inlineCode",{parentName:"td"},"method")," field is missing from the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for a given plugin."),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure the ",(0,r.kt)("inlineCode",{parentName:"td"},"method")," field is added to the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for each plugin. The value should be the name of the function exported by the plugin.")))),(0,r.kt)("h2",{id:"missingpluginpatherror"},(0,r.kt)("inlineCode",{parentName:"h2"},"MissingPluginPathError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingPluginPathError")," class are caused by missing information in manifest's ",(0,r.kt)("inlineCode",{parentName:"p"},"initalize.plugins")," section."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Initalization param 'path' is missing.")),(0,r.kt)("td",{parentName:"tr",align:null},"The required ",(0,r.kt)("inlineCode",{parentName:"td"},"path")," field is missing from the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for a given plugin"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure the ",(0,r.kt)("inlineCode",{parentName:"td"},"path")," field is added to the ",(0,r.kt)("inlineCode",{parentName:"td"},"initialize")," block for each plugin. The value should be the path to the directory in ",(0,r.kt)("inlineCode",{parentName:"td"},"if/node_modules")," for your plugin.")))),(0,r.kt)("h2",{id:"invalidexhaustpluginerror"},(0,r.kt)("inlineCode",{parentName:"h2"},"InvalidExhaustPluginError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidExhaustPluginError")," class are caused by using unsupported exhaust plugin."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Invalid exhaust plugin: ${pluginName}.")),(0,r.kt)("td",{parentName:"tr",align:null},"Unsupported or misspelled plugin was used as output method"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure the ",(0,r.kt)("inlineCode",{parentName:"td"},"pluginName")," corresponds to supported plugins.")))),(0,r.kt)("h2",{id:"plugin-errors"},"Plugin Errors"),(0,r.kt)("p",null,"Plugins can emit their own custom error messages, but we still prefer those messages to be attached to one of a finite set of predefined error classes.\nThose classes are listed in this section."),(0,r.kt)("h3",{id:"globalconfigerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"GlobalConfigError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"GlobalConfigError")," are used when part of the config data provided to a plugin is invalid or missing."),(0,r.kt)("p",null,"For example the ",(0,r.kt)("inlineCode",{parentName:"p"},"Divide")," plugin throws a ",(0,r.kt)("inlineCode",{parentName:"p"},"GlobalConfigError")," when it receives a denominator equal to zero."),(0,r.kt)("p",null,"The message should name the config element that was invalid and describe the reason why. For example:"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},'GlobalConfigError: "denominator" parameter is number must be greater than 0. Error code: too_small.')),(0,r.kt)("h3",{id:"missinginputdataerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"MissingInputDataError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingInputDataError")," class arise because your plugin is not receiving the data it expects in ",(0,r.kt)("inlineCode",{parentName:"p"},"input")," data, global config or node-level config.\nThe specific messages depend on the plugin. It is expected that the messages emitted by each plugin are listed in their own documentation."),(0,r.kt)("p",null,"The example below is a message emitted by the ",(0,r.kt)("inlineCode",{parentName:"p"},"interpolation")," plugin when the ",(0,r.kt)("inlineCode",{parentName:"p"},"method")," given in global config is ",(0,r.kt)("em",{parentName:"p"},"not")," one of the expected enum variants:"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"MissingInputDataError: \"interpolation\" parameter is invalid enum value. expected 'spline' | 'linear', received 'dummy'. Error code: invalid_enum_value.")),(0,r.kt)("h3",{id:"processexecutionerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ProcessExecutionError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ProcessExecutionError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"shell")," plugin have faced problems while executing the script you have provided."),(0,r.kt)("h3",{id:"regexmismatcherror"},(0,r.kt)("inlineCode",{parentName:"h3"},"RegexMismatchError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"RegexMismatchError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"regex")," plugin have faced problems while parsing given string with specified ",(0,r.kt)("inlineCode",{parentName:"p"},"regex"),"."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"${input} does not match the ${match} regex expression")),(0,r.kt)("td",{parentName:"tr",align:null},"Given string doesn't contain anything matching given ",(0,r.kt)("inlineCode",{parentName:"td"},"regex")),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that input contains string which can be matched by your ",(0,r.kt)("inlineCode",{parentName:"td"},"regex"),".")))),(0,r.kt)("h3",{id:"fetchingfileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"FetchingFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"FetchingFileError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin have faced problems fetching given ",(0,r.kt)("inlineCode",{parentName:"p"},"url"),"."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Failed fetching the file: ${filepath}.")),(0,r.kt)("td",{parentName:"tr",align:null},"Fetching the file with given URL failed"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that file's url is accessible")))),(0,r.kt)("h3",{id:"readfileerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"ReadFileError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ReadFileError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin have faced problems reading given file ",(0,r.kt)("inlineCode",{parentName:"p"},"path"),". The error should include the file path and the system error that was encountered when IF attempted to read data from the file:"),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Failed reading the file: ${filepath}.")),(0,r.kt)("td",{parentName:"tr",align:null},"Reading the file with given path failed"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that file's path is correct")))),(0,r.kt)("h3",{id:"missingcsvcolumnerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"MissingCSVColumnError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"MissingCSVColumnError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin can't access given csv file column."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"There is no column with the name: ${columnName}.")),(0,r.kt)("td",{parentName:"tr",align:null},"CSV file doens't contain such column"),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that specified ",(0,r.kt)("inlineCode",{parentName:"td"},"query")," is correct and contains existing column name.")))),(0,r.kt)("h3",{id:"querydatanotfounderror"},(0,r.kt)("inlineCode",{parentName:"h3"},"QueryDataNotFoundError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"QueryDataNotFoundError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"csv-lookup")," plugin can't find ",(0,r.kt)("inlineCode",{parentName:"p"},"query")," related data in given CSV file."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"One or more of the given query parameters are not found in the target CSV file column headers.")),(0,r.kt)("td",{parentName:"tr",align:null},"CSV file doens't contain data with given criteria."),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that specified ",(0,r.kt)("inlineCode",{parentName:"td"},"query")," and ",(0,r.kt)("inlineCode",{parentName:"td"},"input")," values have intersection with CSV file's data.")))),(0,r.kt)("h3",{id:"invaliddateininputerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidDateInInputError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidDateInInputError")," class arise because ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," plugin can't parse date from inputs."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Unexpected date datatype: ${typeof date}: ${date}")),(0,r.kt)("td",{parentName:"tr",align:null},"Unsupported type for date."),(0,r.kt)("td",{parentName:"tr",align:null},"Ensure that dates in inputs are correct timestamps.")))),(0,r.kt)("h3",{id:"invalidpaddingerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidPaddingError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidPaddingError")," class arise when there is misconfiguration of ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," plugin."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Avoiding padding at ${start or end}")),(0,r.kt)("td",{parentName:"tr",align:null},"Error on padding is enabled and config is missing padding configuration."),(0,r.kt)("td",{parentName:"tr",align:null},"Make sure padding is correctly configured.")))),(0,r.kt)("h3",{id:"invalidinputerror"},(0,r.kt)("inlineCode",{parentName:"h3"},"InvalidInputError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"InvalidInputError")," class arise when there is input timestamps incompatibility while using ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," plugin."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Observation timestamps overlap, please check inputs.")),(0,r.kt)("td",{parentName:"tr",align:null},"Input timestamps have overlap."),(0,r.kt)("td",{parentName:"tr",align:null},"Make sure that input timestamps are continuous.")))),(0,r.kt)("h2",{id:"exhaustoutputargerror"},(0,r.kt)("inlineCode",{parentName:"h2"},"ExhaustOutputArgError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"ExhaustOutputArgError")," class arise when there is output path issues while exporting file or exporting criteria misconfiguration."),(0,r.kt)("table",null,(0,r.kt)("thead",{parentName:"table"},(0,r.kt)("tr",{parentName:"thead"},(0,r.kt)("th",{parentName:"tr",align:null},"message"),(0,r.kt)("th",{parentName:"tr",align:null},"cause"),(0,r.kt)("th",{parentName:"tr",align:null},"remedy"))),(0,r.kt)("tbody",{parentName:"table"},(0,r.kt)("tr",{parentName:"tbody"},(0,r.kt)("td",{parentName:"tr",align:null},(0,r.kt)("inlineCode",{parentName:"td"},"Output path is required, please make sure output is configured properly.")),(0,r.kt)("td",{parentName:"tr",align:null},"Missed output path."),(0,r.kt)("td",{parentName:"tr",align:null},"Make sure that output path is present in your cli command.")))),(0,r.kt)("h2",{id:"csvparseerror"},(0,r.kt)("inlineCode",{parentName:"h2"},"CSVParseError")),(0,r.kt)("p",null,"Errors of the ",(0,r.kt)("inlineCode",{parentName:"p"},"CSVParseError")," occur due to a problem reading CSV file. Typically, this can occur when provided file is not a CSV."),(0,r.kt)("h2",{id:"capturing-errors-in-manifests"},"Capturing errors in manifests"),(0,r.kt)("p",null,"When you run a ",(0,r.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"manifest"),", IF generates output data and either displays it in the console or saves it to a file. If IF or one of the plugins being executed throws an exception, IF can still return an output file, except instead of adding ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs"),", it captures the error message that caused IF to fail in the manifest's ",(0,r.kt)("inlineCode",{parentName:"p"},"execution")," section. Inside the ",(0,r.kt)("inlineCode",{parentName:"p"},"execution")," section, you will find two fields: ",(0,r.kt)("inlineCode",{parentName:"p"},"status")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"error"),". The ",(0,r.kt)("inlineCode",{parentName:"p"},"status")," field is either ",(0,r.kt)("inlineCode",{parentName:"p"},"success")," or ",(0,r.kt)("inlineCode",{parentName:"p"},"fail"),", and the ",(0,r.kt)("inlineCode",{parentName:"p"},"error")," field contains the error message."),(0,r.kt)("p",null,"For example, the following is an output file generated by running a manifest whose ",(0,r.kt)("inlineCode",{parentName:"p"},"input")," data omitted the required ",(0,r.kt)("inlineCode",{parentName:"p"},"duration")," field:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"name: basic-error-demo\ndescription:\ntags:\ninitialize:\n plugins:\n teads-curve:\n path: '@grnsft/if-unofficial-plugins'\n method: TeadsCurve\n global-config:\n interpolation: spline\nexecution:\n status: fail\n error: 'InputValidationError: \"duration\" parameter is required. Error code: invalid_type'.\ntree:\n children:\n child-0:\n defaults:\n cpu/thermal-design-power: 100\n pipeline:\n observe:\n regroup:\n compute:\n - teads-curve\n inputs:\n - timestamp: 2023-07-06T00:00\n cpu/utilization: 20\n")),(0,r.kt)("p",null,"No configuration is necessary - this is the default behaviour for IF if the output is configured to save to yaml and the manifest has an error causing IF to fail."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/91c76d4c.3d35b8ab.js b/assets/js/91c76d4c.3d35b8ab.js deleted file mode 100644 index fef8b761..00000000 --- a/assets/js/91c76d4c.3d35b8ab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[960],{4137:(e,n,t)=>{t.d(n,{Zo:()=>s,kt:()=>c});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function l(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var p=i.createContext({}),d=function(e){var n=i.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},s=function(e){var n=d(e.components);return i.createElement(p.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,s=r(e,["components","mdxType","originalType","parentName"]),m=d(t),c=a,f=m["".concat(p,".").concat(c)]||m[c]||u[c]||o;return t?i.createElement(f,l(l({ref:n},s),{},{components:t})):i.createElement(f,l({ref:n},s))}));function c(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,l=new Array(o);l[0]=m;var r={};for(var p in n)hasOwnProperty.call(n,p)&&(r[p]=n[p]);r.originalType=e,r.mdxType="string"==typeof e?e:a,l[1]=r;for(var d=2;d{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>r,toc:()=>d});var i=t(7462),a=(t(7294),t(4137));const o={},l="Command line tool",r={unversionedId:"reference/cli",id:"reference/cli",title:"Command line tool",description:"A core feature of the Impact Framework is the if-run command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file.",source:"@site/docs/reference/cli.md",sourceDirName:"reference",slug:"/reference/cli",permalink:"/reference/cli",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/cli.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Reference",permalink:"/reference/"},next:{title:"Errors",permalink:"/reference/errors"}},p={},d=[{value:"if-run",id:"if-run",level:2},{value:"--manifest , -m",id:"--manifest---m",level:3},{value:"--output , -o",id:"--output---o",level:3},{value:"--help , -h",id:"--help---h",level:3},{value:"--observe",id:"--observe",level:3},{value:"--regroup",id:"--regroup",level:3},{value:"--compute",id:"--compute",level:3},{value:"--debug",id:"--debug",level:3},{value:"if-diff",id:"if-diff",level:2},{value:"if-diff matching rules",id:"if-diff-matching-rules",level:3},{value:"if-diff outputs",id:"if-diff-outputs",level:3},{value:"if-env",id:"if-env",level:2},{value:"commands",id:"commands",level:3},{value:"Setting up new development environments using if-env",id:"setting-up-new-development-environments-using-if-env",level:3},{value:"Replicating runtime environments using if-env",id:"replicating-runtime-environments-using-if-env",level:3},{value:"if-check",id:"if-check",level:2},{value:"Running IF over multiple manifests with --d",id:"running-if-over-multiple-manifests-with---d",level:3},{value:"if-csv",id:"if-csv",level:2},{value:"commands",id:"commands-1",level:3}],s={toc:d};function u(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,i.Z)({},s,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"command-line-tool"},"Command line tool"),(0,a.kt)("p",null,"A core feature of the Impact Framework is the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file."),(0,a.kt)("p",null,"We also provide several other command line tools that work in concert with ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," to enable flows such as comparing, re-executing and verifying IF output files."),(0,a.kt)("p",null,"This page includes reference documentation for the CLI tools, including the various commands and flags each tool exposes."),(0,a.kt)("p",null,"We also provide tutorial-style user documentation for these tools in the ",(0,a.kt)("a",{parentName:"p",href:"../users/"},(0,a.kt)("inlineCode",{parentName:"a"},"Users"))," section."),(0,a.kt)("h2",{id:"if-run"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-run")),(0,a.kt)("p",null,"If you have globally installed our ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," npm package, you can invoke the CLI using the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," command directly in your terminal. The ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," command is an alias to ",(0,a.kt)("inlineCode",{parentName:"p"},"npx ts-node src/index.ts"),", which executes the Impact Framework's ",(0,a.kt)("inlineCode",{parentName:"p"},"src/index.ts")," script and acts as the entry point for Impact Framework."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run ")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," runs the full execution cycle of a manifest file, including ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," phases along with ",(0,a.kt)("inlineCode",{parentName:"p"},"aggregation")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"explain")," if they are configured in the manifest."),(0,a.kt)("h3",{id:"--manifest---m"},(0,a.kt)("inlineCode",{parentName:"h3"},"--manifest")," , ",(0,a.kt)("inlineCode",{parentName:"h3"},"-m")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"--manifest")," flag is the only required flag and tells ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," where to find the manifest file that you want to execute. This command expects to receive the path where your manifest file is saved, as shown in the following example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest examples/manifests/my-manifest.yml\n")),(0,a.kt)("h3",{id:"--output---o"},(0,a.kt)("inlineCode",{parentName:"h3"},"--output")," , ",(0,a.kt)("inlineCode",{parentName:"h3"},"-o")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," flag is optional and is used for defining a path to save your output data."),(0,a.kt)("p",null,"Here is an example of ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," being used to define a path:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest examples/manifests/my-manifest.yml --output examples/outputs/my-outdata\n## or using aliases\nif-run -m examples/manifests/my-manifest.yml -o examples/outputs/my-outdata\n")),(0,a.kt)("h3",{id:"--help---h"},(0,a.kt)("inlineCode",{parentName:"h3"},"--help")," , ",(0,a.kt)("inlineCode",{parentName:"h3"},"-h")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"--help")," command provides information about all available commands in order to help you easily find the command you need."),(0,a.kt)("p",null,"Example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --help\n## or using alias\nif-run -h\n")),(0,a.kt)("h3",{id:"--observe"},(0,a.kt)("inlineCode",{parentName:"h3"},"--observe")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run --observe")," runs ",(0,a.kt)("em",{parentName:"p"},"only")," the observe phase of the manifest execution. This means only those plugins that generate ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data are run. These are defined in the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," section of the pipeline for each component in the manifest."),(0,a.kt)("p",null,'An example of an observe pipeline that invokes a plugin called "azure-importer" could look as follows:'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n - azure-importer\n")),(0,a.kt)("h3",{id:"--regroup"},(0,a.kt)("inlineCode",{parentName:"h3"},"--regroup")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run --regroup")," runs ",(0,a.kt)("em",{parentName:"p"},"only")," the regrouping phase of the manifest's execution. There has to be ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data available in the manifest to regroup (or ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe")," has to be invoked too) and the regrouping configuration has to be included in the manifest. This config defines which parameters ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --regroup")," should regroup the data by."),(0,a.kt)("p",null,"For example, to regroup on ",(0,a.kt)("inlineCode",{parentName:"p"},"cloud/region")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"cloud/instance-type"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n - cloud/region\n - cloud/instance-type\n")),(0,a.kt)("h3",{id:"--compute"},(0,a.kt)("inlineCode",{parentName:"h3"},"--compute")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run --compute")," runs ",(0,a.kt)("em",{parentName:"p"},"only")," the compute phase of the manifest's execution. The manifest passed to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --compute")," should already have input data, appropriately grouped (or you have to pass ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe --regroup")," too). This includes the plugins that do operations over the input data to generate output data."),(0,a.kt)("p",null,"For example, in a manifest that executes ",(0,a.kt)("inlineCode",{parentName:"p"},"sum"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"coefficient")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"multiply")," in its compute phase:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child-1:\n pipeline:\n observe:\n compute: \n - sum\n - coefficient\n - multiply\n")),(0,a.kt)("h3",{id:"--debug"},(0,a.kt)("inlineCode",{parentName:"h3"},"--debug")),(0,a.kt)("p",null,"You can provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"--debug")," flag to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," in order to display execution logs to the console. These logs show messages for each operation IF and its plugins are executing. For example, your ",(0,a.kt)("inlineCode",{parentName:"p"},"debug")," logs will look similar to the following:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"INFO: 2024-06-12T08:48:02.918Z: Starting IF\nDEBUG: 2024-06-12T08:48:02.919Z: Loading manifest\nDEBUG: 2024-06-12T08:48:02.924Z: Capturing runtime environment data\nDEBUG: 2024-06-12T08:48:03.978Z: Validating manifest\nDEBUG: 2024-06-12T08:48:03.980Z: Syncing parameters\nDEBUG: 2024-06-12T08:48:03.980Z: Initializing plugins\nDEBUG: 2024-06-12T08:48:03.981Z: Initializing Sum\nDEBUG: 2024-06-12T08:48:03.981Z: Loading Sum from builtin\nDEBUG: 2024-06-12T08:48:04.859Z: Initializing Coefficient\nDEBUG: 2024-06-12T08:48:04.859Z: Loading Coefficient from builtin\nDEBUG: 2024-06-12T08:48:04.860Z: Initializing Multiply\nDEBUG: 2024-06-12T08:48:04.860Z: Loading Multiply from builtin\nDEBUG: 2024-06-12T08:48:04.860Z: Computing pipeline for `sum`\nDEBUG: 2024-06-12T08:48:04.861Z: Computing pipeline for `coefficient`\nDEBUG: 2024-06-12T08:48:04.861Z: Computing pipeline for `multiply`\nDEBUG: 2024-06-12T08:48:04.862Z: Aggregating outputs\nDEBUG: 2024-06-12T08:48:04.862Z: Preparing output data\n")),(0,a.kt)("p",null,"You can use the ",(0,a.kt)("inlineCode",{parentName:"p"},"--debug")," flag to help debug failing IF runs. You will see exactly where in the execution pipeline an error arose. If the error arose from a plugin, this will be clear from the execution logs, for example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"INFO: 2024-06-12T08:53:21.376Z: Starting IF\nDEBUG: 2024-06-12T08:53:21.376Z: Loading manifest\nDEBUG: 2024-06-12T08:53:21.381Z: Capturing runtime environment data\nDEBUG: 2024-06-12T08:53:22.367Z: Validating manifest\nDEBUG: 2024-06-12T08:53:22.369Z: Syncing parameters\nDEBUG: 2024-06-12T08:53:22.369Z: Initializing plugins\nDEBUG: 2024-06-12T08:53:22.369Z: Initializing Sum\nDEBUG: 2024-06-12T08:53:22.370Z: Loading Sum from builtin\nDEBUG: 2024-06-12T08:53:23.165Z: Initializing Coefficient\nDEBUG: 2024-06-12T08:53:23.165Z: Loading Coefficient from builtin\nDEBUG: 2024-06-12T08:53:23.165Z: Initializing Multiply\nDEBUG: 2024-06-12T08:53:23.165Z: Loading Multiply from builtin\nDEBUG: 2024-06-12T08:53:23.165Z: Computing pipeline for `sum`\n[2024-06-12 09:53:23.166 AM] error: cpu/energy is missing from the input array.\n")),(0,a.kt)("h2",{id:"if-diff"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-diff")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," command line tool allows you to determine whether two manifest or output files are the same, and if not, how they differ."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," needs two files to compare - a ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"source"),' file is considered to be the "true" file that another file, the ',(0,a.kt)("inlineCode",{parentName:"p"},"target"),", is compared against. Note that for most purposes, it doesn't matter which file is assigned as ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"target")," - the important thing is that ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," receives two files. Both files should be ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," files. They are expected to be IF output files, meaning they contain all the required fields of a ",(0,a.kt)("inlineCode",{parentName:"p"},"manifest")," plus the IF-generated ",(0,a.kt)("inlineCode",{parentName:"p"},"output")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," blocks."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," is run as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-diff --source file-1.yml --target file2.yml\n")),(0,a.kt)("p",null,"You can also pipe the outputs from ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," directly into ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff"),". This means you only provide ",(0,a.kt)("em",{parentName:"p"},"one")," file to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," and the other comes from a new ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," run configured to send its output data to the console. This is an important feature because it allows you to receive an output file and verify that it was computed correctly and not tampered with post-execution. For example, if someone provides you with an output file, you can strip out the ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," section and re-run it with ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", piping the outputs straight to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," to compare against the original you received."),(0,a.kt)("p",null,"If the original was correctly and honestly reported, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," will return a success response."),(0,a.kt)("p",null,"e.g."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m my-manifest | if-diff --target my-output-file.yml\n")),(0,a.kt)("h3",{id:"if-diff-matching-rules"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-diff")," matching rules"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," looks for differences between the ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),". However, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," applies its own IF-specific matching rules, ensuring that the outputs are functionally identical even if they are not precisely identical. For example, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," allows the order of nodes in a tree to vary between files as long as identically named components contain identical data."),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Difference identified"),(0,a.kt)("th",{parentName:"tr",align:null},"Report or ignore?"),(0,a.kt)("th",{parentName:"tr",align:null},"Note"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"trees contain different number of nodes"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"works the same regardless whether ",(0,a.kt)("inlineCode",{parentName:"td"},"source")," or ",(0,a.kt)("inlineCode",{parentName:"td"},"target")," has more nodes")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"nodes in tree have different names"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"There should be no named nodes existing in one file that aren't also in the other")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"nodes in tree contain non-identical fields and/or values"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"the data inside each tree component should contain identical keys/values")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"keys and values in context field are non- identical"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"the same fields should exist in the ",(0,a.kt)("inlineCode",{parentName:"td"},"context")," section and their values should be identical")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"status")," and ",(0,a.kt)("inlineCode",{parentName:"td"},"error")," fields in ",(0,a.kt)("inlineCode",{parentName:"td"},"execution")," block"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"Only these two fields in ",(0,a.kt)("inlineCode",{parentName:"td"},"execution")," are considered")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"order of nodes in tree are different"),(0,a.kt)("td",{parentName:"tr",align:null},"ignore"),(0,a.kt)("td",{parentName:"tr",align:null},"if data is identical, position of node in tree is ignored")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"order of fields in context"),(0,a.kt)("td",{parentName:"tr",align:null},"ignore"),(0,a.kt)("td",{parentName:"tr",align:null},"if data is identical, position of field in context is ignored")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"content of execution block EXCEPT ",(0,a.kt)("inlineCode",{parentName:"td"},"status")," and ",(0,a.kt)("inlineCode",{parentName:"td"},"error")),(0,a.kt)("td",{parentName:"tr",align:null},"ignore"),(0,a.kt)("td",{parentName:"tr",align:null},"environment information is ignored")))),(0,a.kt)("h3",{id:"if-diff-outputs"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-diff")," outputs"),(0,a.kt)("p",null,"If ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," finds no in-scope differences between the ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"target")," then it returns a success message and exit code ",(0,a.kt)("inlineCode",{parentName:"p"},"0"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"FILES MATCH and exit code 0.\n")),(0,a.kt)("p",null,"If ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," detects an in-scope difference between the files, it halts execution, returns exit code ",(0,a.kt)("inlineCode",{parentName:"p"},"1")," and reports the difference to the command line."),(0,a.kt)("p",null,"The report includes the yaml path to the differing element in the tree, the value in the ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and the value in the ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),", using the following schema:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"Files do not match!\n\nsource: \ntarget: \n")),(0,a.kt)("p",null,"If the difference relates to a missing node in the tree for source or target then ",(0,a.kt)("inlineCode",{parentName:"p"},"")," should be either exists or missing and the yaml path should point to the highest level element that is missing (e.g. if an entire child component is missing, provide the path to the child component)."),(0,a.kt)("p",null,"e.g. different values detected for a given key in an input array:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"Files do not match!\ntree.children.vm1[4].cpu/utilization\nsource: 45\ntarget: 43\n")),(0,a.kt)("p",null,"e.g. different components in ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," in ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"Files do not match!\ntree.children.child1\nsource: missing\ntarget: exists\n")),(0,a.kt)("h2",{id:"if-env"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-env")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," is a command line tool that helps you to create local development environments where you can run manifests."),(0,a.kt)("p",null,"There are two use cases for this:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"setting up a new development environment for plugin building"),(0,a.kt)("li",{parentName:"ol"},"replicating a runtime environment for a given manifest, so you can re-execute it")),(0,a.kt)("h3",{id:"commands"},"commands"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),": the path to a manifest whose dependencies you want to install"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--install")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-i"),": instructs ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env")," to automatically install the dependencies in the local ",(0,a.kt)("inlineCode",{parentName:"li"},"package.json")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--cwd")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-c"),": forces ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env")," to create or update the package.json in the current working directory. This is already default behaviour when no arguments are passed to ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env"),", but when a manifest is passed to ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),", ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env")," defaults to saving a package.json in the same folder as the manifest. using ",(0,a.kt)("inlineCode",{parentName:"li"},"-cwd")," overrides that behaviour and uses the current working directory as the ",(0,a.kt)("inlineCode",{parentName:"li"},"package.json")," target path.")),(0,a.kt)("h3",{id:"setting-up-new-development-environments-using-if-env"},"Setting up new development environments using ",(0,a.kt)("inlineCode",{parentName:"h3"},"if-env")),(0,a.kt)("p",null,"If you are creating a new manifest from scratch and want to bootstrap your way in, you can use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," with no arguments to generate a template manifest and package.json in your current working directory. Then, all you need to do is tweak the templates for your specific use case."),(0,a.kt)("p",null,"For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"mkdir my-manifest && cd my-manifest\nif-env\n")),(0,a.kt)("p",null,"After running these commands, you will see the following files in ",(0,a.kt)("inlineCode",{parentName:"p"},"my-manifest"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"ls my-manifest\n\n> package.json manifest.yaml\n")),(0,a.kt)("p",null,"Now, you can use these files as templates for your manifest development."),(0,a.kt)("h3",{id:"replicating-runtime-environments-using-if-env"},"Replicating runtime environments using ",(0,a.kt)("inlineCode",{parentName:"h3"},"if-env")),(0,a.kt)("p",null,"If you are given an IF output file and you want to re-run it, you can use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," to install that output file's dependencies so that all the plugins in its execution pipeline can be executed."),(0,a.kt)("p",null,"For example, if you are given a file, ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yml"),", you can save the file to ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," and run"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"cd if\nif-env -m output-file.yml\n")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," will compare the installed dependencies in the ",(0,a.kt)("inlineCode",{parentName:"p"},"package.json")," it sees in ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," with the dependencies listed in ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yaml"),". Any dependencies that are in ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yaml")," and not in ",(0,a.kt)("inlineCode",{parentName:"p"},"if/package.json")," will be added to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-package.json"),". Then, you can run:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"npm i\n")),(0,a.kt)("p",null,"and you are ready to re-execute ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yaml")," in your local environment. We also provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"--install")," flag to instruct ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," to automatically run ",(0,a.kt)("inlineCode",{parentName:"p"},"npm i")," after merging the dependencies, so you could craft a single command to install all the relevant dependencies and then run the manifest, as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-env -m output-file.yml -i && if-run -m output-file.yml\n")),(0,a.kt)("h2",{id:"if-check"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-check")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," is a manifest verification tool that is equivalent to running ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," on a given manifest file. The manifest file must have ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," and an ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," section for ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," to run."),(0,a.kt)("p",null,"The intended use case is to verify that a manifest's outputs are correct and honest. Say someone handed you a manifest as evidence of their environmental impact. You could choose to trust them, or you could run ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," to verify that their calculations are correct. Under the hood, IF is creating a development environment using the dependencies listed in the given file's ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," section and then executing the file locally, then comparing the newly generated results to those in the given file."),(0,a.kt)("p",null,"To check a file:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-check -m \n")),(0,a.kt)("p",null,"If the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," is successful you will receive the following response:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-check: successfully verified \n")),(0,a.kt)("p",null,"If ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," was not able to verify the file because there were differences in the given and re-executed files, then you will receive the following response which includes the details of how the files differ, as per ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-check: could not verify . The re-executed file does not match the original.\n")),(0,a.kt)("h3",{id:"running-if-over-multiple-manifests-with---d"},"Running IF over multiple manifests with ",(0,a.kt)("inlineCode",{parentName:"h3"},"--d")),(0,a.kt)("p",null,"Alice could also run ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," over any number of manifests in a single command, using the ",(0,a.kt)("inlineCode",{parentName:"p"},"--directory")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"-d")," subcommand. For a folder containing multiple manifests, pass the folder path:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-check -d /my-folder-of-manifests\n")),(0,a.kt)("p",null,"Each manifest will be run through ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," in sequence."),(0,a.kt)("h2",{id:"if-csv"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-csv")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-csv")," is a command line tool that helps to save data to CSV file."),(0,a.kt)("h3",{id:"commands-1"},"commands"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),": (optional) the path to an executed manifest"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--output")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-o"),": (optional) the path to save your output data in ",(0,a.kt)("inlineCode",{parentName:"li"},"csv")," format"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--params")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-p"),": (required) the metric to export the data")),(0,a.kt)("p",null,"There are three use cases for this:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Exporting CSV with the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag: When the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag is provided, ",(0,a.kt)("inlineCode",{parentName:"li"},"if-csv")," exports the data to a CSV file at the specified path. This is useful for saving data for later use or sharing with others.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./my-manifest.yml -p carbon -o ./my-outdata\n")),(0,a.kt)("ol",{start:2},(0,a.kt)("li",{parentName:"ol"},"Printing CSV to the console without the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag: If the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag is omitted, ",(0,a.kt)("inlineCode",{parentName:"li"},"if-csv")," will print the CSV data directly to the console. This is useful for quick checks.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./my-manifest.yml -p carbon\n")),(0,a.kt)("ol",{start:3},(0,a.kt)("li",{parentName:"ol"},"Piping output from ",(0,a.kt)("inlineCode",{parentName:"li"},"if-run")," to ",(0,a.kt)("inlineCode",{parentName:"li"},"if-csv"),". By piping the output from ",(0,a.kt)("inlineCode",{parentName:"li"},"if-run"),", you can chain commands to execute a manifest and then immediately export the data to a CSV file.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m ./my-manifest.yml | if-csv -p carbon -o ./my-outdata\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/91c76d4c.9a236c64.js b/assets/js/91c76d4c.9a236c64.js new file mode 100644 index 00000000..847278f1 --- /dev/null +++ b/assets/js/91c76d4c.9a236c64.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[960],{4137:(e,n,t)=>{t.d(n,{Zo:()=>d,kt:()=>c});var i=t(7294);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var p=i.createContext({}),s=function(e){var n=i.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},d=function(e){var n=s(e.components);return i.createElement(p.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},m=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),m=s(t),c=a,f=m["".concat(p,".").concat(c)]||m[c]||u[c]||o;return t?i.createElement(f,r(r({ref:n},d),{},{components:t})):i.createElement(f,r({ref:n},d))}));function c(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=m;var l={};for(var p in n)hasOwnProperty.call(n,p)&&(l[p]=n[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,r[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var i=t(7462),a=(t(7294),t(4137));const o={},r="Command line tool",l={unversionedId:"reference/cli",id:"reference/cli",title:"Command line tool",description:"A core feature of the Impact Framework is the if-run command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file.",source:"@site/docs/reference/cli.md",sourceDirName:"reference",slug:"/reference/cli",permalink:"/reference/cli",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/cli.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Reference",permalink:"/reference/"},next:{title:"Errors",permalink:"/reference/errors"}},p={},s=[{value:"if-run",id:"if-run",level:2},{value:"--manifest , -m",id:"--manifest---m",level:3},{value:"--output , -o",id:"--output---o",level:3},{value:"--help , -h",id:"--help---h",level:3},{value:"--observe",id:"--observe",level:3},{value:"--regroup",id:"--regroup",level:3},{value:"--compute",id:"--compute",level:3},{value:"--debug",id:"--debug",level:3},{value:"if-diff",id:"if-diff",level:2},{value:"if-diff matching rules",id:"if-diff-matching-rules",level:3},{value:"if-diff outputs",id:"if-diff-outputs",level:3},{value:"if-env",id:"if-env",level:2},{value:"commands",id:"commands",level:3},{value:"Setting up new development environments using if-env",id:"setting-up-new-development-environments-using-if-env",level:3},{value:"Replicating runtime environments using if-env",id:"replicating-runtime-environments-using-if-env",level:3},{value:"if-check",id:"if-check",level:2},{value:"Running IF over multiple manifests with --d",id:"running-if-over-multiple-manifests-with---d",level:3},{value:"if-csv",id:"if-csv",level:2},{value:"commands",id:"commands-1",level:3},{value:"--append",id:"--append",level:2},{value:"example",id:"example",level:3}],d={toc:s};function u(e){let{components:n,...t}=e;return(0,a.kt)("wrapper",(0,i.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"command-line-tool"},"Command line tool"),(0,a.kt)("p",null,"A core feature of the Impact Framework is the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file."),(0,a.kt)("p",null,"We also provide several other command line tools that work in concert with ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," to enable flows such as comparing, re-executing and verifying IF output files."),(0,a.kt)("p",null,"This page includes reference documentation for the CLI tools, including the various commands and flags each tool exposes."),(0,a.kt)("p",null,"We also provide tutorial-style user documentation for these tools in the ",(0,a.kt)("a",{parentName:"p",href:"../users/"},(0,a.kt)("inlineCode",{parentName:"a"},"Users"))," section."),(0,a.kt)("h2",{id:"if-run"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-run")),(0,a.kt)("p",null,"If you have globally installed our ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," npm package, you can invoke the CLI using the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," command directly in your terminal. The ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," command is an alias to ",(0,a.kt)("inlineCode",{parentName:"p"},"npx ts-node src/index.ts"),", which executes the Impact Framework's ",(0,a.kt)("inlineCode",{parentName:"p"},"src/index.ts")," script and acts as the entry point for Impact Framework."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run ")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," runs the full execution cycle of a manifest file, including ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute")," phases along with ",(0,a.kt)("inlineCode",{parentName:"p"},"aggregation")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"explain")," if they are configured in the manifest."),(0,a.kt)("h3",{id:"--manifest---m"},(0,a.kt)("inlineCode",{parentName:"h3"},"--manifest")," , ",(0,a.kt)("inlineCode",{parentName:"h3"},"-m")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"--manifest")," flag is the only required flag and tells ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," where to find the manifest file that you want to execute. This command expects to receive the path where your manifest file is saved, as shown in the following example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest examples/manifests/my-manifest.yml\n")),(0,a.kt)("h3",{id:"--output---o"},(0,a.kt)("inlineCode",{parentName:"h3"},"--output")," , ",(0,a.kt)("inlineCode",{parentName:"h3"},"-o")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," flag is optional and is used for defining a path to save your output data."),(0,a.kt)("p",null,"Here is an example of ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," being used to define a path:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest examples/manifests/my-manifest.yml --output examples/outputs/my-outdata\n## or using aliases\nif-run -m examples/manifests/my-manifest.yml -o examples/outputs/my-outdata\n")),(0,a.kt)("h3",{id:"--help---h"},(0,a.kt)("inlineCode",{parentName:"h3"},"--help")," , ",(0,a.kt)("inlineCode",{parentName:"h3"},"-h")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"--help")," command provides information about all available commands in order to help you easily find the command you need."),(0,a.kt)("p",null,"Example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --help\n## or using alias\nif-run -h\n")),(0,a.kt)("h3",{id:"--observe"},(0,a.kt)("inlineCode",{parentName:"h3"},"--observe")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run --observe")," runs ",(0,a.kt)("em",{parentName:"p"},"only")," the observe phase of the manifest execution. This means only those plugins that generate ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data are run. These are defined in the ",(0,a.kt)("inlineCode",{parentName:"p"},"observe")," section of the pipeline for each component in the manifest."),(0,a.kt)("p",null,'An example of an observe pipeline that invokes a plugin called "azure-importer" could look as follows:'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n - azure-importer\n")),(0,a.kt)("h3",{id:"--regroup"},(0,a.kt)("inlineCode",{parentName:"h3"},"--regroup")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run --regroup")," runs ",(0,a.kt)("em",{parentName:"p"},"only")," the regrouping phase of the manifest's execution. There has to be ",(0,a.kt)("inlineCode",{parentName:"p"},"input")," data available in the manifest to regroup (or ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe")," has to be invoked too) and the regrouping configuration has to be included in the manifest. This config defines which parameters ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --regroup")," should regroup the data by."),(0,a.kt)("p",null,"For example, to regroup on ",(0,a.kt)("inlineCode",{parentName:"p"},"cloud/region")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"cloud/instance-type"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n - cloud/region\n - cloud/instance-type\n")),(0,a.kt)("h3",{id:"--compute"},(0,a.kt)("inlineCode",{parentName:"h3"},"--compute")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run --compute")," runs ",(0,a.kt)("em",{parentName:"p"},"only")," the compute phase of the manifest's execution. The manifest passed to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run --compute")," should already have input data, appropriately grouped (or you have to pass ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe --regroup")," too). This includes the plugins that do operations over the input data to generate output data."),(0,a.kt)("p",null,"For example, in a manifest that executes ",(0,a.kt)("inlineCode",{parentName:"p"},"sum"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"coefficient")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"multiply")," in its compute phase:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child-1:\n pipeline:\n observe:\n compute: \n - sum\n - coefficient\n - multiply\n")),(0,a.kt)("h3",{id:"--debug"},(0,a.kt)("inlineCode",{parentName:"h3"},"--debug")),(0,a.kt)("p",null,"You can provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"--debug")," flag to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," in order to display execution logs to the console. These logs show messages for each operation IF and its plugins are executing. For example, your ",(0,a.kt)("inlineCode",{parentName:"p"},"debug")," logs will look similar to the following:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"INFO: 2024-06-12T08:48:02.918Z: Starting IF\nDEBUG: 2024-06-12T08:48:02.919Z: Loading manifest\nDEBUG: 2024-06-12T08:48:02.924Z: Capturing runtime environment data\nDEBUG: 2024-06-12T08:48:03.978Z: Validating manifest\nDEBUG: 2024-06-12T08:48:03.980Z: Syncing parameters\nDEBUG: 2024-06-12T08:48:03.980Z: Initializing plugins\nDEBUG: 2024-06-12T08:48:03.981Z: Initializing Sum\nDEBUG: 2024-06-12T08:48:03.981Z: Loading Sum from builtin\nDEBUG: 2024-06-12T08:48:04.859Z: Initializing Coefficient\nDEBUG: 2024-06-12T08:48:04.859Z: Loading Coefficient from builtin\nDEBUG: 2024-06-12T08:48:04.860Z: Initializing Multiply\nDEBUG: 2024-06-12T08:48:04.860Z: Loading Multiply from builtin\nDEBUG: 2024-06-12T08:48:04.860Z: Computing pipeline for `sum`\nDEBUG: 2024-06-12T08:48:04.861Z: Computing pipeline for `coefficient`\nDEBUG: 2024-06-12T08:48:04.861Z: Computing pipeline for `multiply`\nDEBUG: 2024-06-12T08:48:04.862Z: Aggregating outputs\nDEBUG: 2024-06-12T08:48:04.862Z: Preparing output data\n")),(0,a.kt)("p",null,"You can use the ",(0,a.kt)("inlineCode",{parentName:"p"},"--debug")," flag to help debug failing IF runs. You will see exactly where in the execution pipeline an error arose. If the error arose from a plugin, this will be clear from the execution logs, for example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"INFO: 2024-06-12T08:53:21.376Z: Starting IF\nDEBUG: 2024-06-12T08:53:21.376Z: Loading manifest\nDEBUG: 2024-06-12T08:53:21.381Z: Capturing runtime environment data\nDEBUG: 2024-06-12T08:53:22.367Z: Validating manifest\nDEBUG: 2024-06-12T08:53:22.369Z: Syncing parameters\nDEBUG: 2024-06-12T08:53:22.369Z: Initializing plugins\nDEBUG: 2024-06-12T08:53:22.369Z: Initializing Sum\nDEBUG: 2024-06-12T08:53:22.370Z: Loading Sum from builtin\nDEBUG: 2024-06-12T08:53:23.165Z: Initializing Coefficient\nDEBUG: 2024-06-12T08:53:23.165Z: Loading Coefficient from builtin\nDEBUG: 2024-06-12T08:53:23.165Z: Initializing Multiply\nDEBUG: 2024-06-12T08:53:23.165Z: Loading Multiply from builtin\nDEBUG: 2024-06-12T08:53:23.165Z: Computing pipeline for `sum`\n[2024-06-12 09:53:23.166 AM] error: cpu/energy is missing from the input array.\n")),(0,a.kt)("h2",{id:"if-diff"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-diff")),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," command line tool allows you to determine whether two manifest or output files are the same, and if not, how they differ."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," needs two files to compare - a ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and a ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),". The ",(0,a.kt)("inlineCode",{parentName:"p"},"source"),' file is considered to be the "true" file that another file, the ',(0,a.kt)("inlineCode",{parentName:"p"},"target"),", is compared against. Note that for most purposes, it doesn't matter which file is assigned as ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"target")," - the important thing is that ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," receives two files. Both files should be ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," files. They are expected to be IF output files, meaning they contain all the required fields of a ",(0,a.kt)("inlineCode",{parentName:"p"},"manifest")," plus the IF-generated ",(0,a.kt)("inlineCode",{parentName:"p"},"output")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," blocks."),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," is run as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-diff --source file-1.yml --target file2.yml\n")),(0,a.kt)("p",null,"You can also pipe the outputs from ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," directly into ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff"),". This means you only provide ",(0,a.kt)("em",{parentName:"p"},"one")," file to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," and the other comes from a new ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," run configured to send its output data to the console. This is an important feature because it allows you to receive an output file and verify that it was computed correctly and not tampered with post-execution. For example, if someone provides you with an output file, you can strip out the ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," section and re-run it with ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", piping the outputs straight to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," to compare against the original you received."),(0,a.kt)("p",null,"If the original was correctly and honestly reported, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," will return a success response."),(0,a.kt)("p",null,"e.g."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m my-manifest | if-diff --target my-output-file.yml\n")),(0,a.kt)("h3",{id:"if-diff-matching-rules"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-diff")," matching rules"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," looks for differences between the ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),". However, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," applies its own IF-specific matching rules, ensuring that the outputs are functionally identical even if they are not precisely identical. For example, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," allows the order of nodes in a tree to vary between files as long as identically named components contain identical data."),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Difference identified"),(0,a.kt)("th",{parentName:"tr",align:null},"Report or ignore?"),(0,a.kt)("th",{parentName:"tr",align:null},"Note"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"trees contain different number of nodes"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"works the same regardless whether ",(0,a.kt)("inlineCode",{parentName:"td"},"source")," or ",(0,a.kt)("inlineCode",{parentName:"td"},"target")," has more nodes")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"nodes in tree have different names"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"There should be no named nodes existing in one file that aren't also in the other")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"nodes in tree contain non-identical fields and/or values"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"the data inside each tree component should contain identical keys/values")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"keys and values in context field are non- identical"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"the same fields should exist in the ",(0,a.kt)("inlineCode",{parentName:"td"},"context")," section and their values should be identical")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"status")," and ",(0,a.kt)("inlineCode",{parentName:"td"},"error")," fields in ",(0,a.kt)("inlineCode",{parentName:"td"},"execution")," block"),(0,a.kt)("td",{parentName:"tr",align:null},"report"),(0,a.kt)("td",{parentName:"tr",align:null},"Only these two fields in ",(0,a.kt)("inlineCode",{parentName:"td"},"execution")," are considered")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"order of nodes in tree are different"),(0,a.kt)("td",{parentName:"tr",align:null},"ignore"),(0,a.kt)("td",{parentName:"tr",align:null},"if data is identical, position of node in tree is ignored")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"order of fields in context"),(0,a.kt)("td",{parentName:"tr",align:null},"ignore"),(0,a.kt)("td",{parentName:"tr",align:null},"if data is identical, position of field in context is ignored")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"content of execution block EXCEPT ",(0,a.kt)("inlineCode",{parentName:"td"},"status")," and ",(0,a.kt)("inlineCode",{parentName:"td"},"error")),(0,a.kt)("td",{parentName:"tr",align:null},"ignore"),(0,a.kt)("td",{parentName:"tr",align:null},"environment information is ignored")))),(0,a.kt)("h3",{id:"if-diff-outputs"},(0,a.kt)("inlineCode",{parentName:"h3"},"if-diff")," outputs"),(0,a.kt)("p",null,"If ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," finds no in-scope differences between the ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"target")," then it returns a success message and exit code ",(0,a.kt)("inlineCode",{parentName:"p"},"0"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"FILES MATCH and exit code 0.\n")),(0,a.kt)("p",null,"If ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," detects an in-scope difference between the files, it halts execution, returns exit code ",(0,a.kt)("inlineCode",{parentName:"p"},"1")," and reports the difference to the command line."),(0,a.kt)("p",null,"The report includes the yaml path to the differing element in the tree, the value in the ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and the value in the ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),", using the following schema:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"Files do not match!\n\nsource: \ntarget: \n")),(0,a.kt)("p",null,"If the difference relates to a missing node in the tree for source or target then ",(0,a.kt)("inlineCode",{parentName:"p"},"")," should be either exists or missing and the yaml path should point to the highest level element that is missing (e.g. if an entire child component is missing, provide the path to the child component)."),(0,a.kt)("p",null,"e.g. different values detected for a given key in an input array:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"Files do not match!\ntree.children.vm1[4].cpu/utilization\nsource: 45\ntarget: 43\n")),(0,a.kt)("p",null,"e.g. different components in ",(0,a.kt)("inlineCode",{parentName:"p"},"tree")," in ",(0,a.kt)("inlineCode",{parentName:"p"},"source")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"target"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"Files do not match!\ntree.children.child1\nsource: missing\ntarget: exists\n")),(0,a.kt)("h2",{id:"if-env"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-env")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," is a command line tool that helps you to create local development environments where you can run manifests."),(0,a.kt)("p",null,"There are two use cases for this:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"setting up a new development environment for plugin building"),(0,a.kt)("li",{parentName:"ol"},"replicating a runtime environment for a given manifest, so you can re-execute it")),(0,a.kt)("h3",{id:"commands"},"commands"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),": the path to a manifest whose dependencies you want to install"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--install")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-i"),": instructs ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env")," to automatically install the dependencies in the local ",(0,a.kt)("inlineCode",{parentName:"li"},"package.json")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--cwd")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-c"),": forces ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env")," to create or update the package.json in the current working directory. This is already default behaviour when no arguments are passed to ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env"),", but when a manifest is passed to ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),", ",(0,a.kt)("inlineCode",{parentName:"li"},"if-env")," defaults to saving a package.json in the same folder as the manifest. using ",(0,a.kt)("inlineCode",{parentName:"li"},"-cwd")," overrides that behaviour and uses the current working directory as the ",(0,a.kt)("inlineCode",{parentName:"li"},"package.json")," target path.")),(0,a.kt)("h3",{id:"setting-up-new-development-environments-using-if-env"},"Setting up new development environments using ",(0,a.kt)("inlineCode",{parentName:"h3"},"if-env")),(0,a.kt)("p",null,"If you are creating a new manifest from scratch and want to bootstrap your way in, you can use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," with no arguments to generate a template manifest and package.json in your current working directory. Then, all you need to do is tweak the templates for your specific use case."),(0,a.kt)("p",null,"For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"mkdir my-manifest && cd my-manifest\nif-env\n")),(0,a.kt)("p",null,"After running these commands, you will see the following files in ",(0,a.kt)("inlineCode",{parentName:"p"},"my-manifest"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"ls my-manifest\n\n> package.json manifest.yaml\n")),(0,a.kt)("p",null,"Now, you can use these files as templates for your manifest development."),(0,a.kt)("h3",{id:"replicating-runtime-environments-using-if-env"},"Replicating runtime environments using ",(0,a.kt)("inlineCode",{parentName:"h3"},"if-env")),(0,a.kt)("p",null,"If you are given an IF output file and you want to re-run it, you can use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," to install that output file's dependencies so that all the plugins in its execution pipeline can be executed."),(0,a.kt)("p",null,"For example, if you are given a file, ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yml"),", you can save the file to ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," and run"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"cd if\nif-env -m output-file.yml\n")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," will compare the installed dependencies in the ",(0,a.kt)("inlineCode",{parentName:"p"},"package.json")," it sees in ",(0,a.kt)("inlineCode",{parentName:"p"},"if")," with the dependencies listed in ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yaml"),". Any dependencies that are in ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yaml")," and not in ",(0,a.kt)("inlineCode",{parentName:"p"},"if/package.json")," will be added to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-package.json"),". Then, you can run:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"npm i\n")),(0,a.kt)("p",null,"and you are ready to re-execute ",(0,a.kt)("inlineCode",{parentName:"p"},"output-file.yaml")," in your local environment. We also provide the ",(0,a.kt)("inlineCode",{parentName:"p"},"--install")," flag to instruct ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," to automatically run ",(0,a.kt)("inlineCode",{parentName:"p"},"npm i")," after merging the dependencies, so you could craft a single command to install all the relevant dependencies and then run the manifest, as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-env -m output-file.yml -i && if-run -m output-file.yml\n")),(0,a.kt)("h2",{id:"if-check"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-check")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," is a manifest verification tool that is equivalent to running ",(0,a.kt)("inlineCode",{parentName:"p"},"if-env")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff")," on a given manifest file. The manifest file must have ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs")," and an ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," section for ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," to run."),(0,a.kt)("p",null,"The intended use case is to verify that a manifest's outputs are correct and honest. Say someone handed you a manifest as evidence of their environmental impact. You could choose to trust them, or you could run ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," to verify that their calculations are correct. Under the hood, IF is creating a development environment using the dependencies listed in the given file's ",(0,a.kt)("inlineCode",{parentName:"p"},"execution")," section and then executing the file locally, then comparing the newly generated results to those in the given file."),(0,a.kt)("p",null,"To check a file:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-check -m \n")),(0,a.kt)("p",null,"If the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," is successful you will receive the following response:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-check: successfully verified \n")),(0,a.kt)("p",null,"If ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," was not able to verify the file because there were differences in the given and re-executed files, then you will receive the following response which includes the details of how the files differ, as per ",(0,a.kt)("inlineCode",{parentName:"p"},"if-diff"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-check: could not verify . The re-executed file does not match the original.\n")),(0,a.kt)("h3",{id:"running-if-over-multiple-manifests-with---d"},"Running IF over multiple manifests with ",(0,a.kt)("inlineCode",{parentName:"h3"},"--d")),(0,a.kt)("p",null,"Alice could also run ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," over any number of manifests in a single command, using the ",(0,a.kt)("inlineCode",{parentName:"p"},"--directory")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"-d")," subcommand. For a folder containing multiple manifests, pass the folder path:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-check -d /my-folder-of-manifests\n")),(0,a.kt)("p",null,"Each manifest will be run through ",(0,a.kt)("inlineCode",{parentName:"p"},"if-check")," in sequence."),(0,a.kt)("h2",{id:"if-csv"},(0,a.kt)("inlineCode",{parentName:"h2"},"if-csv")),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-csv")," is a command line tool that helps to save data to CSV file."),(0,a.kt)("h3",{id:"commands-1"},"commands"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),": (optional) the path to an executed manifest"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--output")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-o"),": (optional) the path to save your output data in ",(0,a.kt)("inlineCode",{parentName:"li"},"csv")," format"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--params")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-p"),": (required) the metric to export the data")),(0,a.kt)("p",null,"There are three use cases for this:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Exporting CSV with the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag: When the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag is provided, ",(0,a.kt)("inlineCode",{parentName:"li"},"if-csv")," exports the data to a CSV file at the specified path. This is useful for saving data for later use or sharing with others.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./my-manifest.yml -p carbon -o ./my-outdata\n")),(0,a.kt)("ol",{start:2},(0,a.kt)("li",{parentName:"ol"},"Printing CSV to the console without the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag: If the ",(0,a.kt)("inlineCode",{parentName:"li"},"--output")," flag is omitted, ",(0,a.kt)("inlineCode",{parentName:"li"},"if-csv")," will print the CSV data directly to the console. This is useful for quick checks.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m ./my-manifest.yml -p carbon\n")),(0,a.kt)("ol",{start:3},(0,a.kt)("li",{parentName:"ol"},"Piping output from ",(0,a.kt)("inlineCode",{parentName:"li"},"if-run")," to ",(0,a.kt)("inlineCode",{parentName:"li"},"if-csv"),". By piping the output from ",(0,a.kt)("inlineCode",{parentName:"li"},"if-run"),", you can chain commands to execute a manifest and then immediately export the data to a CSV file.")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m ./my-manifest.yml | if-csv -p carbon -o ./my-outdata\n")),(0,a.kt)("h2",{id:"--append"},(0,a.kt)("inlineCode",{parentName:"h2"},"--append")),(0,a.kt)("p",null,"You can re-use a manifest file to make multiple batches of observations, appending the results to the existing outputs. The command that makes this possible is ",(0,a.kt)("inlineCode",{parentName:"p"},"--append"),". To use ",(0,a.kt)("inlineCode",{parentName:"p"},"--append")," you have to pass a manifest files that has ",(0,a.kt)("strong",{parentName:"p"},"already been computed")," - i.e.it already has outputs. If you do, then the newly generated outputs will be appended to the existing output data."),(0,a.kt)("p",null,"The use case for this is when you want to repeatedly monitor the same resource or set of resources without changign the manifest config - you just want to grab new observations. The ",(0,a.kt)("inlineCode",{parentName:"p"},"--append")," command allows you to do this without havign to generate lots of individual manifest files."),(0,a.kt)("h3",{id:"example"},"example"),(0,a.kt)("p",null,"With a computed manifest:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: append\ndescription: >-\n a complete pipeline that starts with mocked CPU utilization data and outputs\n operational carbon in gCO2eq\ninitialize:\n plugins:\n mock-observations:\n path: builtin\n method: MockObservations\n config:\n timestamp-from: '2024-03-05T00:00:04.000Z'\n timestamp-to: '2024-03-05T00:00:07.000Z'\n duration: 1\n components:\n - name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n generators:\n common:\n cloud/vendor: azure\n randint:\n cpu/energy:\n min: 1\n max: 99\n mem/energy:\n min: 1\n max: 99\n sum:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - mem/energy\n output-parameter: energy\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/Code/if/src/index.ts -m\n manifests/examples/mock-cpu-util-to-carbon.yml -s\n environment:\n if-version: 0.4.0\n os: linux\n os-version: 5.15.0-107-generic\n node-version: 21.4.0\n date-time: 2024-06-18T14:18:44.864Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.3'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n pipeline:\n compute:\n - mock-observations\n - sum\n regroup:\n - cloud/region\n - name\n defaults: null\n inputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 5\n mem/energy: 10\n - timestamp: '2024-03-05T00:00:01.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 71\n mem/energy: 5\n - timestamp: '2024-03-05T00:00:02.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 36\n mem/energy: 74\n outputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 5\n mem/energy: 10\n energy: 15\n - timestamp: '2024-03-05T00:00:01.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 71\n mem/energy: 5\n energy: 76\n - timestamp: '2024-03-05T00:00:02.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 36\n mem/energy: 74\n energy: 110\n")),(0,a.kt)("p",null,"run "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"npm run if-run -- -m manifests/outputs/features/append.yaml -o manifests/outputs/features/re-append --append \n")),(0,a.kt)("p",null,"And see the following output (with new observations appended to old observations):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: append\ndescription: >-\n a complete pipeline that starts with mocked CPU utilization data and outputs\n operational carbon in gCO2eq\ninitialize:\n plugins:\n mock-observations:\n path: builtin\n method: MockObservations\n config:\n timestamp-from: '2024-03-05T00:00:04.000Z'\n timestamp-to: '2024-03-05T00:00:07.000Z'\n duration: 1\n components:\n - name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n generators:\n common:\n cloud/vendor: azure\n randint:\n cpu/energy:\n min: 1\n max: 99\n mem/energy:\n min: 1\n max: 99\n sum:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - mem/energy\n output-parameter: energy\nexecution:\n command: >-\n /Users/jcrowley/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /Users/jcrowley/Development/gsf/if/src/if-run/index.ts -m\n manifests/outputs/features/append.yaml -o\n manifests/outputs/features/re-append --append\n environment:\n if-version: 0.6.0\n os: macOS\n os-version: 14.6.1\n node-version: 20.16.0\n date-time: 2024-09-04T01:05:58.758Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.16'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.23.8\n status: success\ntree:\n pipeline:\n compute:\n - mock-observations\n - sum\n regroup:\n - cloud/region\n - name\n defaults: null\n children:\n westus3:\n children:\n server-1:\n inputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 5\n mem/energy: 10\n - timestamp: '2024-03-05T00:00:01.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 71\n mem/energy: 5\n - timestamp: '2024-03-05T00:00:02.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 36\n mem/energy: 74\n outputs:\n - timestamp: '2024-03-05T00:00:00.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 5\n mem/energy: 10\n energy: 15\n - timestamp: '2024-03-05T00:00:01.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 71\n mem/energy: 5\n energy: 76\n - timestamp: '2024-03-05T00:00:02.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 36\n mem/energy: 74\n energy: 110\n - timestamp: '2024-03-05T00:00:04.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 2\n mem/energy: 26\n energy: 28\n - timestamp: '2024-03-05T00:00:05.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 67\n mem/energy: 27\n energy: 94\n - timestamp: '2024-03-05T00:00:06.000Z'\n duration: 1\n name: server-1\n cloud/instance-type: Standard_E64_v3\n cloud/region: westus3\n cloud/vendor: azure\n cpu/energy: 88\n mem/energy: 6\n energy: 94\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.c3cebc34.js b/assets/js/935f2afb.c3cebc34.js deleted file mode 100644 index efda23ab..00000000 --- a/assets/js/935f2afb.c3cebc34.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Introduction","href":"/intro","docId":"intro"},{"type":"category","label":"Major Concepts","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Design philosophy","href":"/major-concepts/design-philosophy","docId":"major-concepts/design-philosophy"},{"type":"link","label":"Aggregation","href":"/major-concepts/aggregation","docId":"major-concepts/aggregation"},{"type":"link","label":"Pipelines","href":"/major-concepts/pipelines","docId":"major-concepts/pipelines"},{"type":"link","label":"Exhaust scripts","href":"/major-concepts/exhaust-script","docId":"major-concepts/exhaust-script"},{"type":"link","label":"Impact Engine (CLI)","href":"/major-concepts/if","docId":"major-concepts/if"},{"type":"link","label":"Manifest File","href":"/major-concepts/manifest-file","docId":"major-concepts/manifest-file"},{"type":"link","label":"Plugins","href":"/major-concepts/plugins","docId":"major-concepts/plugins"},{"type":"link","label":"Regroup","href":"/major-concepts/regroup","docId":"major-concepts/regroup"},{"type":"link","label":"Time","href":"/major-concepts/time","docId":"major-concepts/time"}],"href":"/major-concepts/"},{"type":"category","label":"Users","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Quick start","href":"/users/quick-start","docId":"users/quick-start"},{"type":"link","label":"How to install Impact Framework","href":"/users/how-to-install-if","docId":"users/how-to-install-if"},{"type":"link","label":"How to load plugins","href":"/users/how-to-import-plugins","docId":"users/how-to-import-plugins"},{"type":"link","label":"How to write a manifest file","href":"/users/how-to-write-manifests","docId":"users/how-to-write-manifests"},{"type":"link","label":"Exporting CSV file with `if-csv`","href":"/users/how-to-export-csv-file-with-if-csv","docId":"users/how-to-export-csv-file-with-if-csv"},{"type":"link","label":"How to compare files with `if-diff`","href":"/users/how-to-compare-files-with-if-diff","docId":"users/how-to-compare-files-with-if-diff"},{"type":"link","label":"Verifying IF outputs with `if-check`","href":"/users/how-to-verify-files-with-if-check","docId":"users/how-to-verify-files-with-if-check"},{"type":"link","label":"How to check parameters and units using `explainer`","href":"/users/how-to-use-the-explain-feature","docId":"users/how-to-use-the-explain-feature"}],"href":"/users/"},{"type":"category","label":"Developers","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"How to build plugins","href":"/developers/how-to-build-plugins","docId":"developers/how-to-build-plugins"},{"type":"link","label":"How to create an exhaust script","href":"/developers/how-to-create-exhaust-script","docId":"developers/how-to-create-exhaust-script"},{"type":"link","label":"How to make plugins production ready","href":"/developers/how-to-refine-plugins","docId":"developers/how-to-refine-plugins"},{"type":"link","label":"How to submit plugins","href":"/developers/how-to-submit-plugins","docId":"developers/how-to-submit-plugins"},{"type":"link","label":"How to write unit tests","href":"/developers/how-to-write-unit-tests","docId":"developers/how-to-write-unit-tests"}],"href":"/developers/"},{"type":"category","label":"Pipelines","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"From CPU utilization to carbon emissions","href":"/pipelines/cpu-to-carbon","docId":"pipelines/cpu-to-carbon"},{"type":"link","label":"Grabbing instance metadata from a CSV file","href":"/pipelines/instance-metadata","docId":"pipelines/instance-metadata"},{"type":"link","label":"Software Carbon Intensity (SCI)","href":"/pipelines/sci","docId":"pipelines/sci"}],"href":"/pipelines/"},{"type":"category","label":"Reference","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Command line tool","href":"/reference/cli","docId":"reference/cli"},{"type":"link","label":"Errors","href":"/reference/errors","docId":"reference/errors"},{"type":"link","label":"IF features","href":"/reference/features","docId":"reference/features"},{"type":"link","label":"Plugins","href":"/reference/plugins","docId":"reference/plugins"}],"href":"/reference/"},{"type":"link","label":"FAQs","href":"/FAQ","docId":"FAQ"}]},"docs":{"developers/how-to-build-plugins":{"id":"developers/how-to-build-plugins","title":"How to build plugins","description":"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.","sidebar":"tutorialSidebar"},"developers/how-to-create-exhaust-script":{"id":"developers/how-to-create-exhaust-script","title":"How to create an exhaust script","description":"The If framework outputs data in yaml format. Any other output formats require a separate script that takes the yaml output data and processes it. We provide if-csv for outputting data in csv format bundled with IF. For any other format, you need to write an exhaust script.","sidebar":"tutorialSidebar"},"developers/how-to-refine-plugins":{"id":"developers/how-to-refine-plugins","title":"How to make plugins production ready","description":"Our How to build plugins guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.","sidebar":"tutorialSidebar"},"developers/how-to-submit-plugins":{"id":"developers/how-to-submit-plugins","title":"How to submit plugins","description":"Once you have built a plugin and made it production ready you probably want to share it with the world!","sidebar":"tutorialSidebar"},"developers/how-to-write-unit-tests":{"id":"developers/how-to-write-unit-tests","title":"How to write unit tests","description":"Impact Framework unit tests follow a standard format. We use the jest testing library. You can run all our existing tests by opening the project directory and running npm test. This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap).","sidebar":"tutorialSidebar"},"developers/index":{"id":"developers/index","title":"Developers","description":"This section contains information for Impact Framework developers. You are a developer if you want to change or update the Impact Framework by adding new features, fixing bugs or building new plugins.","sidebar":"tutorialSidebar"},"FAQ":{"id":"FAQ","title":"FAQs","description":"How can I contribute to Impact framework?","sidebar":"tutorialSidebar"},"intro":{"id":"intro","title":"Introduction","description":"Impact Framework","sidebar":"tutorialSidebar"},"major-concepts/aggregation":{"id":"major-concepts/aggregation","title":"Aggregation","description":"Aggregation is the process of summarizing a set of metrics.","sidebar":"tutorialSidebar"},"major-concepts/design-philosophy":{"id":"major-concepts/design-philosophy","title":"Design philosophy","description":"Transparency","sidebar":"tutorialSidebar"},"major-concepts/exhaust-script":{"id":"major-concepts/exhaust-script","title":"Exhaust scripts","description":"Exhaust scripts are scripts that can run independently of IF itself that take an executed manifest file (one with outputs) as an input, parse the yaml data and reformat it into some other representation. We provide if-csv bundled with IF, but if you want other data formats, you\'ll have to create an exhaust script yourself.","sidebar":"tutorialSidebar"},"major-concepts/if":{"id":"major-concepts/if","title":"Impact Engine (CLI)","description":"Introduction","sidebar":"tutorialSidebar"},"major-concepts/index":{"id":"major-concepts/index","title":"Major Concepts","description":"Here you will find explanations for the fundamental Impact Framework concepts. This includes:","sidebar":"tutorialSidebar"},"major-concepts/manifest-file":{"id":"major-concepts/manifest-file","title":"Manifest File","description":"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:","sidebar":"tutorialSidebar"},"major-concepts/pipelines":{"id":"major-concepts/pipelines","title":"Pipelines","description":"Pipelines are chains of plugins that operate in sequence over the input data in your manifest file.","sidebar":"tutorialSidebar"},"major-concepts/plugins":{"id":"major-concepts/plugins","title":"Plugins","description":"Plugins are self-contained units of code that do one thing. They can be loaded into IF and chained together in a pipeline so that simple individual plugins can form a complicated procedure for computing a manifest file.","sidebar":"tutorialSidebar"},"major-concepts/regroup":{"id":"major-concepts/regroup","title":"Regroup","description":"Regroup is an IF feature that reorganizes a tree according to keys provided by the user. This allows users to regroup their observations according to various properties of their application. For example, the following manifest file contains a flat array of observations. This is how you might expect data to arrive from an importer plugin, maybe one that hits a metrics API for a cloud service.","sidebar":"tutorialSidebar"},"major-concepts/time":{"id":"major-concepts/time","title":"Time","description":"Every observation in an array of inputs represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:","sidebar":"tutorialSidebar"},"pipelines/cpu-to-carbon":{"id":"pipelines/cpu-to-carbon","title":"From CPU utilization to carbon emissions","description":"Tags","sidebar":"tutorialSidebar"},"pipelines/index":{"id":"pipelines/index","title":"Pipelines","description":"This section contains walkthrough guides for some common pipelines you may want to use in your manifest files.","sidebar":"tutorialSidebar"},"pipelines/instance-metadata":{"id":"pipelines/instance-metadata","title":"Grabbing instance metadata from a CSV file","description":"Observations","sidebar":"tutorialSidebar"},"pipelines/sci":{"id":"pipelines/sci","title":"Software Carbon Intensity (SCI)","description":"Description","sidebar":"tutorialSidebar"},"reference/cli":{"id":"reference/cli","title":"Command line tool","description":"A core feature of the Impact Framework is the if-run command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file.","sidebar":"tutorialSidebar"},"reference/errors":{"id":"reference/errors","title":"Errors","description":"IF defines a finite set of error classes. All error messages emitted by IF are attached to one of these classes.","sidebar":"tutorialSidebar"},"reference/features":{"id":"reference/features","title":"IF features","description":"This page simply lists the features of Impact Framework that are not plugins or CLI tools, along with a brief description, usage instruction and link to more detailed docs.","sidebar":"tutorialSidebar"},"reference/index":{"id":"reference/index","title":"Reference","description":"In this section you will find reference documentation for the core data structures and features used in the Impact Framework.","sidebar":"tutorialSidebar"},"reference/plugins":{"id":"reference/plugins","title":"Plugins","description":"Impact Framework works by executing pipelines of plugins over input data. Those plugins are re-useable units of code that can be thought of as Lego bricks - simple blocks of code that can be assembled into complex workflows.","sidebar":"tutorialSidebar"},"users/how-to-compare-files-with-if-diff":{"id":"users/how-to-compare-files-with-if-diff","title":"How to compare files with `if-diff`","description":"if-diff is a command line tool that allows you to compare two if-run output files. They either match according to if-diff\'s matching rules, or they don\'t. If they match, then if-diff returns a simple success response. If the differ, then if-diff returns a report of the differences it finds.","sidebar":"tutorialSidebar"},"users/how-to-export-csv-file-with-if-csv":{"id":"users/how-to-export-csv-file-with-if-csv","title":"Exporting CSV file with `if-csv`","description":"IF includes a command line tool called if-csv which is designed to export CSV files based on a specified manifest file and metric.","sidebar":"tutorialSidebar"},"users/how-to-import-plugins":{"id":"users/how-to-import-plugins","title":"How to load plugins","description":"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins come bundled with IF. These are known as builtins.","sidebar":"tutorialSidebar"},"users/how-to-install-if":{"id":"users/how-to-install-if","title":"How to install Impact Framework","description":"You can install Impact Framework either globally or locally. For most users, we recommend installing our official releases globally using npm. You can do this using the following command:","sidebar":"tutorialSidebar"},"users/how-to-use-the-explain-feature":{"id":"users/how-to-use-the-explain-feature","title":"How to check parameters and units using `explainer`","description":"Manifest files can get complicated, especially when there are many plugin instances initialized. It can be challenging to keep track of the flow of parameters and their units through a pipeline. To help manifest authors and auditors verify the correct flow of information through a pipeline, we provide the explainer feature.","sidebar":"tutorialSidebar"},"users/how-to-verify-files-with-if-check":{"id":"users/how-to-verify-files-with-if-check","title":"Verifying IF outputs with `if-check`","description":"IF includes a command line tool called if-check that can be used to verify the results in a manifest file.","sidebar":"tutorialSidebar"},"users/how-to-write-manifests":{"id":"users/how-to-write-manifests","title":"How to write a manifest file","description":"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app.","sidebar":"tutorialSidebar"},"users/index":{"id":"users/index","title":"Users","description":"This section contains information for Impact Framework users. You are a user if you want to apply the Impact Framework to your own use-case, such as using it to measure the environmental impact of your own apps running on some cloud platform.","sidebar":"tutorialSidebar"},"users/quick-start":{"id":"users/quick-start","title":"Quick start","description":"This page will provide the basic instructions for getting up and running with Impact Framework.","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.cf6c6ad2.js b/assets/js/935f2afb.cf6c6ad2.js new file mode 100644 index 00000000..12a5abf9 --- /dev/null +++ b/assets/js/935f2afb.cf6c6ad2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Introduction","href":"/intro","docId":"intro"},{"type":"category","label":"Major Concepts","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Design philosophy","href":"/major-concepts/design-philosophy","docId":"major-concepts/design-philosophy"},{"type":"link","label":"Aggregation","href":"/major-concepts/aggregation","docId":"major-concepts/aggregation"},{"type":"link","label":"Pipelines","href":"/major-concepts/pipelines","docId":"major-concepts/pipelines"},{"type":"link","label":"Exhaust scripts","href":"/major-concepts/exhaust-script","docId":"major-concepts/exhaust-script"},{"type":"link","label":"Impact Engine (CLI)","href":"/major-concepts/if","docId":"major-concepts/if"},{"type":"link","label":"Manifest File","href":"/major-concepts/manifest-file","docId":"major-concepts/manifest-file"},{"type":"link","label":"Plugins","href":"/major-concepts/plugins","docId":"major-concepts/plugins"},{"type":"link","label":"Regroup","href":"/major-concepts/regroup","docId":"major-concepts/regroup"},{"type":"link","label":"Time","href":"/major-concepts/time","docId":"major-concepts/time"}],"href":"/major-concepts/"},{"type":"category","label":"Users","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Quick start","href":"/users/quick-start","docId":"users/quick-start"},{"type":"link","label":"How to install Impact Framework","href":"/users/how-to-install-if","docId":"users/how-to-install-if"},{"type":"link","label":"How to load plugins","href":"/users/how-to-import-plugins","docId":"users/how-to-import-plugins"},{"type":"link","label":"How to write a manifest file","href":"/users/how-to-write-manifests","docId":"users/how-to-write-manifests"},{"type":"link","label":"Exporting CSV file with `if-csv`","href":"/users/how-to-export-csv-file-with-if-csv","docId":"users/how-to-export-csv-file-with-if-csv"},{"type":"link","label":"How to compare files with `if-diff`","href":"/users/how-to-compare-files-with-if-diff","docId":"users/how-to-compare-files-with-if-diff"},{"type":"link","label":"Verifying IF outputs with `if-check`","href":"/users/how-to-verify-files-with-if-check","docId":"users/how-to-verify-files-with-if-check"},{"type":"link","label":"How to check parameters and units using `explainer`","href":"/users/how-to-use-the-explain-feature","docId":"users/how-to-use-the-explain-feature"}],"href":"/users/"},{"type":"category","label":"Developers","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"How to build plugins","href":"/developers/how-to-build-plugins","docId":"developers/how-to-build-plugins"},{"type":"link","label":"How to create an exhaust script","href":"/developers/how-to-create-exhaust-script","docId":"developers/how-to-create-exhaust-script"},{"type":"link","label":"How to make plugins production ready","href":"/developers/how-to-refine-plugins","docId":"developers/how-to-refine-plugins"},{"type":"link","label":"How to submit plugins","href":"/developers/how-to-submit-plugins","docId":"developers/how-to-submit-plugins"},{"type":"link","label":"How to write unit tests","href":"/developers/how-to-write-unit-tests","docId":"developers/how-to-write-unit-tests"}],"href":"/developers/"},{"type":"category","label":"Pipelines","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"From CPU utilization to carbon emissions","href":"/pipelines/cpu-to-carbon","docId":"pipelines/cpu-to-carbon"},{"type":"link","label":"Teads CPU pipeline","href":"/pipelines/teads","docId":"pipelines/teads"},{"type":"link","label":"Grabbing instance metadata from a CSV file","href":"/pipelines/instance-metadata","docId":"pipelines/instance-metadata"},{"type":"link","label":"Software Carbon Intensity (SCI)","href":"/pipelines/sci","docId":"pipelines/sci"}],"href":"/pipelines/"},{"type":"category","label":"Reference","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Command line tool","href":"/reference/cli","docId":"reference/cli"},{"type":"link","label":"Errors","href":"/reference/errors","docId":"reference/errors"},{"type":"link","label":"IF features","href":"/reference/features","docId":"reference/features"},{"type":"link","label":"Plugins","href":"/reference/plugins","docId":"reference/plugins"}],"href":"/reference/"},{"type":"link","label":"FAQs","href":"/FAQ","docId":"FAQ"}]},"docs":{"developers/how-to-build-plugins":{"id":"developers/how-to-build-plugins","title":"How to build plugins","description":"The IF is designed to be as composable as possible. This means you can develop your own plugins and use them in a pipeline.","sidebar":"tutorialSidebar"},"developers/how-to-create-exhaust-script":{"id":"developers/how-to-create-exhaust-script","title":"How to create an exhaust script","description":"The If framework outputs data in yaml format. Any other output formats require a separate script that takes the yaml output data and processes it. We provide if-csv for outputting data in csv format bundled with IF. For any other format, you need to write an exhaust script.","sidebar":"tutorialSidebar"},"developers/how-to-refine-plugins":{"id":"developers/how-to-refine-plugins","title":"How to make plugins production ready","description":"Our How to build plugins guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.","sidebar":"tutorialSidebar"},"developers/how-to-submit-plugins":{"id":"developers/how-to-submit-plugins","title":"How to submit plugins","description":"Once you have built a plugin and made it production ready you probably want to share it with the world!","sidebar":"tutorialSidebar"},"developers/how-to-write-unit-tests":{"id":"developers/how-to-write-unit-tests","title":"How to write unit tests","description":"Impact Framework unit tests follow a standard format. We use the jest testing library. You can run all our existing tests by opening the project directory and running npm test. This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap).","sidebar":"tutorialSidebar"},"developers/index":{"id":"developers/index","title":"Developers","description":"This section contains information for Impact Framework developers. You are a developer if you want to change or update the Impact Framework by adding new features, fixing bugs or building new plugins.","sidebar":"tutorialSidebar"},"FAQ":{"id":"FAQ","title":"FAQs","description":"How can I contribute to Impact framework?","sidebar":"tutorialSidebar"},"intro":{"id":"intro","title":"Introduction","description":"Impact Framework","sidebar":"tutorialSidebar"},"major-concepts/aggregation":{"id":"major-concepts/aggregation","title":"Aggregation","description":"Aggregation is the process of summarizing a set of metrics.","sidebar":"tutorialSidebar"},"major-concepts/design-philosophy":{"id":"major-concepts/design-philosophy","title":"Design philosophy","description":"Transparency","sidebar":"tutorialSidebar"},"major-concepts/exhaust-script":{"id":"major-concepts/exhaust-script","title":"Exhaust scripts","description":"Exhaust scripts are scripts that can run independently of IF itself that take an executed manifest file (one with outputs) as an input, parse the yaml data and reformat it into some other representation. We provide if-csv bundled with IF, but if you want other data formats, you\'ll have to create an exhaust script yourself.","sidebar":"tutorialSidebar"},"major-concepts/if":{"id":"major-concepts/if","title":"Impact Engine (CLI)","description":"Introduction","sidebar":"tutorialSidebar"},"major-concepts/index":{"id":"major-concepts/index","title":"Major Concepts","description":"Here you will find explanations for the fundamental Impact Framework concepts. This includes:","sidebar":"tutorialSidebar"},"major-concepts/manifest-file":{"id":"major-concepts/manifest-file","title":"Manifest File","description":"Manifest files are fundamental to Impact Framework and they serve multiple important purposes, including:","sidebar":"tutorialSidebar"},"major-concepts/pipelines":{"id":"major-concepts/pipelines","title":"Pipelines","description":"Pipelines are chains of plugins that operate in sequence over the input data in your manifest file.","sidebar":"tutorialSidebar"},"major-concepts/plugins":{"id":"major-concepts/plugins","title":"Plugins","description":"Plugins are self-contained units of code that do one thing. They can be loaded into IF and chained together in a pipeline so that simple individual plugins can form a complicated procedure for computing a manifest file.","sidebar":"tutorialSidebar"},"major-concepts/regroup":{"id":"major-concepts/regroup","title":"Regroup","description":"Regroup is an IF feature that reorganizes a tree according to keys provided by the user. This allows users to regroup their observations according to various properties of their application. For example, the following manifest file contains a flat array of observations. This is how you might expect data to arrive from an importer plugin, maybe one that hits a metrics API for a cloud service.","sidebar":"tutorialSidebar"},"major-concepts/time":{"id":"major-concepts/time","title":"Time","description":"Every observation in an array of inputs represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:","sidebar":"tutorialSidebar"},"pipelines/cpu-to-carbon":{"id":"pipelines/cpu-to-carbon","title":"From CPU utilization to carbon emissions","description":"Tags","sidebar":"tutorialSidebar"},"pipelines/index":{"id":"pipelines/index","title":"Pipelines","description":"This section contains walkthrough guides for some common pipelines you may want to use in your manifest files.","sidebar":"tutorialSidebar"},"pipelines/instance-metadata":{"id":"pipelines/instance-metadata","title":"Grabbing instance metadata from a CSV file","description":"Observations","sidebar":"tutorialSidebar"},"pipelines/sci":{"id":"pipelines/sci","title":"Software Carbon Intensity (SCI)","description":"Description","sidebar":"tutorialSidebar"},"pipelines/teads":{"id":"pipelines/teads","title":"Teads CPU pipeline","description":"The Teads CPU power curve CPU utilization (as a percentage) against a scaling factor that can be applied to the CPUs thermal design power to estimate the power drawn by the CPU in Watts.","sidebar":"tutorialSidebar"},"reference/cli":{"id":"reference/cli","title":"Command line tool","description":"A core feature of the Impact Framework is the if-run command line tool (CLI). This is how you trigger Impact Framework to execute a certain manifest file.","sidebar":"tutorialSidebar"},"reference/errors":{"id":"reference/errors","title":"Errors","description":"IF defines a finite set of error classes. All error messages emitted by IF are attached to one of these classes.","sidebar":"tutorialSidebar"},"reference/features":{"id":"reference/features","title":"IF features","description":"This page simply lists the features of Impact Framework that are not plugins or CLI tools, along with a brief description, usage instruction and link to more detailed docs.","sidebar":"tutorialSidebar"},"reference/index":{"id":"reference/index","title":"Reference","description":"In this section you will find reference documentation for the core data structures and features used in the Impact Framework.","sidebar":"tutorialSidebar"},"reference/plugins":{"id":"reference/plugins","title":"Plugins","description":"Impact Framework works by executing pipelines of plugins over input data. Those plugins are re-useable units of code that can be thought of as Lego bricks - simple blocks of code that can be assembled into complex workflows.","sidebar":"tutorialSidebar"},"users/how-to-compare-files-with-if-diff":{"id":"users/how-to-compare-files-with-if-diff","title":"How to compare files with `if-diff`","description":"if-diff is a command line tool that allows you to compare two if-run output files. They either match according to if-diff\'s matching rules, or they don\'t. If they match, then if-diff returns a simple success response. If the differ, then if-diff returns a report of the differences it finds.","sidebar":"tutorialSidebar"},"users/how-to-export-csv-file-with-if-csv":{"id":"users/how-to-export-csv-file-with-if-csv","title":"Exporting CSV file with `if-csv`","description":"IF includes a command line tool called if-csv which is designed to export CSV files based on a specified manifest file and metric.","sidebar":"tutorialSidebar"},"users/how-to-import-plugins":{"id":"users/how-to-import-plugins","title":"How to load plugins","description":"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins come bundled with IF. These are known as builtins.","sidebar":"tutorialSidebar"},"users/how-to-install-if":{"id":"users/how-to-install-if","title":"How to install Impact Framework","description":"You can install Impact Framework either globally or locally. For most users, we recommend installing our official releases globally using npm. You can do this using the following command:","sidebar":"tutorialSidebar"},"users/how-to-use-the-explain-feature":{"id":"users/how-to-use-the-explain-feature","title":"How to check parameters and units using `explainer`","description":"Manifest files can get complicated, especially when there are many plugin instances initialized. It can be challenging to keep track of the flow of parameters and their units through a pipeline. To help manifest authors and auditors verify the correct flow of information through a pipeline, we provide the explainer feature.","sidebar":"tutorialSidebar"},"users/how-to-verify-files-with-if-check":{"id":"users/how-to-verify-files-with-if-check","title":"Verifying IF outputs with `if-check`","description":"IF includes a command line tool called if-check that can be used to verify the results in a manifest file.","sidebar":"tutorialSidebar"},"users/how-to-write-manifests":{"id":"users/how-to-write-manifests","title":"How to write a manifest file","description":"The Impact Framework receives all its configuration and input data in the form of a manifest file known as an manifest. To use the framework, you will need to write a manifest file and pass its path to the command line tool. This guide will help you to understand how to construct one of these files and use it to measure the energy and carbon usage of your app.","sidebar":"tutorialSidebar"},"users/index":{"id":"users/index","title":"Users","description":"This section contains information for Impact Framework users. You are a user if you want to apply the Impact Framework to your own use-case, such as using it to measure the environmental impact of your own apps running on some cloud platform.","sidebar":"tutorialSidebar"},"users/quick-start":{"id":"users/quick-start","title":"Quick start","description":"This page will provide the basic instructions for getting up and running with Impact Framework.","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/93f138be.a9252a89.js b/assets/js/93f138be.a9252a89.js new file mode 100644 index 00000000..f5daf7d0 --- /dev/null +++ b/assets/js/93f138be.a9252a89.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[713],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=i,f=d["".concat(l,".").concat(m)]||d[m]||c[m]||o;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var r=n(7462),i=(n(7294),n(4137));const o={"sidebar-position":3},a="How to write unit tests",s={unversionedId:"developers/how-to-write-unit-tests",id:"developers/how-to-write-unit-tests",title:"How to write unit tests",description:"Impact Framework unit tests follow a standard format. We use the jest testing library. You can run all our existing tests by opening the project directory and running npm test. This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap).",source:"@site/docs/developers/how-to-write-unit-tests.md",sourceDirName:"developers",slug:"/developers/how-to-write-unit-tests",permalink:"/developers/how-to-write-unit-tests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-write-unit-tests.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"How to submit plugins",permalink:"/developers/how-to-submit-plugins"},next:{title:"Pipelines",permalink:"/pipelines/"}},l={},p=[{value:"Test files",id:"test-files",level:2},{value:"Setting up your test file",id:"setting-up-your-test-file",level:2},{value:"Describe",id:"describe",level:2},{value:"It",id:"it",level:2},{value:"Errors",id:"errors",level:2},{value:"Mocks",id:"mocks",level:2},{value:"Coverage",id:"coverage",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-write-unit-tests"},"How to write unit tests"),(0,i.kt)("p",null,"Impact Framework unit tests follow a standard format. We use the ",(0,i.kt)("inlineCode",{parentName:"p"},"jest")," testing library. You can run all our existing tests by opening the project directory and running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm test"),". This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap)."),(0,i.kt)("h2",{id:"test-files"},"Test files"),(0,i.kt)("p",null,"The IF includes a ",(0,i.kt)("inlineCode",{parentName:"p"},"__test__")," directory. Inside, you will find subdirectory ",(0,i.kt)("inlineCode",{parentName:"p"},"if-run/builtins")," containing test files for each plugin. Your plugin repository should also follow this structure. Inside the ",(0,i.kt)("inlineCode",{parentName:"p"},"builtins")," you can add ",(0,i.kt)("inlineCode",{parentName:"p"},"plugin.test.ts"),". This is where you write your unit tests. For example, here's the directory tree for our ",(0,i.kt)("inlineCode",{parentName:"p"},"sum")," test file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"\nif\n |\n |- src\n |\n |-__tests__\n |\n |-if-run\n |\n |-builtins\n |\n sum.test.ts\n")),(0,i.kt)("h2",{id:"setting-up-your-test-file"},"Setting up your test file"),(0,i.kt)("p",null,"You will need to import your plugin so that it can be instantiated and tested. You will also need some elements from ",(0,i.kt)("inlineCode",{parentName:"p"},"jest/globals"),":\nFor example, these are the imports for our ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import { ERRORS } from '@grnsft/if-core/utils';\n\nimport { Sum } from '../../../if-run/builtins/sum';\n\nconst { InputValidationError } = ERRORS;\n")),(0,i.kt)("p",null,"You may require other imports for your specific set of tests."),(0,i.kt)("h2",{id:"describe"},"Describe"),(0,i.kt)("p",null,"Each method should have its own dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block."),(0,i.kt)("p",null,"Your unit tests should have ",(0,i.kt)("em",{parentName:"p"},"at least")," two ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," blocks, one to test the plugin initialization and one for ",(0,i.kt)("inlineCode",{parentName:"p"},"execute"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"describe('init', () => {});\ndescribe('execute', () => {});\n")),(0,i.kt)("p",null,"For example, here is a describe block checking that the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin initializes correctly:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"describe('builtins/sum: ', () => {\n describe('Sum: ', () => {\n const config = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(config);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n });\n});\n")),(0,i.kt)("h2",{id:"it"},"It"),(0,i.kt)("p",null,"Within each ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block, each effect to be tested should have a dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," block."),(0,i.kt)("p",null,"Here's an example of a new ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method on the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin. The ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block indicates that we are testing effects of the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method. ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," is specific to a single outcome - in this case there are two ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," blocks that test that the plugin returns a specific result in the happy path and throws an exception if the user has provided invalid config data, specifically that the user-provided ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," parameter is missing:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n\n it('throws an error on missing params in input.', async () => {\n const expectedMessage = 'Sum: cpu/energy is missing from the input array.';\n\n expect.assertions(1);\n\n try {\n await sum.execute([\n {\n duration: 3600,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n } catch (error) {\n expect(error).toStrictEqual(new InputValidationError(expectedMessage));\n }\n });\n});\n")),(0,i.kt)("h2",{id:"errors"},"Errors"),(0,i.kt)("p",null,"We prefer to use ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," to check the errors returned from a test. We do this by writing ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," in a ",(0,i.kt)("inlineCode",{parentName:"p"},"catch")," block. Here's an example from our ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," plugin tests:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"it('throws an exception on missing functional unit data.', async () => {\n const inputs = [\n {\n timestamp: '2021-01-01T00:00:00Z',\n 'operational-carbon': 0.002,\n 'embodied-carbon': 0.0005,\n 'functional-unit': 'requests',\n duration: 1,\n },\n ];\n expect.assertions(1);\n\n try {\n await sciModel.execute(inputs);\n } catch (error) {\n expect(error).toBeInstanceOf(InputValidationError);\n }\n});\n")),(0,i.kt)("p",null,"It is also necessary to include ",(0,i.kt)("inlineCode",{parentName:"p"},"expect.assertions(n)")," for testing asynchronous code, where ",(0,i.kt)("inlineCode",{parentName:"p"},"n")," is the number of assertiosn that should be tested before the test completes."),(0,i.kt)("h2",{id:"mocks"},"Mocks"),(0,i.kt)("p",null,"Please try to avoid mocking data if possible. However, if it is necessary to mock (e.g. if your plugin relies on a third party credentialed API) then please make your mock data as realistic as possible (no ",(0,i.kt)("inlineCode",{parentName:"p"},"foo"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"bar"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"baz")," style mock data, please)."),(0,i.kt)("p",null,"We do have mock backends in several of our tests, and we also have a mock data generator plugin that can create realistic dummy data to your specific requirements."),(0,i.kt)("h2",{id:"coverage"},"Coverage"),(0,i.kt)("p",null,"Please use ",(0,i.kt)("inlineCode",{parentName:"p"},"jest --coverage")," to see a coverage report for your plugin - your unit tests should yield 100% coverage. The snippet below shows what to expect from the coverage report:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"------------------------------------|---------|----------|---------|---------|-------------------\n| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n| --------------------------------- | ------- | -------- | ------- | ------- | ----------------- |\n| All files | 100| 100 | 100 | 100 |\n| if-run/builtins/coefficient | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| if-run/builtins/copy-param | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| if-run/builtins/csv-lookup | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| if-run/builtins/divide | 100 | 94.11 | 100 | 100 |\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/93f138be.febc7216.js b/assets/js/93f138be.febc7216.js deleted file mode 100644 index f4977a3c..00000000 --- a/assets/js/93f138be.febc7216.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[713],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>m});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),p=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=p(e.components);return r.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=i,f=d["".concat(l,".").concat(m)]||d[m]||c[m]||o;return n?r.createElement(f,a(a({ref:t},u),{},{components:n})):r.createElement(f,a({ref:t},u))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:i,a[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var r=n(7462),i=(n(7294),n(4137));const o={"sidebar-position":3},a="How to write unit tests",s={unversionedId:"developers/how-to-write-unit-tests",id:"developers/how-to-write-unit-tests",title:"How to write unit tests",description:"Impact Framework unit tests follow a standard format. We use the jest testing library. You can run all our existing tests by opening the project directory and running npm test. This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap).",source:"@site/docs/developers/how-to-write-unit-tests.md",sourceDirName:"developers",slug:"/developers/how-to-write-unit-tests",permalink:"/developers/how-to-write-unit-tests",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/developers/how-to-write-unit-tests.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"How to submit plugins",permalink:"/developers/how-to-submit-plugins"},next:{title:"Pipelines",permalink:"/pipelines/"}},l={},p=[{value:"Test files",id:"test-files",level:2},{value:"Setting up your test file",id:"setting-up-your-test-file",level:2},{value:"Describe",id:"describe",level:2},{value:"It",id:"it",level:2},{value:"Errors",id:"errors",level:2},{value:"Mocks",id:"mocks",level:2},{value:"Coverage",id:"coverage",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-write-unit-tests"},"How to write unit tests"),(0,i.kt)("p",null,"Impact Framework unit tests follow a standard format. We use the ",(0,i.kt)("inlineCode",{parentName:"p"},"jest")," testing library. You can run all our existing tests by opening the project directory and running ",(0,i.kt)("inlineCode",{parentName:"p"},"npm test"),". This page explains how you can add new unit tests for your plugins (or add some for our plugins if you notice a gap)."),(0,i.kt)("h2",{id:"test-files"},"Test files"),(0,i.kt)("p",null,"Both the IF and the project repositories include a ",(0,i.kt)("inlineCode",{parentName:"p"},"__test__")," directory. Inside, you will find subdirectory ",(0,i.kt)("inlineCode",{parentName:"p"},"unit/lib")," containing directories for each plugin. Your plugin repository should also follow this structure. Inside the plugin directory you can add ",(0,i.kt)("inlineCode",{parentName:"p"},"index.test.ts"),". This is where you write your unit tests. For example, here's the directory tree for our ",(0,i.kt)("inlineCode",{parentName:"p"},"teads-curve")," test file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"\nif-unofficial-plugins\n |\n |- src\n |\n |-__tests__\n |\n |-unit\n |\n |-lib\n |\n teads-curve\n |\n |- index.test.ts\n")),(0,i.kt)("h2",{id:"setting-up-your-test-file"},"Setting up your test file"),(0,i.kt)("p",null,"You will need to import your plugin so that it can be instantiated and tested. You will also need some elements from ",(0,i.kt)("inlineCode",{parentName:"p"},"jest/globals"),":\nFor example, these are the imports for our ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import {Sum} from '../../../../lib';\nimport {ERRORS} from '../../../../util/errors';\nconst {InputValidationError} = ERRORS;\n")),(0,i.kt)("p",null,"You may require other imports for your specific set of tests."),(0,i.kt)("h2",{id:"describe"},"Describe"),(0,i.kt)("p",null,"Each method should have its own dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block. "),(0,i.kt)("p",null,"Your unit tests should have ",(0,i.kt)("em",{parentName:"p"},"at least")," two ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," blocks, one to test the plugin initialization and one for ",(0,i.kt)("inlineCode",{parentName:"p"},"execute"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},'describe("init", ()=> {})\ndescribe("execute", ()=> {})\n')),(0,i.kt)("p",null,"For example, here is a describe block checking that the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin initializes correctly:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"},"describe('lib/sum: ', () => {\n describe('Sum: ', () => {\n const globalConfig = {\n 'input-parameters': ['cpu/energy', 'network/energy', 'memory/energy'],\n 'output-parameter': 'energy',\n };\n const sum = Sum(globalConfig);\n\n describe('init: ', () => {\n it('successfully initalized.', () => {\n expect(sum).toHaveProperty('metadata');\n expect(sum).toHaveProperty('execute');\n });\n });\n })\n})\n")),(0,i.kt)("h2",{id:"it"},"It"),(0,i.kt)("p",null,"Within each ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block, each effect to be tested should have a dedicated ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," block."),(0,i.kt)("p",null,"Here's an example of a new ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block for the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method on the ",(0,i.kt)("inlineCode",{parentName:"p"},"Sum")," plugin. The ",(0,i.kt)("inlineCode",{parentName:"p"},"describe")," block indicates that we are testing effects of the ",(0,i.kt)("inlineCode",{parentName:"p"},"execute()")," method. ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," is specific to a single outcome - in this case there are two ",(0,i.kt)("inlineCode",{parentName:"p"},"it")," blocks that test that the plugin returns a specific result in the happy path and throws an exception if the user has provided invalid config data, specifically that the user-provided ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/energy")," parameter is missing:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-typescript"}," describe('execute(): ', () => {\n it('successfully applies Sum strategy to given input.', async () => {\n expect.assertions(1);\n\n const expectedResult = [\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n energy: 3,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ];\n\n const result = await sum.execute([\n {\n duration: 3600,\n 'cpu/energy': 1,\n 'network/energy': 1,\n 'memory/energy': 1,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n\n expect(result).toStrictEqual(expectedResult);\n });\n\n it('throws an error on missing params in input.', async () => {\n const expectedMessage =\n 'Sum: cpu/energy is missing from the input array.';\n\n expect.assertions(1);\n\n try {\n await sum.execute([\n {\n duration: 3600,\n timestamp: '2021-01-01T00:00:00Z',\n },\n ]);\n } catch (error) {\n expect(error).toStrictEqual(\n new InputValidationError(expectedMessage)\n );\n }\n });\n })\n")),(0,i.kt)("h2",{id:"errors"},"Errors"),(0,i.kt)("p",null,"We prefer to use ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," to check the errors returned from a test. We do this by writing ",(0,i.kt)("inlineCode",{parentName:"p"},"expect")," in a ",(0,i.kt)("inlineCode",{parentName:"p"},"catch")," block. Here's an example from our ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," plugin tests:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"it('throws an exception on missing functional unit data.', async () => {\n const inputs = [\n {\n timestamp: '2021-01-01T00:00:00Z',\n 'operational-carbon': 0.002,\n 'embodied-carbon': 0.0005,\n 'functional-unit': 'requests',\n duration: 1,\n },\n ];\n expect.assertions(1);\n\n try {\n await sciModel.execute(inputs);\n } catch (error) {\n expect(error).toBeInstanceOf(InputValidationError);\n }\n});\n")),(0,i.kt)("p",null,"It is also necessary to include ",(0,i.kt)("inlineCode",{parentName:"p"},"expect.assertions(n)")," for testing asynchronous code, where ",(0,i.kt)("inlineCode",{parentName:"p"},"n")," is the number of assertiosn that should be tested before the test completes. "),(0,i.kt)("h2",{id:"mocks"},"Mocks"),(0,i.kt)("p",null,"Please try to avoid mocking data if possible. However, if it is necessary to mock (e.g. if your plugin relies on a third party credentialed API) then please make your mock data as realistic as possible (no ",(0,i.kt)("inlineCode",{parentName:"p"},"foo"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"bar"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"baz")," style mock data, please)."),(0,i.kt)("p",null,"We do have mock backends in several of our tests, and we also have a mock data generator plugin that can create realistic dummy data to your specific requirements."),(0,i.kt)("h2",{id:"coverage"},"Coverage"),(0,i.kt)("p",null,"Please use ",(0,i.kt)("inlineCode",{parentName:"p"},"jest --coverage")," to see a coverage report for your plugin - your unit tests should yield 100% coverage. The snippet below shows what to expect from the coverage report:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"-------------------------------|---------|----------|---------|---------|-------------------\n| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n| --------------------------- | ------- | -------- | ------- | ------- | ----------------- |\n| All files | 100 | 100 | 100 | 100 |\n| lib | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/cloud-metadata | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/e-mem | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n| lib/e-net | 100 | 100 | 100 | 100 |\n| index.ts | 100 | 100 | 100 | 100 |\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/95ad2285.13354aaf.js b/assets/js/95ad2285.13354aaf.js new file mode 100644 index 00000000..607a6ee6 --- /dev/null +++ b/assets/js/95ad2285.13354aaf.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[95],{4137:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>m});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function r(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=a.createContext({}),u=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},s=function(e){var t=u(e.components);return a.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,s=p(e,["components","mdxType","originalType","parentName"]),d=u(n),m=i,h=d["".concat(l,".").concat(m)]||d[m]||c[m]||o;return n?a.createElement(h,r(r({ref:t},s),{},{components:n})):a.createElement(h,r({ref:t},s))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,r=new Array(o);r[0]=d;var p={};for(var l in t)hasOwnProperty.call(t,l)&&(p[l]=t[l]);p.originalType=e,p.mdxType="string"==typeof e?e:i,r[1]=p;for(var u=2;u{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>p,toc:()=>u});var a=n(7462),i=(n(7294),n(4137));const o={sidebar_position:2},r="Teads CPU pipeline",p={unversionedId:"pipelines/teads",id:"pipelines/teads",title:"Teads CPU pipeline",description:"The Teads CPU power curve CPU utilization (as a percentage) against a scaling factor that can be applied to the CPUs thermal design power to estimate the power drawn by the CPU in Watts.",source:"@site/docs/pipelines/teads.md",sourceDirName:"pipelines",slug:"/pipelines/teads",permalink:"/pipelines/teads",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/teads.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"From CPU utilization to carbon emissions",permalink:"/pipelines/cpu-to-carbon"},next:{title:"Grabbing instance metadata from a CSV file",permalink:"/pipelines/instance-metadata"}},l={},u=[{value:"Impact Framework implementation",id:"impact-framework-implementation",level:2},{value:"Step 1: measure CPU utilization",id:"step-1-measure-cpu-utilization",level:3},{value:"Step 2: Determine the thermal design power of your processor",id:"step-2-determine-the-thermal-design-power-of-your-processor",level:3},{value:"Step 3: Interpolate the Teads curve",id:"step-3-interpolate-the-teads-curve",level:3},{value:"Step 4: Convert CPU factor to power",id:"step-4-convert-cpu-factor-to-power",level:3},{value:"Step 5: Convert wattage to energy",id:"step-5-convert-wattage-to-energy",level:3},{value:"Step 6: Scale the energy by the allocated CPUs",id:"step-6-scale-the-energy-by-the-allocated-cpus",level:3},{value:"Step 7: Define your pipeline",id:"step-7-define-your-pipeline",level:3},{value:"Running the manifest",id:"running-the-manifest",level:2}],s={toc:u};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"teads-cpu-pipeline"},"Teads CPU pipeline"),(0,i.kt)("p",null,"The Teads CPU power curve CPU utilization (as a percentage) against a scaling factor that can be applied to the CPUs thermal design power to estimate the power drawn by the CPU in Watts."),(0,i.kt)("p",null,"The research underpinning the curve was summarized in a pair of blog posts:"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"TEADS Engineering: Buildiong an AWS EC2 Carbon Emissions Dataset"),"\n",(0,i.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/estimating-aws-ec2-instances-power-consumption-c9745e347959"},"Teads Engineering: Estimating AWS EC2 Instances Power Consumption")),(0,i.kt)("p",null,"The curve has become very widely used as a general purpose utilization-to-wattage converter for CPUs, despite the fact that it does not geenralize well."),(0,i.kt)("p",null,"The wattage can be transformed into energy by doing the following:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Measure your CPU utilization"),(0,i.kt)("li",{parentName:"ol"},"Determine the thermal design power of your processor"),(0,i.kt)("li",{parentName:"ol"},"Determine the scaling factor for your CPU utilization by interpolating the Teads curve"),(0,i.kt)("li",{parentName:"ol"},"Determine the power drawn by your CPU by multiplying your scaling factor by the CPU's thermal design power"),(0,i.kt)("li",{parentName:"ol"},"Perform a unit conversion to convert power in Watts to energy in kwH"),(0,i.kt)("li",{parentName:"ol"},"Scale the energy estimated for the entire chip to the portion of the chip that is actually in use.")),(0,i.kt)("p",null,"These steps can be executed in IF using just three plugins:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Interpolate")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Multiply")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Divide"))),(0,i.kt)("p",null,"We'll go through each step in the energy estimate and examine how to implement it in a manifest file using IF's standard library of ",(0,i.kt)("inlineCode",{parentName:"p"},"builtin"),"s."),(0,i.kt)("h2",{id:"impact-framework-implementation"},"Impact Framework implementation"),(0,i.kt)("p",null,"First, create a manifest file and add this following boilerplate code:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: carbon-intensity plugin demo\ndescription:\ntags:\ninitialize:\n plugins:\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n defaults:\n inputs:\n")),(0,i.kt)("p",null,"If this structure looks unfamiliar to you, you can go back to our ",(0,i.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"manifests page"),"."),(0,i.kt)("h3",{id:"step-1-measure-cpu-utilization"},"Step 1: measure CPU utilization"),(0,i.kt)("p",null,"The first step was to measure your CPU utilization. In real use cases you would typoically do this using an importer plugin that grabs data from a monitor API or similar. However, for this example we will just manually create some dummy data. Add some timestamps, durations and cpu/utilization data to your ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array, as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: teads demo\ndescription:\ntags:\ninitialize:\n plugins:\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n defaults:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n")),(0,i.kt)("h3",{id:"step-2-determine-the-thermal-design-power-of-your-processor"},"Step 2: Determine the thermal design power of your processor"),(0,i.kt)("p",null,"Typically determinign the TDP of your processor would be done using a CSV lookup. We have a pipeline example for ",(0,i.kt)("a",{parentName:"p",href:"./tdp-finder.md"},"tdp-finder")," in these docs - combining this pipeline with the ",(0,i.kt)("inlineCode",{parentName:"p"},"tdp-finder")," pipeline would eb a great follow on exercise after you have finished this tutorial. Foir now, we will just hartd code some TDP data into your manifest so we can focus on the CPU utilization to energy calculations. Add ",(0,i.kt)("inlineCode",{parentName:"p"},"thermal-design-power")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"defaults")," - this is a shortcut to providing it in every timestep in your ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"default:\n thermal-design-power: 100\n")),(0,i.kt)("h3",{id:"step-3-interpolate-the-teads-curve"},"Step 3: Interpolate the Teads curve"),(0,i.kt)("p",null,"The Teads curve has CPU utilization ont he ",(0,i.kt)("inlineCode",{parentName:"p"},"x")," axis and a scaling factor on the ",(0,i.kt)("inlineCode",{parentName:"p"},"y")," axis. There are only four points on the published curve. Your task is to get the scaling factor for your specific CPU utilization values by interpolating between the known points. Luckily, we have a ",(0,i.kt)("inlineCode",{parentName:"p"},"builtin")," for that purpose!"),(0,i.kt)("p",null,"Add the ",(0,i.kt)("inlineCode",{parentName:"p"},"Interpolation")," plugin to your list of plugins in the ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n interpolate:\n method Interpolation\n path: builtin\n")),(0,i.kt)("p",null,"The details about the interpolation you want to do and the values to return are configured in the ",(0,i.kt)("inlineCode",{parentName:"p"},"config")," whoch is also added int he ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize block"),". Specifically, you have to provide the known points of the curve you want to interpolate, the ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameter")," (which is the ",(0,i.kt)("inlineCode",{parentName:"p"},"x")," value whose correspondiong ",(0,i.kt)("inlineCode",{parentName:"p"},"y")," value you want to find out, i.e. your CPU utilization value) and the ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," (the name you want to give to your retrieved ",(0,i.kt)("inlineCode",{parentName:"p"},"y")," value)."),(0,i.kt)("p",null,"You want to interpolate the Teads curve, so you can provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"x")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"y")," values obtained from the articles linked in the introduction section above:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"x: [0, 10, 50, 100]\ny: [0.12, 0.32, 0.75, 1.02]\n")),(0,i.kt)("p",null,"Your ",(0,i.kt)("inlineCode",{parentName:"p"},"input-parameter")," is your ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu/utilization")," and we'll name give the ",(0,i.kt)("inlineCode",{parentName:"p"},"output-parameter")," the name ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-factor"),"."),(0,i.kt)("p",null,"Your compelted ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block for ",(0,i.kt)("inlineCode",{parentName:"p"},"interpolate")," should look as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"interpolate:\n method: Interpolation\n path: 'builtin'\n config:\n method: linear\n x: [0, 10, 50, 100]\n y: [0.12, 0.32, 0.75, 1.02]\n input-parameter: 'cpu/utilization'\n output-parameter: 'cpu-factor'\n")),(0,i.kt)("h3",{id:"step-4-convert-cpu-factor-to-power"},"Step 4: Convert CPU factor to power"),(0,i.kt)("p",null,"The interpoaltion only gave use the scaling factor; we need to apply that scaling factor to the processor's TDP to get the power drawn by the CPU at your specific CPU utilization."),(0,i.kt)("p",null,"To do this, we can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"Multiply")," plugin in the IF standard library. We'll give the instance of ",(0,i.kt)("inlineCode",{parentName:"p"},"Multiply")," the name ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-factor-to-wattage")," and int he ",(0,i.kt)("inlineCode",{parentName:"p"},"config")," we'll define ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-factor")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"thermal-design-power")," as the two elements in our ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," array that we want to multiply together. Then we'll name the result ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-wattage"),":"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"cpu-factor-to-wattage:\n method: Multiply\n path: builtin\n config:\n input-parameters: ['cpu-factor', 'thermal-design-power']\n output-parameter: 'cpu-wattage'\n")),(0,i.kt)("p",null,"Add this to your ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block."),(0,i.kt)("h3",{id:"step-5-convert-wattage-to-energy"},"Step 5: Convert wattage to energy"),(0,i.kt)("p",null,"Next we have to perform some unit conversions. Wattage is a measure of power (energy over time). To convert to energy, we can first multiply by the number of seconds our observation covers (",(0,i.kt)("inlineCode",{parentName:"p"},"duration"),") to yield energy in joules. Then, convert to kWh by applying a scaling factor that takes seconds to hours and watts to kilowatts."),(0,i.kt)("p",null,"You can do this in two steps: the first uses another instance of ",(0,i.kt)("inlineCode",{parentName:"p"},"Multiply")," an the second uses ",(0,i.kt)("inlineCode",{parentName:"p"},"Divide"),":"),(0,i.kt)("p",null,"To do the initial multiplication of the CPU wattage and the observation duration, add the following config to your ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"wattage-times-duration:\n method: Multiply\n path: builtin\n config:\n input-parameters: ['cpu-wattage', 'duration']\n output-parameter: 'cpu-wattage-times-duration'\n")),(0,i.kt)("p",null,"next, use the ",(0,i.kt)("inlineCode",{parentName:"p"},"Divide")," plugin to do the unit conversion:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"wattage-to-energy-kwh:\n method: Divide\n path: 'builtin'\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n")),(0,i.kt)("h3",{id:"step-6-scale-the-energy-by-the-allocated-cpus"},"Step 6: Scale the energy by the allocated CPUs"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy-raw")," value you just configured is for the entire chip. But your application probably doesn't use the entire chip. Chances are you have some number of VCPUs allocated to you that is less than the total available. So you can scale your energy estimate by the ratio of VCPUs allocated to VCPUS available."),(0,i.kt)("p",null,"Let's assume you know the number of VCPUs allocated and available in advance and that they are the same in every timestep. In this case, you can just add the values to ",(0,i.kt)("inlineCode",{parentName:"p"},"defaults")," so they become available in every timestep, just as you did with ",(0,i.kt)("inlineCode",{parentName:"p"},"thermal-design-power"),"."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"defaults:\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n")),(0,i.kt)("p",null,"You need one instance of ",(0,i.kt)("inlineCode",{parentName:"p"},"Divide")," to calculate the ",(0,i.kt)("inlineCode",{parentName:"p"},"vcpu-ratio")," and another to apply that ",(0,i.kt)("inlineCode",{parentName:"p"},"vcpu-ratio")," to your ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy-raw")," value and yield your final result: ",(0,i.kt)("inlineCode",{parentName:"p"},"cpu-energy-kwh"),". Add the following to your ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block to achieve those steps:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"calculate-vcpu-ratio:\n method: Divide\n path: 'builtin'\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\ncorrect-cpu-energy-for-vcpu-ratio:\n method: Divide\n path: 'builtin'\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu-energy-kwh\n")),(0,i.kt)("h3",{id:"step-7-define-your-pipeline"},"Step 7: Define your pipeline"),(0,i.kt)("p",null,"Now you have configured all your plugins, covering all the stages of the calculation, you can simple define them in order in the ",(0,i.kt)("inlineCode",{parentName:"p"},"pipeline")," section of your manifest, as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"tree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n")),(0,i.kt)("p",null,"You also need to add some input data that your pipeline can operate over."),(0,i.kt)("p",null,"You can see the full manifest in the ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/blob/main/manifests/examples/teads-curve.yml"},"IF repository"),"."),(0,i.kt)("p",null,"That's it! Your manifest is ready to run!"),(0,i.kt)("h2",{id:"running-the-manifest"},"Running the manifest"),(0,i.kt)("p",null,"Having saved your manifest as ",(0,i.kt)("inlineCode",{parentName:"p"},"teads-curve.yaml")," you can run it using IF:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m teads-curve.yml -o teads-output.yml\n")),(0,i.kt)("p",null,"This will yield the following output file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: teads curve demo\ndescription: null\ntags: null\ninitialize:\n plugins:\n interpolate:\n path: builtin\n method: Interpolation\n config:\n method: linear\n x:\n - 0\n - 10\n - 50\n - 100\n 'y':\n - 0.12\n - 0.32\n - 0.75\n - 1.02\n input-parameter: cpu/utilization\n output-parameter: cpu-factor\n cpu-factor-to-wattage:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-factor\n - thermal-design-power\n output-parameter: cpu-wattage\n wattage-times-duration:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-wattage\n - duration\n output-parameter: cpu-wattage-times-duration\n wattage-to-energy-kwh:\n path: builtin\n method: Divide\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n calculate-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n correct-cpu-energy-for-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu-energy-kwh\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/if/src/index.ts -m manifests/examples/teads-curve.yml\n environment:\n if-version: 0.6.0\n os: macOS\n os-version: 14.6.1\n node-version: 18.20.4\n date-time: 2024-10-03T15:05:11.948Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.25'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.23.8\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n defaults:\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 0.13999999999999999\n cpu-wattage: 13.999999999999998\n cpu-wattage-times-duration: 5039.999999999999\n cpu-energy-raw: 0.0013999999999999998\n vcpu-ratio: 4\n cpu-energy-kwh: 0.00034999999999999994\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 0.32\n cpu-wattage: 32\n cpu-wattage-times-duration: 11520\n cpu-energy-raw: 0.0032\n vcpu-ratio: 4\n cpu-energy-kwh: 0.0008\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 0.75\n cpu-wattage: 75\n cpu-wattage-times-duration: 27000\n cpu-energy-raw: 0.0075\n vcpu-ratio: 4\n cpu-energy-kwh: 0.001875\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 1.02\n cpu-wattage: 102\n cpu-wattage-times-duration: 36720\n cpu-energy-raw: 0.0102\n vcpu-ratio: 4\n cpu-energy-kwh: 0.00255\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9e4f9ffe.0c955d2d.js b/assets/js/9e4f9ffe.0c955d2d.js deleted file mode 100644 index feba0053..00000000 --- a/assets/js/9e4f9ffe.0c955d2d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[684],{4137:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),l=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},u=function(e){var n=l(e.components);return a.createElement(p.Provider,{value:n},e.children)},d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},c=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),c=l(t),m=i,g=c["".concat(p,".").concat(m)]||c[m]||d[m]||o;return t?a.createElement(g,r(r({ref:n},u),{},{components:t})):a.createElement(g,r({ref:n},u))}));function m(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,r=new Array(o);r[0]=c;var s={};for(var p in n)hasOwnProperty.call(n,p)&&(s[p]=n[p]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var l=2;l{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=t(7462),i=(t(7294),t(4137));const o={sidebar_position:7},r="How to check parameters and units using `explainer`",s={unversionedId:"users/how-to-use-the-explain-feature",id:"users/how-to-use-the-explain-feature",title:"How to check parameters and units using `explainer`",description:"Manifest files can get complicated, especially when there are many plugin instances initialized. It can be challenging to keep track of the flow of parameters and their units through a pipeline. To help manifest authors and auditors verify the correct flow of information through a pipeline, we provide the explainer feature.",source:"@site/docs/users/how-to-use-the-explain-feature.md",sourceDirName:"users",slug:"/users/how-to-use-the-explain-feature",permalink:"/users/how-to-use-the-explain-feature",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-use-the-explain-feature.md",tags:[],version:"current",sidebarPosition:7,frontMatter:{sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"Verifying IF outputs with `if-check`",permalink:"/users/how-to-verify-files-with-if-check"},next:{title:"Developers",permalink:"/developers/"}},p={},l=[{value:"Toggling explainer on or off",id:"toggling-explainer-on-or-off",level:2},{value:"Defining parameter metadata",id:"defining-parameter-metadata",level:2},{value:"Example manifest",id:"example-manifest",level:2},{value:"When not to use explainer",id:"when-not-to-use-explainer",level:2}],u={toc:l};function d(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-check-parameters-and-units-using-explainer"},"How to check parameters and units using ",(0,i.kt)("inlineCode",{parentName:"h1"},"explainer")),(0,i.kt)("p",null,"Manifest files can get complicated, especially when there are many plugin instances initialized. It can be challenging to keep track of the flow of parameters and their units through a pipeline. To help manifest authors and auditors verify the correct flow of information through a pipeline, we provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," adds a block to the manifest that simply lists the parameter metadata used be the plugin's instance in the manifest. The metadata contains:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"method:")," the function name being executed by the plugin"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"path"),": the import path for the plugin"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"inputs"),": a list of each input parameter"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"outputs"),": a list of each output parameter")),(0,i.kt)("p",null,"Each element in ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"outputs")," contains the following information about each specific parameter:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"description:")," a plain-language summary of the parameter"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"unit:"),": The unit the parameter is expressed in"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"aggregation-method:"),": The appropriate method to use when aggregating the parameter across time or components (e.g. should it be summed, averaged, or held constant)")),(0,i.kt)("p",null,"This information allows you to check that the units output by one plugin are consistent with those expected as inputs to another, in one clear itemized list in your output manifest."),(0,i.kt)("h2",{id:"toggling-explainer-on-or-off"},"Toggling ",(0,i.kt)("inlineCode",{parentName:"h2"},"explainer")," on or off"),(0,i.kt)("p",null,"To enable the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature, add the following line to your manifest, somewhere in the manifest context (e.g. above the ",(0,i.kt)("inlineCode",{parentName:"p"},"plugins")," block):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"explainer: true\n")),(0,i.kt)("p",null,"If you set ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"false")," or omit the line altogether, the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature will not run."),(0,i.kt)("h2",{id:"defining-parameter-metadata"},"Defining parameter metadata"),(0,i.kt)("p",null,"Plugins are expected to ship with default values for their parameter metadata in their source code. For example, our plugin for calculating embodied carbon, ",(0,i.kt)("inlineCode",{parentName:"p"},"SciEmbodied"),", includes the following metadata definition:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-Typescript"},"export const SciEmbodied = (\n config: ConfigParams = {},\n parametersMetadata: PluginParametersMetadata,\n mapping: MappingParams\n): ExecutePlugin => {\n const metadata = {\n kind: 'execute',\n inputs: {\n ...({\n vCPUs: {\n description: 'number of CPUs allocated to an application',\n unit: 'CPUs',\n 'aggregation-method': 'copy',\n },\n memory: {\n description: 'RAM available for a resource, in GB',\n unit: 'GB',\n 'aggregation-method': 'copy',\n },\n ssd: {\n description: 'number of SSDs available for a resource',\n unit: 'SSDs',\n 'aggregation-method': 'copy',\n },\n hdd: {\n description: 'number of HDDs available for a resource',\n unit: 'HDDs',\n 'aggregation-method': 'copy',\n },\n gpu: {\n description: 'number of GPUs available for a resource',\n unit: 'GPUs',\n 'aggregation-method': 'copy',\n },\n 'usage-ratio': {\n description:\n 'a scaling factor that can be used to describe the ratio of actual resource usage comapred to real device usage, e.g. 0.25 if you are using 2 out of 8 vCPUs, 0.1 if you are responsible for 1 out of 10 GB of storage, etc',\n unit: 'dimensionless',\n 'aggregation-method': 'copy',\n },\n time: {\n description:\n 'a time unit to scale the embodied carbon by, in seconds. If not provided,time defaults to the value of the timestep duration.',\n unit: 'seconds',\n 'aggregation-method': 'copy',\n },\n } as ParameterMetadata),\n ...parametersMetadata?.inputs,\n },\n outputs: parametersMetadata?.outputs || {\n 'embodied-carbon': {\n description: 'embodied carbon for a resource, scaled by usage',\n unit: 'gCO2e',\n 'aggregation-method': 'sum',\n },\n },\n };\n")),(0,i.kt)("p",null,"However, there are cases where a plugin might not have parameter metadata in its source code, either because it was omitted, it was not knowable in advance, or the plugin was built before we shipped the ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," feature. Sometimes, you might want to override the hard-coded defaults and use alternative metadata. In these cases, you can define new plugin metadata in the manifest file. It is considered best-practice to ensure all plugin instances have a complete set of plugin metadata."),(0,i.kt)("p",null,"Setting parameter metadata from the manifest file is done in the plugin instance's ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block, as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n 'interpolate':\n method: Interpolation\n path: 'builtin'\n global-config:\n method: linear\n x: [0, 10, 50, 100]\n y: [0.12, 0.32, 0.75, 1.02]\n input-parameter: 'cpu/utilization'\n output-parameter: 'cpu-factor'\n parameter-metadata:\n inputs:\n cpu/utilization:\n description: 'portion of the total CPU capacity being used by an application'\n unit: 'percentage'\n aggregation-method: 'avg'\n outputs:\n cpu-factor:\n description: \"a dimensionless intermediate used to scale a processor's thermal design power by CPU usage\"\n unit: 'dimensionless'\n aggregation-method: 'avg'\n")),(0,i.kt)("h2",{id:"example-manifest"},"Example manifest"),(0,i.kt)("p",null,"The following manifest uses three plugins: ",(0,i.kt)("inlineCode",{parentName:"p"},"sci"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-embodied")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"sum-carbon"),". Of these, only ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-embodied"),' has defaults hardcoded into the plugin code. The other two do not because they are "generic" arithmetic plugins for whom the values cannot be known in advance. Therefore, we set new parameter metadata in the ',(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block for ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"sum-carbon")," but use the hardcoded defaults for ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-embodied"),"."),(0,i.kt)("p",null,"We toggle the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature by adding ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer: true")," in the manifest context."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sci\ndescription: successful path\ntags:\nexplainer: true\ninitialize:\n plugins:\n sci-embodied:\n path: 'builtin'\n method: SciEmbodied\n 'sum-carbon':\n path: 'builtin'\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n parameter-metadata:\n inputs:\n carbon-operational:\n description: \"carbon emitted due to an application's execution\"\n unit: 'gCO2eq'\n aggregation-method: 'sum'\n embodied-carbon:\n description: \"carbon emitted during the production, distribution and disposal of a hardware component, scaled by the fraction of the component's lifespan being allocated to the application under investigation\"\n unit: 'gCO2eq'\n aggregation-method: 'sum'\n outputs:\n carbon:\n description: \"total carbon emissions attributed to an application's usage as the sum of embodied and operational carbon\"\n unit: 'gCO2eq'\n aggregation-method: 'sum'\n sci:\n kind: plugin\n method: Sci\n path: 'builtin'\n config:\n functional-unit: requests\n parameter-metadata:\n inputs:\n carbon:\n description: \"total carbon emissions attributed to an application's usage as the sum of embodied and operational carbon\"\n unit: 'gCO2eq'\n aggregation-method: 'sum'\n requests:\n description: 'number of requests made to application in the given timestep'\n unit: 'requests'\n aggregation-method: 'sum'\n outputs:\n sci:\n description: 'software carbon intensity expressed as a rate of carbon emission per request'\n unit: 'gCO2eq/request'\n aggregation-method: 'sum'\ntree:\n children:\n child:\n pipeline:\n compute:\n - sci-embodied\n - sum-carbon\n - sci\n defaults:\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n vcpus-allocated: 1\n vcpus-total: 8\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n requests: 100\n")),(0,i.kt)("p",null,"When we execute this manifest, the following ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," block is added to the output file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"explain:\n sci-embodied:\n method: SciEmbodied\n path: builtin\n inputs:\n vCPUs:\n description: number of CPUs allocated to an application\n unit: CPUs\n aggregation-method: copy\n memory:\n description: RAM available for a resource, in GB\n unit: GB\n aggregation-method: copy\n ssd:\n description: number of SSDs available for a resource\n unit: SSDs\n aggregation-method: copy\n hdd:\n description: number of HDDs available for a resource\n unit: HDDs\n aggregation-method: copy\n gpu:\n description: number of GPUs available for a resource\n unit: GPUs\n aggregation-method: copy\n usage-ratio:\n description: >-\n a scaling factor that can be used to describe the ratio of actual\n resource usage comapred to real device usage, e.g. 0.25 if you are\n using 2 out of 8 vCPUs, 0.1 if you are responsible for 1 out of 10 GB\n of storage, etc\n unit: dimensionless\n aggregation-method: copy\n time:\n description: >-\n a time unit to scale the embodied carbon by, in seconds. If not\n provided,time defaults to the value of the timestep duration.\n unit: seconds\n aggregation-method: copy\n outputs:\n embodied-carbon:\n description: embodied carbon for a resource, scaled by usage\n unit: gCO2e\n aggregation-method: sum\n sum-carbon:\n method: Sum\n path: builtin\n inputs:\n carbon-operational:\n unit: gCO2eq\n description: carbon emitted due to an application's execution\n aggregation-method: sum\n embodied-carbon:\n unit: gCO2eq\n description: >-\n carbon emitted during the production, distribution and disposal of a\n hardware component, scaled by the fraction of the component's lifespan\n being allocated to the application under investigation\n aggregation-method: sum\n outputs:\n carbon:\n unit: gCO2eq\n description: >-\n total carbon emissions attributed to an application's usage as the sum\n of embodied and operational carbon\n aggregation-method: sum\n sci:\n method: Sci\n path: builtin\n inputs:\n carbon:\n unit: gCO2eq\n description: >-\n total carbon emissions attributed to an application's usage as the sum\n of embodied and operational carbon\n aggregation-method: sum\n functional-unit:\n description: >-\n the name of the functional unit in which the final SCI value should be\n expressed, e.g. requests, users\n unit: none\n aggregation-method: sum\n requests:\n unit: requests\n description: number of requests made to application in the given timestep\n aggregation-method: sum\n outputs:\n sci:\n unit: gCO2eq/request\n description: >-\n software carbon intensity expressed as a rate of carbon emission per\n request\n aggregation-method: sum\n")),(0,i.kt)("h2",{id:"when-not-to-use-explainer"},"When ",(0,i.kt)("em",{parentName:"h2"},"not")," to use ",(0,i.kt)("inlineCode",{parentName:"h2"},"explainer")),(0,i.kt)("p",null,"In manifests where you are only using generic plugins, or override all the metadata loaded in from the plugin source code, ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," will simply echo back information from your ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block since all the parameter metadata is set there. In these cases, the ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," block is probably redundant information as you could just read the same information in your manifest's ",(0,i.kt)("inlineCode",{parentName:"p"},"plugins")," section. The point of ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," is to confirm what units and parameters are being passed through a pipeline when you have a mixture of plugins from many sources whose parameter metadata is defined in-code and in-manifest."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9e4f9ffe.4d9c6eb1.js b/assets/js/9e4f9ffe.4d9c6eb1.js new file mode 100644 index 00000000..69c81fab --- /dev/null +++ b/assets/js/9e4f9ffe.4d9c6eb1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[684],{4137:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>d});var a=t(7294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function r(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var p=a.createContext({}),l=function(e){var n=a.useContext(p),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},c=function(e){var n=l(e.components);return a.createElement(p.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},m=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),m=l(t),d=i,g=m["".concat(p,".").concat(d)]||m[d]||u[d]||o;return t?a.createElement(g,r(r({ref:n},c),{},{components:t})):a.createElement(g,r({ref:n},c))}));function d(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var o=t.length,r=new Array(o);r[0]=m;var s={};for(var p in n)hasOwnProperty.call(n,p)&&(s[p]=n[p]);s.originalType=e,s.mdxType="string"==typeof e?e:i,r[1]=s;for(var l=2;l{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>r,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=t(7462),i=(t(7294),t(4137));const o={sidebar_position:7},r="How to check parameters and units using `explainer`",s={unversionedId:"users/how-to-use-the-explain-feature",id:"users/how-to-use-the-explain-feature",title:"How to check parameters and units using `explainer`",description:"Manifest files can get complicated, especially when there are many plugin instances initialized. It can be challenging to keep track of the flow of parameters and their units through a pipeline. To help manifest authors and auditors verify the correct flow of information through a pipeline, we provide the explainer feature.",source:"@site/docs/users/how-to-use-the-explain-feature.md",sourceDirName:"users",slug:"/users/how-to-use-the-explain-feature",permalink:"/users/how-to-use-the-explain-feature",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-use-the-explain-feature.md",tags:[],version:"current",sidebarPosition:7,frontMatter:{sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"Verifying IF outputs with `if-check`",permalink:"/users/how-to-verify-files-with-if-check"},next:{title:"Developers",permalink:"/developers/"}},p={},l=[{value:"Toggling explainer on or off",id:"toggling-explainer-on-or-off",level:2},{value:"Defining parameter metadata",id:"defining-parameter-metadata",level:2},{value:"Example manifest",id:"example-manifest",level:2},{value:"When not to use explainer",id:"when-not-to-use-explainer",level:2}],c={toc:l};function u(e){let{components:n,...t}=e;return(0,i.kt)("wrapper",(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-check-parameters-and-units-using-explainer"},"How to check parameters and units using ",(0,i.kt)("inlineCode",{parentName:"h1"},"explainer")),(0,i.kt)("p",null,"Manifest files can get complicated, especially when there are many plugin instances initialized. It can be challenging to keep track of the flow of parameters and their units through a pipeline. To help manifest authors and auditors verify the correct flow of information through a pipeline, we provide the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," adds a block to the manifest that simply lists the parameter metadata used be the plugin's instance in the manifest. The metadata contains:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"plugins:")," the list of plugins where the parameter is used"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"unit"),": the unit in which the parameter is expressed"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"description:")," a plain-language summary of the parameter"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},"aggregation-method:"),": The appropriate method to use when aggregating the parameter across time or components (e.g. should it be summed, averaged, or held constant)")),(0,i.kt)("p",null,"This information allows you to check that the units output by one plugin are consistent with those expected as inputs to another, in one clear itemized list in your output manifest."),(0,i.kt)("p",null,"Note that when the parameter has different units across instances, an error will occur."),(0,i.kt)("h2",{id:"toggling-explainer-on-or-off"},"Toggling ",(0,i.kt)("inlineCode",{parentName:"h2"},"explainer")," on or off"),(0,i.kt)("p",null,"To enable the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature, add the following line to your manifest, somewhere in the manifest context (e.g. above the ",(0,i.kt)("inlineCode",{parentName:"p"},"plugins")," block):"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"explainer: true\n")),(0,i.kt)("p",null,"If you set ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," to ",(0,i.kt)("inlineCode",{parentName:"p"},"false")," or omit the line altogether, the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature will not run."),(0,i.kt)("h2",{id:"defining-parameter-metadata"},"Defining parameter metadata"),(0,i.kt)("p",null,"Plugins are expected to ship with default values for their parameter metadata in their source code. For example, our plugin for calculating embodied carbon, ",(0,i.kt)("inlineCode",{parentName:"p"},"SciEmbodied"),", includes the following metadata definition:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"export const SciEmbodied = PluginFactory({\n metadata: {\n inputs: {\n vCPUs: {\n description: 'number of CPUs allocated to an application',\n unit: 'CPUs',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n memory: {\n description: 'RAM available for a resource, in GB',\n unit: 'GB',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n ssd: {\n description: 'number of SSDs available for a resource',\n unit: 'SSDs',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n hdd: {\n description: 'number of HDDs available for a resource',\n unit: 'HDDs',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n gpu: {\n description: 'number of GPUs available for a resource',\n unit: 'GPUs',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n 'usage-ratio': {\n description:\n 'a scaling factor that can be used to describe the ratio of actual resource usage comapred to real device usage, e.g. 0.25 if you are using 2 out of 8 vCPUs, 0.1 if you are responsible for 1 out of 10 GB of storage, etc',\n unit: 'dimensionless',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n time: {\n description:\n 'a time unit to scale the embodied carbon by, in seconds. If not provided,time defaults to the value of the timestep duration.',\n unit: 'seconds',\n 'aggregation-method': {\n time: 'copy',\n component: 'copy',\n },\n },\n },\n outputs: {\n 'embodied-carbon': {\n description: 'embodied carbon for a resource, scaled by usage',\n unit: 'gCO2e',\n 'aggregation-method': {\n time: 'sum',\n component: 'sum',\n },\n },\n },\n },\n});\n")),(0,i.kt)("p",null,"However, there are cases where a plugin might not have parameter metadata in its source code, either because it was omitted, it was not knowable in advance, or the plugin was built before we shipped the ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," feature. Sometimes, you might want to override the hard-coded defaults and use alternative metadata. In these cases, you can define new plugin metadata in the manifest file. It is considered best-practice to ensure all plugin instances have a complete set of plugin metadata."),(0,i.kt)("p",null,"Setting parameter metadata from the manifest file is done in the plugin instance's ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block, as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"initialize:\n plugins:\n interpolate:\n method: Interpolation\n path: 'builtin'\n config:\n method: linear\n x: [0, 10, 50, 100]\n y: [0.12, 0.32, 0.75, 1.02]\n input-parameter: 'cpu/utilization'\n output-parameter: 'cpu-factor'\n parameter-metadata:\n inputs:\n cpu/utilization:\n description: 'portion of the total CPU capacity being used by an application'\n unit: 'percentage'\n aggregation-method:\n time: avg\n component: avg\n outputs:\n cpu-factor:\n description: \"a dimensionless intermediate used to scale a processor's thermal design power by CPU usage\"\n unit: 'dimensionless'\n aggregation-method:\n time: avg\n component: avg\n")),(0,i.kt)("h2",{id:"example-manifest"},"Example manifest"),(0,i.kt)("p",null,"The following manifest uses three plugins: ",(0,i.kt)("inlineCode",{parentName:"p"},"sci"),", ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-embodied")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"sum-carbon"),". Of these, only ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-embodied"),' has defaults hardcoded into the plugin code. The other two do not because they are "generic" arithmetic plugins for whom the values cannot be known in advance. Therefore, we set new parameter metadata in the ',(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block for ",(0,i.kt)("inlineCode",{parentName:"p"},"sci")," and ",(0,i.kt)("inlineCode",{parentName:"p"},"sum-carbon")," but use the hardcoded defaults for ",(0,i.kt)("inlineCode",{parentName:"p"},"sci-embodied"),"."),(0,i.kt)("p",null,"We toggle the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," feature by adding ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer: true")," in the manifest context."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sci\ndescription: successful path\ntags:\nexplainer: true\ninitialize:\n plugins:\n sci-embodied:\n path: 'builtin'\n method: SciEmbodied\n 'sum-carbon':\n path: 'builtin'\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n parameter-metadata:\n inputs:\n carbon-operational:\n description: \"carbon emitted due to an application's execution\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n embodied-carbon:\n description: \"carbon emitted during the production, distribution and disposal of a hardware component, scaled by the fraction of the component's lifespan being allocated to the application under investigation\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n outputs:\n carbon:\n description: \"total carbon emissions attributed to an application's usage as the sum of embodied and operational carbon\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n sci:\n kind: plugin\n method: Sci\n path: 'builtin'\n config:\n functional-unit: requests\n parameter-metadata:\n inputs:\n carbon:\n description: \"total carbon emissions attributed to an application's usage as the sum of embodied and operational carbon\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n requests:\n description: 'number of requests made to application in the given timestep'\n unit: 'requests'\n aggregation-method:\n time: sum\n component: sum\n outputs:\n sci:\n description: 'software carbon intensity expressed as a rate of carbon emission per request'\n unit: 'gCO2eq/request'\n aggregation-method:\n time: sum\n component: sum\ntree:\n children:\n child:\n pipeline:\n compute:\n - sci-embodied\n - sum-carbon\n - sci\n defaults:\n device/emissions-embodied: 1533.120 # gCO2eq\n time-reserved: 3600 # 1hr in seconds\n device/expected-lifespan: 94608000 # 3 years in seconds\n vcpus-allocated: 1\n vcpus-total: 8\n inputs:\n - timestamp: 2023-07-06T00:00\n duration: 3600\n energy: 5\n carbon-operational: 5\n requests: 100\n")),(0,i.kt)("p",null,"When we execute this manifest, the following ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," block is added to the output file:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"explain:\n vCPUs:\n plugins:\n - sci-embodied\n unit: CPUs\n description: number of CPUs allocated to an application\n aggregation-method:\n time: copy\n component: copy\n memory:\n plugins:\n - sci-embodied\n unit: GB\n description: RAM available for a resource, in GB\n aggregation-method:\n time: copy\n component: copy\n ssd:\n plugins:\n - sci-embodied\n unit: SSDs\n description: number of SSDs available for a resource\n aggregation-method:\n time: copy\n component: copy\n hdd:\n plugins:\n - sci-embodied\n unit: HDDs\n description: number of HDDs available for a resource\n aggregation-method:\n time: copy\n component: copy\n gpu:\n plugins:\n - sci-embodied\n unit: GPUs\n description: number of GPUs available for a resource\n aggregation-method:\n time: copy\n component: copy\n usage-ratio:\n plugins:\n - sci-embodied\n unit: dimensionless\n description: >-\n a scaling factor that can be used to describe the ratio of actual resource\n usage comapred to real device usage, e.g. 0.25 if you are using 2 out of 8\n vCPUs, 0.1 if you are responsible for 1 out of 10 GB of storage, etc\n aggregation-method:\n time: copy\n component: copy\n time:\n plugins:\n - sci-embodied\n unit: seconds\n description: >-\n a time unit to scale the embodied carbon by, in seconds. If not\n provided,time defaults to the value of the timestep duration.\n aggregation-method:\n time: copy\n component: copy\n embodied-carbon:\n plugins:\n - sci-embodied\n - sum-carbon\n unit: gCO2eq\n description: >-\n carbon emitted during the production, distribution and disposal of a\n hardware component, scaled by the fraction of the component's lifespan\n being allocated to the application under investigation\n aggregation-method:\n time: sum\n component: sum\n carbon-operational:\n plugins:\n - sum-carbon\n unit: gCO2eq\n description: carbon emitted due to an application's execution\n aggregation-method:\n time: sum\n component: sum\n carbon:\n plugins:\n - sum-carbon\n - sci\n unit: gCO2eq\n description: >-\n total carbon emissions attributed to an application's usage as the sum of\n embodied and operational carbon\n aggregation-method:\n time: sum\n component: sum\n requests:\n plugins:\n - sci\n unit: requests\n description: number of requests made to application in the given timestep\n aggregation-method:\n time: sum\n component: sum\n sci:\n plugins:\n - sci\n unit: gCO2eq/request\n description: >-\n software carbon intensity expressed as a rate of carbon emission per\n request\n aggregation-method:\n time: sum\n component: sum\n")),(0,i.kt)("h2",{id:"when-not-to-use-explainer"},"When ",(0,i.kt)("em",{parentName:"h2"},"not")," to use ",(0,i.kt)("inlineCode",{parentName:"h2"},"explainer")),(0,i.kt)("p",null,"In manifests where you are only using generic plugins, or override all the metadata loaded in from the plugin source code, ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," will simply echo back information from your ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block since all the parameter metadata is set there. In these cases, the ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," block is probably redundant information as you could just read the same information in your manifest's ",(0,i.kt)("inlineCode",{parentName:"p"},"plugins")," section. The point of ",(0,i.kt)("inlineCode",{parentName:"p"},"explain")," is to confirm what units and parameters are being passed through a pipeline when you have a mixture of plugins from many sources whose parameter metadata is defined in-code and in-manifest."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a34125e0.358d48e2.js b/assets/js/a34125e0.358d48e2.js deleted file mode 100644 index e7c22470..00000000 --- a/assets/js/a34125e0.358d48e2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[316],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},g=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),g=s(n),f=a,d=g["".concat(c,".").concat(f)]||g[f]||p[f]||o;return n?r.createElement(d,i(i({ref:t},u),{},{components:n})):r.createElement(d,i({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=g;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),a=(n(7294),n(4137));const o={},i="IF features",l={unversionedId:"reference/features",id:"reference/features",title:"IF features",description:"This page simply lists the features of Impact Framework that are not plugins or CLI tools, along with a brief description, usage instruction and link to more detailed docs.",source:"@site/docs/reference/features.md",sourceDirName:"reference",slug:"/reference/features",permalink:"/reference/features",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/features.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Errors",permalink:"/reference/errors"},next:{title:"Plugins",permalink:"/reference/plugins"}},c={},s=[{value:"Aggregate",id:"aggregate",level:2},{value:"How to configure",id:"how-to-configure",level:3},{value:"Explainer",id:"explainer",level:2},{value:"How to configure",id:"how-to-configure-1",level:3}],u={toc:s};function p(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"if-features"},"IF features"),(0,a.kt)("p",null,"This page simply lists the features of Impact Framework that are not plugins or CLI tools, along with a brief description, usage instruction and link to more detailed docs."),(0,a.kt)("p",null,"Typically these features are enabled using a piece of manifest config."),(0,a.kt)("h2",{id:"aggregate"},"Aggregate"),(0,a.kt)("p",null,"Aggregate collects and summarizes data across time or across components in your tree."),(0,a.kt)("h3",{id:"how-to-configure"},"How to configure"),(0,a.kt)("p",null,'Add the following config to your manifest (this example is for aggregating "cpu/utilization" values across both time and components):'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"aggregation:\n metrics:\n - 'cpu/utilization'\n type: both\n")),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"/major-concepts/aggregation"},"Read more on aggregate")),(0,a.kt)("h2",{id:"explainer"},"Explainer"),(0,a.kt)("p",null,"The explainer lists the unit, description, aggregation method, and plugins of the parameter that is used in the manifest."),(0,a.kt)("h3",{id:"how-to-configure-1"},"How to configure"),(0,a.kt)("p",null,"You can toggle the ",(0,a.kt)("inlineCode",{parentName:"p"},"explainer")," by adding the following config to your manifest:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"explainer: true\n")),(0,a.kt)("p",null,"You can override the parameter metadata provided in a plugin's source code by adding it to the plugin's ",(0,a.kt)("inlineCode",{parentName:"p"},"initialize")," block, as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},'explainer: true\nplugins:\n "sum-carbon":\n path: "builtin"\n method: Sum\n global-config:\n input-parameters:\n - carbon-operational\n - carbon-embodied\n output-parameter: carbon\n parameter-metadata:\n inputs:\n carbon-operational:\n description: "carbon emitted due to an application\'s execution"\n unit: "gCO2eq"\n aggregation-method: \'sum\',\n carbon-embodied:\n description: "carbon emitted during the production, distribution and disposal of a hardware component, scaled by the fraction of the component\'s lifespan being allocated to the application under investigation"\n unit: "gCO2eq"\n aggregation-method: \'sum\'\n')),(0,a.kt)("p",null,"Read more on ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-use-the-explain-feature"},"explainer")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a34125e0.3ff6d5e2.js b/assets/js/a34125e0.3ff6d5e2.js new file mode 100644 index 00000000..f0b3b713 --- /dev/null +++ b/assets/js/a34125e0.3ff6d5e2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[316],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=p(n),d=i,f=m["".concat(s,".").concat(d)]||m[d]||c[d]||r;return n?a.createElement(f,o(o({ref:t},u),{},{components:n})):a.createElement(f,o({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var a=n(7462),i=(n(7294),n(4137));const r={},o="IF features",l={unversionedId:"reference/features",id:"reference/features",title:"IF features",description:"This page simply lists the features of Impact Framework that are not plugins or CLI tools, along with a brief description, usage instruction and link to more detailed docs.",source:"@site/docs/reference/features.md",sourceDirName:"reference",slug:"/reference/features",permalink:"/reference/features",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/reference/features.md",tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Errors",permalink:"/reference/errors"},next:{title:"Plugins",permalink:"/reference/plugins"}},s={},p=[{value:"Aggregate",id:"aggregate",level:2},{value:"How to configure",id:"how-to-configure",level:3},{value:"Explainer",id:"explainer",level:2},{value:"How to configure",id:"how-to-configure-1",level:3},{value:"Inline Arithmetic Expressions",id:"inline-arithmetic-expressions",level:2},{value:"Supported Symbols and Operations:",id:"supported-symbols-and-operations",level:3},{value:"Syntax:",id:"syntax",level:3},{value:"Example:",id:"example",level:3},{value:"Plugin support",id:"plugin-support",level:3}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"if-features"},"IF features"),(0,i.kt)("p",null,"This page simply lists the features of Impact Framework that are not plugins or CLI tools, along with a brief description, usage instruction and link to more detailed docs."),(0,i.kt)("p",null,"Typically these features are enabled using a piece of manifest config."),(0,i.kt)("h2",{id:"aggregate"},"Aggregate"),(0,i.kt)("p",null,"Aggregate collects and summarizes data across time or across components in your tree."),(0,i.kt)("h3",{id:"how-to-configure"},"How to configure"),(0,i.kt)("p",null,'Add the following config to your manifest (this example is for aggregating "cpu/utilization" values across both time and components):'),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"aggregation:\n metrics:\n - 'cpu/utilization'\n type: both\n")),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"/major-concepts/aggregation"},"Read more on aggregate")),(0,i.kt)("h2",{id:"explainer"},"Explainer"),(0,i.kt)("p",null,"The explainer lists the unit, description, aggregation method, and plugins of the parameter that is used in the manifest."),(0,i.kt)("h3",{id:"how-to-configure-1"},"How to configure"),(0,i.kt)("p",null,"You can toggle the ",(0,i.kt)("inlineCode",{parentName:"p"},"explainer")," by adding the following config to your manifest:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"explainer: true\n")),(0,i.kt)("p",null,"You can override the parameter metadata provided in a plugin's source code by adding it to the plugin's ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," block, as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"explainer: true\nplugins:\n 'sum-carbon':\n path: 'builtin'\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - carbon-embodied\n output-parameter: carbon\n parameter-metadata:\n inputs:\n carbon-operational:\n description: \"carbon emitted due to an application's execution\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum,\n carbon-embodied:\n description: \"carbon emitted during the production, distribution and disposal of a hardware component, scaled by the fraction of the component's lifespan being allocated to the application under investigation\"\n unit: 'gCO2eq'\n aggregation-method:\n time: sum\n component: sum\n")),(0,i.kt)("p",null,"Read more on ",(0,i.kt)("a",{parentName:"p",href:"/users/how-to-use-the-explain-feature"},"explainer")),(0,i.kt)("h2",{id:"inline-arithmetic-expressions"},"Inline Arithmetic Expressions"),(0,i.kt)("p",null,"Inline arithmetic expressions allow basic mathematical operations to be embedded directly within ",(0,i.kt)("inlineCode",{parentName:"p"},"config")," parameters and ",(0,i.kt)("inlineCode",{parentName:"p"},"inputs")," values in manifest files. This enables dynamic calculations using constants or input variables, eliminating the need for manual pre-calculation of parameters."),(0,i.kt)("h3",{id:"supported-symbols-and-operations"},"Supported Symbols and Operations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"="),": Indicates the start of an arithmetic expression."),(0,i.kt)("li",{parentName:"ul"},"Supported operators: ",(0,i.kt)("inlineCode",{parentName:"li"},"*")," (multiplication), ",(0,i.kt)("inlineCode",{parentName:"li"},"+")," (addition), ",(0,i.kt)("inlineCode",{parentName:"li"},"-")," (subtraction), ",(0,i.kt)("inlineCode",{parentName:"li"},"/")," (division).")),(0,i.kt)("h3",{id:"syntax"},"Syntax:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"To define an inline arithmetic expression, the string must start with an equal sign (",(0,i.kt)("inlineCode",{parentName:"li"},"="),"). For example:",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"'input-parameter': '= 2 * carbon'\n")),"This expression evaluates the multiplication of ",(0,i.kt)("inlineCode",{parentName:"li"},"2")," by the value of the ",(0,i.kt)("inlineCode",{parentName:"li"},"carbon")," parameter from the input."),(0,i.kt)("li",{parentName:"ul"},"Arithmetic operations between two constants can also be defined without using the equal sign (",(0,i.kt)("inlineCode",{parentName:"li"},"="),"):",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"coefficient: 2 * 2\n")),"This expression evaluates the multiplication of ",(0,i.kt)("inlineCode",{parentName:"li"},"2")," by ",(0,i.kt)("inlineCode",{parentName:"li"},"2")," directly."),(0,i.kt)("li",{parentName:"ul"},"If the parameter name contains symbols, it should be placed in the quotes. The expresion should look like:",(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"output-parameter: '= 2 * \"carbon-product\"'\n")))),(0,i.kt)("h3",{id:"example"},"Example:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"config:\n 'input-parameter': '= 2 * carbon'\n coefficient: 2 * 2\n 'output-parameter': '= 2 * \"carbon-product\"'\n---\ninputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600 * 60\n carbon: = 10 * \"other-param\n other-param: 3\n")),(0,i.kt)("h3",{id:"plugin-support"},"Plugin support"),(0,i.kt)("p",null,"To enable inline arithmetic expressions in your plugin, specify it in your plugin\u2019s definition function like this:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"allowArithmeticExpressions: ['input-parameter'];\n")),(0,i.kt)("p",null,"In the ",(0,i.kt)("inlineCode",{parentName:"p"},"allowArithmeticExpressions")," array, list all parameters (whether in config, inputs, or outputs) that can contain arithmetic expressions. The calculations are handled internally (in the PluginFactory interface)."),(0,i.kt)("p",null,"If your plugin doesn\u2019t have specified parameters but has dynamic output parameters that should support evaluation, you can enable ",(0,i.kt)("inlineCode",{parentName:"p"},"arithmeticExpressions")," with an empty array:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"allowArithmeticExpressions: [];\n")),(0,i.kt)("p",null,"To design your plugin with support for arithmetic expressions, you can use various utility functions."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"If your plugin's config parameters must be of type ",(0,i.kt)("inlineCode",{parentName:"li"},"number"),", you can use the ",(0,i.kt)("inlineCode",{parentName:"li"},"validateArithmeticExpression")," function from ",(0,i.kt)("inlineCode",{parentName:"li"},"@grnsft/if-core/utils"),":")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import {validateArithmeticExpression} from '@grnsft/if-core/utils';\n\n// Plugin definition\n\nconfigValidation: (config: ConfigParams) => {\n const configSchema = z.object({\n coefficient: z.preprocess(\n value => validateArithmeticExpression('coefficient', value, 'number'),\n z.number()\n ),\n 'input-parameter': z.string().min(1),\n 'output-parameter': z.string().min(1),\n });\n\n return validate>(\n configSchema as ZodType,\n config\n );\n },\n")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"If your config parameters contain arithmetic expressions like the following:")),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"config:\n keep-existing: false\n from: = 4 * \"if-size\"\n to: 'if-repo-size'\n")),(0,i.kt)("p",null,"But during implementation, you need to extract the pure parameter name (e.g., ",(0,i.kt)("inlineCode",{parentName:"p"},"if-size"),"), you can use the ",(0,i.kt)("inlineCode",{parentName:"p"},"getParameterFromArithmeticExpression")," function:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-ts"},"import { getParameterFromArithmeticExpression } from '@grnsft/if-core/utils';\n\n// Plugin definition\n\nconfigValidation: (config: ConfigParams) => {\n const configSchema = z.object({\n 'keep-existing': z.boolean(),\n from: z.string().min(1),\n to: z.string().min(1),\n });\n\n const extractedFrom = getParameterFromArithmeticExpression(config.from);\n const updatedConfig = config['keep-existing']\n ? config\n : { ...config, 'pure-from': extractedFrom };\n\n validate>(configSchema, updatedConfig);\n\n return updatedConfig;\n};\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ab3e713c.4435bc0c.js b/assets/js/ab3e713c.4435bc0c.js deleted file mode 100644 index 3fa6bb7e..00000000 --- a/assets/js/ab3e713c.4435bc0c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[712],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),p=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=p(n),d=a,g=m["".concat(l,".").concat(d)]||m[d]||c[d]||o;return n?i.createElement(g,r(r({ref:t},u),{},{components:n})):i.createElement(g,r({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,r[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var i=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":2},r="Software Carbon Intensity (SCI)",s={unversionedId:"pipelines/sci",id:"pipelines/sci",title:"Software Carbon Intensity (SCI)",description:"Description",source:"@site/docs/pipelines/sci.md",sourceDirName:"pipelines",slug:"/pipelines/sci",permalink:"/pipelines/sci",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/sci.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"Grabbing instance metadata from a CSV file",permalink:"/pipelines/instance-metadata"},next:{title:"Reference",permalink:"/reference/"}},l={},p=[{value:"Description",id:"description",level:2},{value:"Tags",id:"tags",level:2},{value:"Prerequisites",id:"prerequisites",level:2},{value:"Scope",id:"scope",level:2},{value:"Common patterns",id:"common-patterns",level:2},{value:"Observations",id:"observations",level:2},{value:"Constants and coefficients:",id:"constants-and-coefficients",level:2},{value:"Assumptions and limitations",id:"assumptions-and-limitations",level:2},{value:"Components",id:"components",level:2},{value:"Plugins",id:"plugins",level:2},{value:"Interpolate",id:"interpolate",level:3},{value:"config",id:"config",level:4},{value:"Multiply",id:"multiply",level:3},{value:"config",id:"config-1",level:4},{value:"Divide",id:"divide",level:3},{value:"config",id:"config-2",level:4},{value:"Sum",id:"sum",level:3},{value:"config",id:"config-3",level:4},{value:"SciEmbodied",id:"sciembodied",level:3},{value:"config",id:"config-4",level:4},{value:"SCI",id:"sci",level:3},{value:"config:",id:"config-5",level:4},{value:"Manifest",id:"manifest",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"software-carbon-intensity-sci"},"Software Carbon Intensity (SCI)"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"https://greensoftware.foundation/articles/software-carbon-intensity-sci-specification-project"},"software carbon intensity (SCI)")," score is perhaps the most important value that can be generated using Impact Framework. "),(0,a.kt)("p",null,"SCI is an ISO-recognized standard for reporting the carbon costs of running software. This tutorial demonstrates how to organize a pipeline of Impact framework plugins to calculate SCI scores from some simple observations that are commonly available for software applications running in the cloud."),(0,a.kt)("h2",{id:"tags"},"Tags"),(0,a.kt)("p",null,"SCI, cloud, cpu, memory, power-curve"),(0,a.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,a.kt)("p",null,"This tutorial builds on top of the ",(0,a.kt)("a",{parentName:"p",href:"./teads.md"},"Teads curve")," pipeline tutorial. That tutorial demonstrates how to organize a pipeline that converts CPU utilization observations into CPU energy. This tutorial uses the same pipeline but goes several steps further, including converting the CPU energy estimates into carbon, adding the embodied carbon associated with the hardware being used and calculating the SCI score."),(0,a.kt)("h2",{id:"scope"},"Scope"),(0,a.kt)("p",null,"This SCI calculation takes into account the operational and embodied carbon of the server running our application. This includes the energy used to run the application, calculated from CPU and memory utilization, and the energy required to transfer data over the internet between server and client. It does not account for data center embodied carbon, embodied carbon of end user devices nor operational carbon in end user devices."),(0,a.kt)("h2",{id:"common-patterns"},"Common patterns"),(0,a.kt)("p",null,"We employ the well known power curve from ",(0,a.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"Davy, 2021")," to estimate CPU power from CPU utilization. You can find a detailed explanation of our implementation of this power curve methodology ",(0,a.kt)("a",{parentName:"p",href:"./teads.md"},"here"),"."),(0,a.kt)("p",null,"We also use the networking energy and embodied carbon estimation methods from ",(0,a.kt)("a",{parentName:"p",href:"https://www.cloudcarbonfootprint.org/docs/methodology"},"Cloud Carbon Footprint"),". This includes using the networking energy coefficient they suggest and implementing their method for calculating embodied emissions in an ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/if-run/builtins/sci-embodied"},"Impact Framework plugin"),"."),(0,a.kt)("h2",{id:"observations"},"Observations"),(0,a.kt)("p",null,"This manifest requires the following observations:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"CPU utilization"),(0,a.kt)("li",{parentName:"ul"},"thermal design power of the processors"),(0,a.kt)("li",{parentName:"ul"},"number of vCPUs allocated to the application under observation"),(0,a.kt)("li",{parentName:"ul"},"total number of vCPUs available on the cloud instance being used"),(0,a.kt)("li",{parentName:"ul"},"the name of the cloud instance type being used"),(0,a.kt)("li",{parentName:"ul"},"the grid carbon intensity for the grid powering the data center"),(0,a.kt)("li",{parentName:"ul"},"data transferred in/out of the application"),(0,a.kt)("li",{parentName:"ul"},"users per timestep")),(0,a.kt)("h2",{id:"constants-and-coefficients"},"Constants and coefficients:"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"parameter"),(0,a.kt)("th",{parentName:"tr",align:null},"description"),(0,a.kt)("th",{parentName:"tr",align:null},"value"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"network-energy-coefficient")),(0,a.kt)("td",{parentName:"tr",align:null},"Coefficient relating data sent over network to energy"),(0,a.kt)("td",{parentName:"tr",align:null},"0.001")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x"),", ",(0,a.kt)("inlineCode",{parentName:"td"},"y")),(0,a.kt)("td",{parentName:"tr",align:null},"Points on power curve relating CPU utilization to a coefficient used to scale the processor's thermal design power"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x: [0, 10, 50, 100], y: [0.12, 0.32, 0.75, 1.02]"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"baseline-emissions")),(0,a.kt)("td",{parentName:"tr",align:null},'embodied emissions for a "baseline" server with 1 CPU, 16GB RAM'),(0,a.kt)("td",{parentName:"tr",align:null},"1000000")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"lifespan")),(0,a.kt)("td",{parentName:"tr",align:null},"lifespan for the server running our application"),(0,a.kt)("td",{parentName:"tr",align:null},"126144000")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"usage-ratio")),(0,a.kt)("td",{parentName:"tr",align:null},"scaling factor for adjusting total embodied carbon down tot he portion our application is responsible for"),(0,a.kt)("td",{parentName:"tr",align:null},"1")))),(0,a.kt)("h2",{id:"assumptions-and-limitations"},"Assumptions and limitations"),(0,a.kt)("p",null,"The following are assumed to be true in this manifest:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"the embodied carbon of the baseline server approximates the real embodied carbon of the server running our application"),(0,a.kt)("li",{parentName:"ul"},"the lifespan of the server running our application is really 4 years"),(0,a.kt)("li",{parentName:"ul"},"the coefficient relating data transferred over the network to energy is accurate"),(0,a.kt)("li",{parentName:"ul"},"the power curve relating CPU utilization to power is appropriate for the processor being used to run our application"),(0,a.kt)("li",{parentName:"ul"},"the coefficient relating memory utilization to energy is accurate"),(0,a.kt)("li",{parentName:"ul"},"it is appropriate to consider end user embodied carbon, end user operational carbon and the operationl and embodied emissions of the data center to be out of scope."),(0,a.kt)("li",{parentName:"ul"},"the temporal granularity of the observations are sufficient to accurately capture the behaviour of our application")),(0,a.kt)("h2",{id:"components"},"Components"),(0,a.kt)("p",null,"There is only one component in this example. It represents the entire application. The component pipeline looks as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"pipeline:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - sum-energy-components\n - embodied-carbon\n - operational-carbon\n - sum-carbon\n - sci\n")),(0,a.kt)("h2",{id:"plugins"},"Plugins"),(0,a.kt)("h3",{id:"interpolate"},"Interpolate"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"interpolate")," plugin is used once. The instance is named ",(0,a.kt)("inlineCode",{parentName:"p"},"interpolate"),". It is used to interpolate the curve relating CPU utilization and thermal-design-power factor so that the right value can be retrieved for the observed CPU utilization at each timestep."),(0,a.kt)("h4",{id:"config"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"method: linear\nx: [0, 10, 50, 100]\ny:[0.12, 0.32, 0.75, 1.02]\ninput-parameter: cpu/utilization\noutput-parameter: cpu-factor\n")),(0,a.kt)("h3",{id:"multiply"},"Multiply"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Multiply")," plugin is used several times. The instances are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"cpu-factor-to-wattage"),": used to multiply the thermal design power of the processor by the factor returned from the power curve interpolation, yielding power in Watts."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"wattage-times-duration"),": used to multiply the power in Watts by the duration of each timestep, yielding energy in W/duration."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"operational-carbon"),": used to convert energy into operational carbon by multiplying energy in kWh by the grid carbon intensity in gCO2/kWh.")),(0,a.kt)("h4",{id:"config-1"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"cpu-factor-to-wattage:\ninput-parameters:\n - cpu-factor\n - cpu/thermal-design-power\noutput-parameter: \n - cpu-wattage\n\nwattage-times-duration:\ninput-parameters:\n - cpu-wattage\n - duration\noutput-parameter: \n - cpu-wattage-times-duration\n\noperational-carbon:\ninput-parameters:\n - energy\n - grid/carbon-intensity\noutput-parameter: \n - carbon-operational\n\n")),(0,a.kt)("h3",{id:"divide"},"Divide"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Divide")," plugin is used once in this manifest. The instance is named ",(0,a.kt)("inlineCode",{parentName:"p"},"wattage-to-energy-kwh"),". It is used to convert energy in W/duration to kWh."),(0,a.kt)("h4",{id:"config-2"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"wattage-to-energy-kwh:\nnumerator: cpu-wattage-times-duration\ndenominator: 3600000\noutput: cpu-energy-raw\n")),(0,a.kt)("h3",{id:"sum"},"Sum"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Sum")," plugin is used several times in this manifest. The instances are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"sum-energy-components"),": used to sum all the various components of energy into a single value, called ",(0,a.kt)("inlineCode",{parentName:"li"},"energy"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"sum-carbon"),": used to sum the various components of carbon into a single value, named ",(0,a.kt)("inlineCode",{parentName:"li"},"carbon"),".")),(0,a.kt)("h4",{id:"config-3"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"sum-energy-components:\ninput-parameters:\n - cpu/energy\n - network/energy\noutput-parameter: \n - energy\n\nsum-carbon:\ninput-parameters:\n - carbon-operational\n - carbon-embodied\noutput-parameter: \n - carbon\n")),(0,a.kt)("h3",{id:"sciembodied"},"SciEmbodied"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"SciEmbodied")," plugin is used once. Its purpose is to calculate the embodied emissions of the server running our application and apportion the total embodied carbon to the fraction that we are responsible (i.e. scale it based on the application only using a fraction of the available resources and a fraction of the server lifespan). We do not scale by resource allocation in this example, only time."),(0,a.kt)("h4",{id:"config-4"},"config"),(0,a.kt)("p",null,"We use the plugin defaults for all the ",(0,a.kt)("inlineCode",{parentName:"p"},"SciEmbodied")," config. This means we assume the total embodied emissions to be 1000000 gCO2e and the server to be a simple rack server with 1 CPU and 16GB RAM and no other components."),(0,a.kt)("h3",{id:"sci"},"SCI"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"SCI")," plugin is used once. It is used to calculate the software carbon intensity by dividing ",(0,a.kt)("inlineCode",{parentName:"p"},"carbon")," by a functional unit, that has to be available in the manifest ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array at the time the plugin is executed. The functional unit in this example is users in each timestep."),(0,a.kt)("h4",{id:"config-5"},"config:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"sci:\nfunctional-unit: users\n")),(0,a.kt)("h2",{id:"manifest"},"Manifest"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sci example\ndescription:\ntags:\naggregation:\n metrics:\n - carbon\n - sci\n type: both\n \ninitialize:\n plugins:\n interpolate:\n path: builtin\n method: Interpolation\n config:\n method: linear\n x:\n - 0\n - 10\n - 50\n - 100\n 'y':\n - 0.12\n - 0.32\n - 0.75\n - 1.02\n input-parameter: cpu/utilization\n output-parameter: cpu-factor\n cpu-factor-to-wattage:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-factor\n - cpu/thermal-design-power\n output-parameter: cpu-wattage\n wattage-times-duration:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-wattage\n - duration\n output-parameter: cpu-wattage-times-duration\n wattage-to-energy-kwh:\n path: builtin\n method: Divide\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n calculate-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n correct-cpu-energy-for-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu/energy\n sum-energy-components:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\n embodied-carbon:\n path: builtin\n method: SciEmbodied\n config:\n output-parameter: carbon-embodied\n operational-carbon:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - energy\n - grid/carbon-intensity\n output-parameter: carbon-operational\n sum-carbon:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n sci:\n path: builtin\n method: Sci\n config:\n functional-unit: users\n\ntree:\n children:\n github-storage-for-if-docs:\n pipeline:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - sum-energy-components\n - embodied-carbon\n - operational-carbon\n - sum-carbon\n - sci\n defaults:\n cpu/thermal-design-power: 100\n vcpus-total: 1\n vcpus-allocated: 1\n network/energy: 0.001\n grid/carbon-intensity: 130\n inputs:\n - timestamp: '2024-07-22T00:00:00'\n duration: 3600 \n site-visits: 228\n cpu/utilization: 45\n component: 1\n users: 1100\n - timestamp: '2024-07-23T00:00:00'\n duration: 3600 \n site-visits: 216\n cpu/utilization: 30\n component: 1\n users: 1050\n - timestamp: '2024-07-24T00:00:00'\n duration: 3600 \n site-visits: 203\n cpu/utilization: 50\n component: 1\n users: 1055\n - timestamp: '2024-07-25T00:00:00'\n duration: 3600 \n site-visits: 203\n cpu/utilization: 33\n component: 1\n users: 996\n - timestamp: '2024-07-26T00:00:00'\n duration: 3600 \n site-visits: 172\n cpu/utilization: 29\n component: 1\n users: 899\n - timestamp: '2024-07-27T00:00:00'\n duration: 3600 \n site-visits: 38\n cpu/utilization: 68\n component: 1\n users: 1080\n - timestamp: '2024-07-28T00:00:00'\n duration: 3600 \n site-visits: 63\n cpu/utilization: 49\n component: 1\n users: 1099\n - timestamp: '2024-07-29T00:00:00'\n duration: 3600 \n site-visits: 621\n cpu/utilization: 77\n component: 1\n users: 1120\n - timestamp: '2024-07-30T00:00:00'\n duration: 3600 \n site-visits: 181\n cpu/utilization: 31\n component: 1\n users: 1125\n - timestamp: '2024-07-31T00:00:00'\n duration: 3600 \n site-visits: 213\n cpu/utilization: 29\n component: 1\n users: 1113\n - timestamp: '2024-08-01T00:00:00'\n duration: 3600 \n site-visits: 167\n cpu/utilization: 29\n component: 1\n users: 1111\n - timestamp: '2024-08-02T00:00:00'\n duration: 3600 \n site-visits: 428\n cpu/utilization: 29\n component: 1\n users: 1230\n - timestamp: '2024-08-03T00:00:00'\n duration: 3600 \n site-visits: 58\n cpu/utilization: 64\n component: 1\n users: 1223\n - timestamp: '2024-08-04T00:00:00'\n duration: 3600 \n site-visits: 66\n cpu/utilization: 59\n component: 1\n users: 1210\n - timestamp: '2024-08-05T00:00:00'\n duration: 3600 \n site-visits: 301\n cpu/utilization: 60\n component: 1\n users: 1011\n - timestamp: '2024-08-06T00:00:00'\n duration: 3600 \n site-visits: 193\n cpu/utilization: 35\n component: 1\n users: 999\n - timestamp: '2024-08-07T00:00:00'\n duration: 3600 \n site-visits: 220\n cpu/utilization: 37\n component: 1\n users: 1010\n - timestamp: '2024-08-08T00:00:00'\n duration: 3600 \n site-visits: 215\n cpu/utilization: 43\n component: 1\n users: 1008\n - timestamp: '2024-08-09T00:00:00'\n duration: 3600 \n site-visits: 516\n cpu/utilization: 28\n component: 1\n users: 992\n - timestamp: '2024-08-10T00:00:00'\n duration: 3600 \n site-visits: 42\n cpu/utilization: 39\n component: 1\n users: 1101\n - timestamp: '2024-08-11T00:00:00'\n duration: 3600 \n cpu/utilization: 40\n site-visits: 76\n component: 1\n users: 1000\n - timestamp: '2024-08-12T00:00:00'\n duration: 3600 \n site-visits: 226\n cpu/utilization: 55\n component: 1\n users: 845\n - timestamp: '2024-08-13T00:00:00'\n duration: 3600 \n site-visits: 180\n cpu/utilization: 62\n component: 1\n users: 1006\n - timestamp: '2024-08-14T00:00:00'\n duration: 3600 \n site-visits: 232\n cpu/utilization: 71\n component: 1\n users: 1076\n - timestamp: '2024-08-15T00:00:00'\n duration: 3600 \n site-visits: 175\n cpu/utilization: 75\n component: 1\n users: 1050\n - timestamp: '2024-08-16T00:00:00'\n duration: 3600 \n site-visits: 235\n cpu/utilization: 77\n component: 1\n users: 1047\n - timestamp: '2024-08-17T00:00:00'\n duration: 3600 \n site-visits: 44\n cpu/utilization: 80\n component: 1\n users: 1020\n - timestamp: '2024-08-18T00:00:00'\n duration: 3600 \n site-visits: 31\n cpu/utilization: 84\n component: 1\n users: 1038\n\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ab3e713c.cada5228.js b/assets/js/ab3e713c.cada5228.js new file mode 100644 index 00000000..8e159152 --- /dev/null +++ b/assets/js/ab3e713c.cada5228.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[712],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var i=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=i.createContext({}),p=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):r(r({},t),e)),n},u=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},m=i.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),m=p(n),d=a,g=m["".concat(l,".").concat(d)]||m[d]||c[d]||o;return n?i.createElement(g,r(r({ref:t},u),{},{components:n})):i.createElement(g,r({ref:t},u))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,r=new Array(o);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:a,r[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>c,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var i=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":2},r="Software Carbon Intensity (SCI)",s={unversionedId:"pipelines/sci",id:"pipelines/sci",title:"Software Carbon Intensity (SCI)",description:"Description",source:"@site/docs/pipelines/sci.md",sourceDirName:"pipelines",slug:"/pipelines/sci",permalink:"/pipelines/sci",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/sci.md",tags:[],version:"current",frontMatter:{"sidebar-position":2},sidebar:"tutorialSidebar",previous:{title:"Grabbing instance metadata from a CSV file",permalink:"/pipelines/instance-metadata"},next:{title:"Reference",permalink:"/reference/"}},l={},p=[{value:"Description",id:"description",level:2},{value:"Tags",id:"tags",level:2},{value:"Prerequisites",id:"prerequisites",level:2},{value:"Scope",id:"scope",level:2},{value:"Common patterns",id:"common-patterns",level:2},{value:"Observations",id:"observations",level:2},{value:"Constants and coefficients:",id:"constants-and-coefficients",level:2},{value:"Assumptions and limitations",id:"assumptions-and-limitations",level:2},{value:"Components",id:"components",level:2},{value:"Plugins",id:"plugins",level:2},{value:"Interpolate",id:"interpolate",level:3},{value:"config",id:"config",level:4},{value:"Multiply",id:"multiply",level:3},{value:"config",id:"config-1",level:4},{value:"Divide",id:"divide",level:3},{value:"config",id:"config-2",level:4},{value:"Sum",id:"sum",level:3},{value:"config",id:"config-3",level:4},{value:"SciEmbodied",id:"sciembodied",level:3},{value:"config",id:"config-4",level:4},{value:"SCI",id:"sci",level:3},{value:"config:",id:"config-5",level:4},{value:"Manifest",id:"manifest",level:2}],u={toc:p};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,i.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"software-carbon-intensity-sci"},"Software Carbon Intensity (SCI)"),(0,a.kt)("h2",{id:"description"},"Description"),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"https://greensoftware.foundation/articles/software-carbon-intensity-sci-specification-project"},"software carbon intensity (SCI)")," score is perhaps the most important value that can be generated using Impact Framework."),(0,a.kt)("p",null,"SCI is an ISO-recognized standard for reporting the carbon costs of running software. This tutorial demonstrates how to organize a pipeline of Impact framework plugins to calculate SCI scores from some simple observations that are commonly available for software applications running in the cloud."),(0,a.kt)("h2",{id:"tags"},"Tags"),(0,a.kt)("p",null,"SCI, cloud, cpu, memory, power-curve"),(0,a.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,a.kt)("p",null,"This tutorial builds on top of the ",(0,a.kt)("a",{parentName:"p",href:"/pipelines/teads"},"Teads curve")," pipeline tutorial. That tutorial demonstrates how to organize a pipeline that converts CPU utilization observations into CPU energy. This tutorial uses the same pipeline but goes several steps further, including converting the CPU energy estimates into carbon, adding the embodied carbon associated with the hardware being used and calculating the SCI score."),(0,a.kt)("h2",{id:"scope"},"Scope"),(0,a.kt)("p",null,"This SCI calculation takes into account the operational and embodied carbon of the server running our application. This includes the energy used to run the application, calculated from CPU and memory utilization, and the energy required to transfer data over the internet between server and client. It does not account for data center embodied carbon, embodied carbon of end user devices nor operational carbon in end user devices."),(0,a.kt)("h2",{id:"common-patterns"},"Common patterns"),(0,a.kt)("p",null,"We employ the well known power curve from ",(0,a.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"Davy, 2021")," to estimate CPU power from CPU utilization. You can find a detailed explanation of our implementation of this power curve methodology ",(0,a.kt)("a",{parentName:"p",href:"/pipelines/teads"},"here"),"."),(0,a.kt)("p",null,"We also use the networking energy and embodied carbon estimation methods from ",(0,a.kt)("a",{parentName:"p",href:"https://www.cloudcarbonfootprint.org/docs/methodology"},"Cloud Carbon Footprint"),". This includes using the networking energy coefficient they suggest and implementing their method for calculating embodied emissions in an ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/Green-Software-Foundation/if/tree/main/src/if-run/builtins/sci-embodied"},"Impact Framework plugin"),"."),(0,a.kt)("h2",{id:"observations"},"Observations"),(0,a.kt)("p",null,"This manifest requires the following observations:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"CPU utilization"),(0,a.kt)("li",{parentName:"ul"},"thermal design power of the processors"),(0,a.kt)("li",{parentName:"ul"},"number of vCPUs allocated to the application under observation"),(0,a.kt)("li",{parentName:"ul"},"total number of vCPUs available on the cloud instance being used"),(0,a.kt)("li",{parentName:"ul"},"the name of the cloud instance type being used"),(0,a.kt)("li",{parentName:"ul"},"the grid carbon intensity for the grid powering the data center"),(0,a.kt)("li",{parentName:"ul"},"data transferred in/out of the application"),(0,a.kt)("li",{parentName:"ul"},"users per timestep")),(0,a.kt)("h2",{id:"constants-and-coefficients"},"Constants and coefficients:"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"parameter"),(0,a.kt)("th",{parentName:"tr",align:null},"description"),(0,a.kt)("th",{parentName:"tr",align:null},"value"),(0,a.kt)("th",{parentName:"tr",align:null},"unit"),(0,a.kt)("th",{parentName:"tr",align:null},"source"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"network-energy-coefficient")),(0,a.kt)("td",{parentName:"tr",align:null},"Coefficient relating data sent over network to energy"),(0,a.kt)("td",{parentName:"tr",align:null},"0.001"),(0,a.kt)("td",{parentName:"tr",align:null},"kWh/GB"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"https://www.cloudcarbonfootprint.org/docs/methodology/#networking"},"CCF"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x"),", ",(0,a.kt)("inlineCode",{parentName:"td"},"y")),(0,a.kt)("td",{parentName:"tr",align:null},"Points on power curve relating CPU utilization to a coefficient used to scale the processor's thermal design power"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x: [0, 10, 50, 100], y: [0.12, 0.32, 0.75, 1.02]")),(0,a.kt)("td",{parentName:"tr",align:null},"dimensionless"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"Davy, 2021"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"baseline-emissions")),(0,a.kt)("td",{parentName:"tr",align:null},'embodied emissions for a "baseline" server with 1 CPU, 16GB RAM'),(0,a.kt)("td",{parentName:"tr",align:null},"1000000"),(0,a.kt)("td",{parentName:"tr",align:null},"gCO2e"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"https://www.cloudcarbonfootprint.org/docs/methodology/#embodied-emissions"},"CCF"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"lifespan")),(0,a.kt)("td",{parentName:"tr",align:null},"lifespan for the server running our application"),(0,a.kt)("td",{parentName:"tr",align:null},"126144000"),(0,a.kt)("td",{parentName:"tr",align:null},"seconds"),(0,a.kt)("td",{parentName:"tr",align:null},"none, assumed 4 years is typical")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"usage-ratio")),(0,a.kt)("td",{parentName:"tr",align:null},"scaling factor for adjusting total embodied carbon down tot he portion our application is responsible for"),(0,a.kt)("td",{parentName:"tr",align:null},"1"),(0,a.kt)("td",{parentName:"tr",align:null},"dimensionless"),(0,a.kt)("td",{parentName:"tr",align:null},"no usage scaling is done here as we assume dedicated hardware, we only scale by time")))),(0,a.kt)("h2",{id:"assumptions-and-limitations"},"Assumptions and limitations"),(0,a.kt)("p",null,"The following are assumed to be true in this manifest:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"the embodied carbon of the baseline server approximates the real embodied carbon of the server running our application"),(0,a.kt)("li",{parentName:"ul"},"the lifespan of the server running our application is really 4 years"),(0,a.kt)("li",{parentName:"ul"},"the coefficient relating data transferred over the network to energy is accurate"),(0,a.kt)("li",{parentName:"ul"},"the power curve relating CPU utilization to power is appropriate for the processor being used to run our application"),(0,a.kt)("li",{parentName:"ul"},"the coefficient relating memory utilization to energy is accurate"),(0,a.kt)("li",{parentName:"ul"},"it is appropriate to consider end user embodied carbon, end user operational carbon and the operationl and embodied emissions of the data center to be out of scope."),(0,a.kt)("li",{parentName:"ul"},"the temporal granularity of the observations are sufficient to accurately capture the behaviour of our application")),(0,a.kt)("h2",{id:"components"},"Components"),(0,a.kt)("p",null,"There is only one component in this example. It represents the entire application. The component pipeline looks as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"pipeline:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - sum-energy-components\n - embodied-carbon\n - operational-carbon\n - sum-carbon\n - sci\n")),(0,a.kt)("h2",{id:"plugins"},"Plugins"),(0,a.kt)("h3",{id:"interpolate"},"Interpolate"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"interpolate")," plugin is used once. The instance is named ",(0,a.kt)("inlineCode",{parentName:"p"},"interpolate"),". It is used to interpolate the curve relating CPU utilization and thermal-design-power factor so that the right value can be retrieved for the observed CPU utilization at each timestep."),(0,a.kt)("h4",{id:"config"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"method: linear\nx: [0, 10, 50, 100]\ny:[0.12, 0.32, 0.75, 1.02]\ninput-parameter: cpu/utilization\noutput-parameter: cpu-factor\n")),(0,a.kt)("h3",{id:"multiply"},"Multiply"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Multiply")," plugin is used several times. The instances are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"cpu-factor-to-wattage"),": used to multiply the thermal design power of the processor by the factor returned from the power curve interpolation, yielding power in Watts."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"wattage-times-duration"),": used to multiply the power in Watts by the duration of each timestep, yielding energy in W/duration."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"operational-carbon"),": used to convert energy into operational carbon by multiplying energy in kWh by the grid carbon intensity in gCO2/kWh.")),(0,a.kt)("h4",{id:"config-1"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"cpu-factor-to-wattage:\n input-parameters:\n - cpu-factor\n - cpu/thermal-design-power\n output-parameter:\n - cpu-wattage\n\nwattage-times-duration:\n input-parameters:\n - cpu-wattage\n - duration\n output-parameter:\n - cpu-wattage-times-duration\n\noperational-carbon:\n input-parameters:\n - energy\n - grid/carbon-intensity\n output-parameter:\n - carbon-operational\n\n")),(0,a.kt)("h3",{id:"divide"},"Divide"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Divide")," plugin is used once in this manifest. The instance is named ",(0,a.kt)("inlineCode",{parentName:"p"},"wattage-to-energy-kwh"),". It is used to convert energy in W/duration to kWh."),(0,a.kt)("h4",{id:"config-2"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"wattage-to-energy-kwh:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n")),(0,a.kt)("h3",{id:"sum"},"Sum"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Sum")," plugin is used several times in this manifest. The instances are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"sum-energy-components"),": used to sum all the various components of energy into a single value, called ",(0,a.kt)("inlineCode",{parentName:"li"},"energy"),"."),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"sum-carbon"),": used to sum the various components of carbon into a single value, named ",(0,a.kt)("inlineCode",{parentName:"li"},"carbon"),".")),(0,a.kt)("h4",{id:"config-3"},"config"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"sum-energy-components:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter:\n - energy\n\nsum-carbon:\n input-parameters:\n - carbon-operational\n - carbon-embodied\n output-parameter:\n - carbon\n")),(0,a.kt)("h3",{id:"sciembodied"},"SciEmbodied"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"SciEmbodied")," plugin is used once. Its purpose is to calculate the embodied emissions of the server running our application and apportion the total embodied carbon to the fraction that we are responsible (i.e. scale it based on the application only using a fraction of the available resources and a fraction of the server lifespan). We do not scale by resource allocation in this example, only time."),(0,a.kt)("h4",{id:"config-4"},"config"),(0,a.kt)("p",null,"We use the plugin defaults for all the ",(0,a.kt)("inlineCode",{parentName:"p"},"SciEmbodied")," config. This means we assume the total embodied emissions to be 1000000 gCO2e and the server to be a simple rack server with 1 CPU and 16GB RAM and no other components."),(0,a.kt)("h3",{id:"sci"},"SCI"),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"SCI")," plugin is used once. It is used to calculate the software carbon intensity by dividing ",(0,a.kt)("inlineCode",{parentName:"p"},"carbon")," by a functional unit, that has to be available in the manifest ",(0,a.kt)("inlineCode",{parentName:"p"},"inputs")," array at the time the plugin is executed. The functional unit in this example is users in each timestep."),(0,a.kt)("h4",{id:"config-5"},"config:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"sci:\nfunctional-unit: users\n")),(0,a.kt)("h2",{id:"manifest"},"Manifest"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sci example\ndescription:\ntags:\naggregation:\n metrics:\n - carbon\n - sci\n type: both\n\ninitialize:\n plugins:\n interpolate:\n path: builtin\n method: Interpolation\n config:\n method: linear\n x:\n - 0\n - 10\n - 50\n - 100\n 'y':\n - 0.12\n - 0.32\n - 0.75\n - 1.02\n input-parameter: cpu/utilization\n output-parameter: cpu-factor\n cpu-factor-to-wattage:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-factor\n - cpu/thermal-design-power\n output-parameter: cpu-wattage\n wattage-times-duration:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-wattage\n - duration\n output-parameter: cpu-wattage-times-duration\n wattage-to-energy-kwh:\n path: builtin\n method: Divide\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n calculate-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n correct-cpu-energy-for-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu/energy\n sum-energy-components:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy\n embodied-carbon:\n path: builtin\n method: SciEmbodied\n config:\n output-parameter: embodied-carbon\n operational-carbon:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - energy\n - grid/carbon-intensity\n output-parameter: carbon-operational\n sum-carbon:\n path: builtin\n method: Sum\n config:\n input-parameters:\n - carbon-operational\n - embodied-carbon\n output-parameter: carbon\n sci:\n path: builtin\n method: Sci\n config:\n functional-unit: users\n\ntree:\n children:\n github-storage-for-if-docs:\n pipeline:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - sum-energy-components\n - embodied-carbon\n - operational-carbon\n - sum-carbon\n - sci\n defaults:\n cpu/thermal-design-power: 100\n vcpus-total: 1\n vcpus-allocated: 1\n network/energy: 0.001\n grid/carbon-intensity: 130\n inputs:\n - timestamp: '2024-07-22T00:00:00'\n duration: 3600\n site-visits: 228\n cpu/utilization: 45\n component: 1\n users: 1100\n - timestamp: '2024-07-23T00:00:00'\n duration: 3600\n site-visits: 216\n cpu/utilization: 30\n component: 1\n users: 1050\n - timestamp: '2024-07-24T00:00:00'\n duration: 3600\n site-visits: 203\n cpu/utilization: 50\n component: 1\n users: 1055\n - timestamp: '2024-07-25T00:00:00'\n duration: 3600\n site-visits: 203\n cpu/utilization: 33\n component: 1\n users: 996\n - timestamp: '2024-07-26T00:00:00'\n duration: 3600\n site-visits: 172\n cpu/utilization: 29\n component: 1\n users: 899\n - timestamp: '2024-07-27T00:00:00'\n duration: 3600\n site-visits: 38\n cpu/utilization: 68\n component: 1\n users: 1080\n - timestamp: '2024-07-28T00:00:00'\n duration: 3600\n site-visits: 63\n cpu/utilization: 49\n component: 1\n users: 1099\n - timestamp: '2024-07-29T00:00:00'\n duration: 3600\n site-visits: 621\n cpu/utilization: 77\n component: 1\n users: 1120\n - timestamp: '2024-07-30T00:00:00'\n duration: 3600\n site-visits: 181\n cpu/utilization: 31\n component: 1\n users: 1125\n - timestamp: '2024-07-31T00:00:00'\n duration: 3600\n site-visits: 213\n cpu/utilization: 29\n component: 1\n users: 1113\n - timestamp: '2024-08-01T00:00:00'\n duration: 3600\n site-visits: 167\n cpu/utilization: 29\n component: 1\n users: 1111\n - timestamp: '2024-08-02T00:00:00'\n duration: 3600\n site-visits: 428\n cpu/utilization: 29\n component: 1\n users: 1230\n - timestamp: '2024-08-03T00:00:00'\n duration: 3600\n site-visits: 58\n cpu/utilization: 64\n component: 1\n users: 1223\n - timestamp: '2024-08-04T00:00:00'\n duration: 3600\n site-visits: 66\n cpu/utilization: 59\n component: 1\n users: 1210\n - timestamp: '2024-08-05T00:00:00'\n duration: 3600\n site-visits: 301\n cpu/utilization: 60\n component: 1\n users: 1011\n - timestamp: '2024-08-06T00:00:00'\n duration: 3600\n site-visits: 193\n cpu/utilization: 35\n component: 1\n users: 999\n - timestamp: '2024-08-07T00:00:00'\n duration: 3600\n site-visits: 220\n cpu/utilization: 37\n component: 1\n users: 1010\n - timestamp: '2024-08-08T00:00:00'\n duration: 3600\n site-visits: 215\n cpu/utilization: 43\n component: 1\n users: 1008\n - timestamp: '2024-08-09T00:00:00'\n duration: 3600\n site-visits: 516\n cpu/utilization: 28\n component: 1\n users: 992\n - timestamp: '2024-08-10T00:00:00'\n duration: 3600\n site-visits: 42\n cpu/utilization: 39\n component: 1\n users: 1101\n - timestamp: '2024-08-11T00:00:00'\n duration: 3600\n cpu/utilization: 40\n site-visits: 76\n component: 1\n users: 1000\n - timestamp: '2024-08-12T00:00:00'\n duration: 3600\n site-visits: 226\n cpu/utilization: 55\n component: 1\n users: 845\n - timestamp: '2024-08-13T00:00:00'\n duration: 3600\n site-visits: 180\n cpu/utilization: 62\n component: 1\n users: 1006\n - timestamp: '2024-08-14T00:00:00'\n duration: 3600\n site-visits: 232\n cpu/utilization: 71\n component: 1\n users: 1076\n - timestamp: '2024-08-15T00:00:00'\n duration: 3600\n site-visits: 175\n cpu/utilization: 75\n component: 1\n users: 1050\n - timestamp: '2024-08-16T00:00:00'\n duration: 3600\n site-visits: 235\n cpu/utilization: 77\n component: 1\n users: 1047\n - timestamp: '2024-08-17T00:00:00'\n duration: 3600\n site-visits: 44\n cpu/utilization: 80\n component: 1\n users: 1020\n - timestamp: '2024-08-18T00:00:00'\n duration: 3600\n site-visits: 31\n cpu/utilization: 84\n component: 1\n users: 1038\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/b5ada7f5.c2344d62.js b/assets/js/b5ada7f5.1886046d.js similarity index 63% rename from assets/js/b5ada7f5.c2344d62.js rename to assets/js/b5ada7f5.1886046d.js index a16a4850..0011f407 100644 --- a/assets/js/b5ada7f5.c2344d62.js +++ b/assets/js/b5ada7f5.1886046d.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[491],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var g=a.createContext({}),l=function(e){var t=a.useContext(g),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(g.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,g=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=l(n),u=r,d=c["".concat(g,".").concat(u)]||c[u]||m[u]||o;return n?a.createElement(d,i(i({ref:t},p),{},{components:n})):a.createElement(d,i({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=c;var s={};for(var g in t)hasOwnProperty.call(t,g)&&(s[g]=t[g]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>g,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=n(7462),r=(n(7294),n(4137));const o={sidebar_position:6},i="Aggregation",s={unversionedId:"major-concepts/aggregation",id:"major-concepts/aggregation",title:"Aggregation",description:"Aggregation is the process of summarizing a set of metrics.",source:"@site/docs/major-concepts/aggregation.md",sourceDirName:"major-concepts",slug:"/major-concepts/aggregation",permalink:"/major-concepts/aggregation",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/aggregation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Design philosophy",permalink:"/major-concepts/design-philosophy"},next:{title:"Pipelines",permalink:"/major-concepts/pipelines"}},g={},l=[{value:"Configuration",id:"configuration",level:2},{value:"Aggregation methods",id:"aggregation-methods",level:2},{value:"Aggregation outputs",id:"aggregation-outputs",level:2}],p={toc:l};function m(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"aggregation"},"Aggregation"),(0,r.kt)("p",null,"Aggregation is the process of summarizing a set of metrics."),(0,r.kt)("p",null,"Two types of aggregation can be executed in IF. The first takes a time series and condenses it down into a single number representing an entire observation period, for example, if your time series contains three timesteps, the value of the metric being aggregated is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," in each timestep and the aggregate is being computed as a sum, the aggregated value for the whole observation period is ",(0,r.kt)("inlineCode",{parentName:"p"},"3"),'. We refer to this as "time series aggregation" or "horizontal aggregation".'),(0,r.kt)("p",null,"The second type of aggregation happens across components in a tree. Where time series exist for multiple child nodes under a parent, their time series are aggregated together into one summary time series that is pushed to the parent node. For example, in the following tree, each child has a time series with three timesteps. At each timestep, the metric value is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," for both children:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"}," parent\n / \\\n / \\\nchild-1 child-2\ndata: [1,1,1] data: [1,1,1]\n")),(0,r.kt)("p",null,"Assuming the aggregation method is ",(0,r.kt)("inlineCode",{parentName:"p"},"sum"),", the parent would receive an aggregated time series ","[2,2,2]",', representing the aggregated values from both children. We refer to this as "tree aggregation" or "vertical aggregation".'),(0,r.kt)("h2",{id:"configuration"},"Configuration"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"Aggregate")," is a built-in feature of the IF. This means you do not need to initialize it along with the plugins you are using in your pipeline. All you need to do is to add a small piece of config to your manifest file."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregate")," config looks as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"aggregation:\n metrics:\n - 'carbon'\n - 'energy'\n type: 'both'\n")),(0,r.kt)("p",null,"There are two fields: ",(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"type"),"."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," is an array of metrics that you want to aggregate. You can provide any value here, but they must match a key that exists in your output data (i.e. if you tell IF to aggregate ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," but ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," is not in your outputs you will receive an error message and aggregation will fail). You can provide any number of metrics. In the example above, the aggregation feature will operate on the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"energy")," values."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"type")," determines which kind of aggregation you want to perform. The choices are ",(0,r.kt)("inlineCode",{parentName:"p"},"horizontal")," (time-series aggregation only), ",(0,r.kt)("inlineCode",{parentName:"p"},"vertical")," (tree aggregation only) or ",(0,r.kt)("inlineCode",{parentName:"p"},"both")," (both kinds of aggregation will be performed). In the example above, both types of aggregation will be performed over the two selected metrics."),(0,r.kt)("h2",{id:"aggregation-methods"},"Aggregation methods"),(0,r.kt)("p",null,"Aggregation can happen in several ways. In the example above, both of the selected metrics are absolute values measured in gCO2eq (carbon) and kWh (energy). To aggregate, it makes sense to add the values in each timestep for time-series aggregation and element-wise across components for tree aggregation. However, summing the values is not always the right way to aggregate."),(0,r.kt)("p",null,"For example, some values are constants that apply to every timestep - these should simply be copied into the aggregated metric and not summed. For example, if you are using a hard-coded value for grid carbon intensity that applies globally, then you simply want to persist that value into the aggregate - adding them together would provide a misleading result."),(0,r.kt)("p",null,"Similarly, some values are proportions or percentages. In these cases, the right way to aggregate is usually to take an average rather than summing over a set of values."),(0,r.kt)("h2",{id:"aggregation-outputs"},"Aggregation outputs"),(0,r.kt)("p",null,"The aggregation process adds new output data to your manifest file. The two types of aggregation add different outputs. The horizontal (time-series) aggregation adds a new field called ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," to each node whose time series has been aggregated. In the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," block, you will find the aggregated value for each of the aggregation metrics defined in the aggregation config."),(0,r.kt)("p",null,"The vertical aggregation adds a new array of output observations. These are simply named ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs")," and they always contain a timestamp and duration along with the aggregated metrics for each timestep."),(0,r.kt)("p",null,"The example below shows the result of running both kinds of aggregation for a single component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},' "outputs": [\n {\n "carbon": 0.04846481793320214,\n "energy": 0.00030285447154471535,\n "timestamp": "2023-12-12T00:00:00.000Z"\n },\n {\n "carbon": 0.037777724630840566,\n "energy": 0.00023606013840495548,\n "timestamp": "2023-12-12T00:00:05.000Z"\n },\n {\n "carbon": 0.03630388278027921,\n "energy": 0.000226848626838947,\n "timestamp": "2023-12-12T00:00:10.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:15.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:20.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:25.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:30.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:35.000Z"\n },\n ],\n "aggregated": {\n "carbon": 0.3246705689138855,\n "energy": 0.0020287555470867216\n }\n')))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[491],{4137:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var g=a.createContext({}),l=function(e){var t=a.useContext(g),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(g.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,g=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),c=l(n),u=r,d=c["".concat(g,".").concat(u)]||c[u]||m[u]||o;return n?a.createElement(d,i(i({ref:t},p),{},{components:n})):a.createElement(d,i({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=c;var s={};for(var g in t)hasOwnProperty.call(t,g)&&(s[g]=t[g]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>g,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=n(7462),r=(n(7294),n(4137));const o={sidebar_position:6},i="Aggregation",s={unversionedId:"major-concepts/aggregation",id:"major-concepts/aggregation",title:"Aggregation",description:"Aggregation is the process of summarizing a set of metrics.",source:"@site/docs/major-concepts/aggregation.md",sourceDirName:"major-concepts",slug:"/major-concepts/aggregation",permalink:"/major-concepts/aggregation",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/aggregation.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Design philosophy",permalink:"/major-concepts/design-philosophy"},next:{title:"Pipelines",permalink:"/major-concepts/pipelines"}},g={},l=[{value:"Configuration",id:"configuration",level:2},{value:"Aggregation methods",id:"aggregation-methods",level:2},{value:"Aggregation outputs",id:"aggregation-outputs",level:2}],p={toc:l};function m(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"aggregation"},"Aggregation"),(0,r.kt)("p",null,"Aggregation is the process of summarizing a set of metrics."),(0,r.kt)("p",null,"Two types of aggregation can be executed in IF. The first takes a time series and condenses it down into a single number representing an entire observation period, for example, if your time series contains three timesteps, the value of the metric being aggregated is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," in each timestep and the aggregate is being computed as a sum, the aggregated value for the whole observation period is ",(0,r.kt)("inlineCode",{parentName:"p"},"3"),'. We refer to this as "time series aggregation" or "horizontal aggregation".'),(0,r.kt)("p",null,"The second type of aggregation happens across components in a tree. Where time series exist for multiple child nodes under a parent, their time series are aggregated together into one summary time series that is pushed to the parent node. For example, in the following tree, each child has a time series with three timesteps. At each timestep, the metric value is ",(0,r.kt)("inlineCode",{parentName:"p"},"1")," for both children:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"}," parent\n / \\\n / \\\nchild-1 child-2\ndata: [1,1,1] data: [1,1,1]\n")),(0,r.kt)("p",null,"Assuming the aggregation method is ",(0,r.kt)("inlineCode",{parentName:"p"},"sum"),", the parent would receive an aggregated time series ","[2,2,2]",', representing the aggregated values from both children. We refer to this as "tree aggregation" or "vertical aggregation".'),(0,r.kt)("h2",{id:"configuration"},"Configuration"),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"Aggregate")," is a built-in feature of the IF. This means you do not need to initialize it along with the plugins you are using in your pipeline. All you need to do is to add a small piece of config to your manifest file."),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregate")," config looks as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"aggregation:\n metrics:\n - 'carbon'\n - 'energy'\n type: 'both'\n")),(0,r.kt)("p",null,"There are two fields: ",(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"type"),"."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"metrics")," is an array of metrics that you want to aggregate. You can provide any value here, but they must match a key that exists in your output data (i.e. if you tell IF to aggregate ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," but ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," is not in your outputs you will receive an error message and aggregation will fail). You can provide any number of metrics. In the example above, the aggregation feature will operate on the ",(0,r.kt)("inlineCode",{parentName:"p"},"carbon")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"energy")," values."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"type")," determines which kind of aggregation you want to perform. The choices are ",(0,r.kt)("inlineCode",{parentName:"p"},"time")," (previously ",(0,r.kt)("inlineCode",{parentName:"p"},"horizontal"),": time-series aggregation only), ",(0,r.kt)("inlineCode",{parentName:"p"},"component")," (previously ",(0,r.kt)("inlineCode",{parentName:"p"},"vertical"),": tree aggregation only) or ",(0,r.kt)("inlineCode",{parentName:"p"},"both")," (both kinds of aggregation will be performed). In the example above, both types of aggregation will be performed over the two selected metrics."),(0,r.kt)("h2",{id:"aggregation-methods"},"Aggregation methods"),(0,r.kt)("p",null,"Aggregation can happen in several ways. In the example above, both of the selected metrics are absolute values measured in gCO2eq (carbon) and kWh (energy). To aggregate, it makes sense to add the values in each timestep for time-series aggregation and element-wise across components for tree aggregation. However, summing the values is not always the right way to aggregate."),(0,r.kt)("p",null,"For example, some values are constants that apply to every timestep - these should simply be copied into the aggregated metric and not summed. For example, if you are using a hard-coded value for grid carbon intensity that applies globally, then you simply want to persist that value into the aggregate - adding them together would provide a misleading result."),(0,r.kt)("p",null,"Similarly, some values are proportions or percentages. In these cases, the right way to aggregate is usually to take an average rather than summing over a set of values."),(0,r.kt)("h2",{id:"aggregation-outputs"},"Aggregation outputs"),(0,r.kt)("p",null,"The aggregation process adds new output data to your manifest file. The two types of aggregation add different outputs. The horizontal (time-series) aggregation adds a new field called ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," to each node whose time series has been aggregated. In the ",(0,r.kt)("inlineCode",{parentName:"p"},"aggregated")," block, you will find the aggregated value for each of the aggregation metrics defined in the aggregation config."),(0,r.kt)("p",null,"The vertical aggregation adds a new array of output observations. These are simply named ",(0,r.kt)("inlineCode",{parentName:"p"},"outputs")," and they always contain a timestamp and duration along with the aggregated metrics for each timestep."),(0,r.kt)("p",null,"The example below shows the result of running both kinds of aggregation for a single component:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},' "outputs": [\n {\n "carbon": 0.04846481793320214,\n "energy": 0.00030285447154471535,\n "timestamp": "2023-12-12T00:00:00.000Z"\n },\n {\n "carbon": 0.037777724630840566,\n "energy": 0.00023606013840495548,\n "timestamp": "2023-12-12T00:00:05.000Z"\n },\n {\n "carbon": 0.03630388278027921,\n "energy": 0.000226848626838947,\n "timestamp": "2023-12-12T00:00:10.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:15.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:20.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:25.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:30.000Z"\n },\n {\n "carbon": 0.0360935970659935,\n "energy": 0.0002255343411246613,\n "timestamp": "2023-12-12T00:00:35.000Z"\n },\n ],\n "aggregated": {\n "carbon": 0.3246705689138855,\n "energy": 0.0020287555470867216\n }\n')))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/cca8bc57.8d17d27a.js b/assets/js/cca8bc57.906ff5a6.js similarity index 55% rename from assets/js/cca8bc57.8d17d27a.js rename to assets/js/cca8bc57.906ff5a6.js index fab1ec9f..ae107365 100644 --- a/assets/js/cca8bc57.8d17d27a.js +++ b/assets/js/cca8bc57.906ff5a6.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[112],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>d});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(n),d=i,g=m["".concat(p,".").concat(d)]||m[d]||c[d]||o;return n?r.createElement(g,a(a({ref:t},u),{},{components:n})):r.createElement(g,a({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),i=(n(7294),n(4137));const o={sidebar_position:3},a="How to load plugins",l={unversionedId:"users/how-to-import-plugins",id:"users/how-to-import-plugins",title:"How to load plugins",description:"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins come bundled with IF. These are known as builtins.",source:"@site/docs/users/how-to-import-plugins.md",sourceDirName:"users",slug:"/users/how-to-import-plugins",permalink:"/users/how-to-import-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-import-plugins.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"},next:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"}},p={},s=[],u={toc:s};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-load-plugins"},"How to load plugins"),(0,i.kt)("p",null,"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins come bundled with IF. These are known as ",(0,i.kt)("inlineCode",{parentName:"p"},"builtins"),". "),(0,i.kt)("p",null,"Builtins have to be initialized in a manifest file using the path ",(0,i.kt)("inlineCode",{parentName:"p"},"builtin"),". Then they can be invoked in pipelines."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},'name: if-demo\ndescription: demo pipeline\ntags:\ninitialize:\n plugins:\n "sum":\n path: "builtin"\n method: Sum\n global-config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy-sum\n')),(0,i.kt)("p",null,"Other plugins are hosted externally to the IF. Anyone can build a plugin and provide it as an npm package or a public code repository (such as Github) and share it using our ",(0,i.kt)("a",{parentName:"p",href:"https://explorer.if.greensoftware.foundation"},"Explorer"),"."),(0,i.kt)("p",null,"These external plugins are loaded into IF by installing locally and initializing in a manifest."),(0,i.kt)("p",null,"First, install the plugin by providing the path to the repository to ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install")," as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm install https://github.com/some-account/some-repo\n")),(0,i.kt)("p",null,"Then, in the manifest's ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," section, you'll need to provide the following fields:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"YOUR-PLUGIN-HERE"),": a name to reference this specific instance of the plugin. The same name has to be used to refer to this plugin instance everywhere across the manifest"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"method"),": the function name exported by your plugin, e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"AzureImporter")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin")),(0,i.kt)("p",null,"And, if your plugin requires it, add its ",(0,i.kt)("inlineCode",{parentName:"p"},"global-config")," too."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n {n.d(t,{Zo:()=>u,kt:()=>d});var r=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),m=s(n),d=i,g=m["".concat(p,".").concat(d)]||m[d]||c[d]||o;return n?r.createElement(g,a(a({ref:t},u),{},{components:n})):r.createElement(g,a({ref:t},u))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,a=new Array(o);a[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),i=(n(7294),n(4137));const o={sidebar_position:3},a="How to load plugins",l={unversionedId:"users/how-to-import-plugins",id:"users/how-to-import-plugins",title:"How to load plugins",description:"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins come bundled with IF. These are known as builtins.",source:"@site/docs/users/how-to-import-plugins.md",sourceDirName:"users",slug:"/users/how-to-import-plugins",permalink:"/users/how-to-import-plugins",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/users/how-to-import-plugins.md",tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"How to install Impact Framework",permalink:"/users/how-to-install-if"},next:{title:"How to write a manifest file",permalink:"/users/how-to-write-manifests"}},p={},s=[],u={toc:s};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"how-to-load-plugins"},"How to load plugins"),(0,i.kt)("p",null,"Plugins are developed separately to the Impact Framework core. However, the IF core developers maintain a standard library of plugins come bundled with IF. These are known as ",(0,i.kt)("inlineCode",{parentName:"p"},"builtins"),"."),(0,i.kt)("p",null,"Builtins have to be initialized in a manifest file using the path ",(0,i.kt)("inlineCode",{parentName:"p"},"builtin"),". Then they can be invoked in pipelines."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: if-demo\ndescription: demo pipeline\ntags:\ninitialize:\n plugins:\n 'sum':\n path: 'builtin'\n method: Sum\n config:\n input-parameters:\n - cpu/energy\n - network/energy\n output-parameter: energy-sum\n")),(0,i.kt)("p",null,"Other plugins are hosted externally to the IF. Anyone can build a plugin and provide it as an npm package or a public code repository (such as Github) and share it using our ",(0,i.kt)("a",{parentName:"p",href:"https://explorer.if.greensoftware.foundation"},"Explorer"),"."),(0,i.kt)("p",null,"These external plugins are loaded into IF by installing locally and initializing in a manifest."),(0,i.kt)("p",null,"First, install the plugin by providing the path to the repository to ",(0,i.kt)("inlineCode",{parentName:"p"},"npm install")," as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-sh"},"npm install https://github.com/some-account/some-repo\n")),(0,i.kt)("p",null,"Then, in the manifest's ",(0,i.kt)("inlineCode",{parentName:"p"},"initialize")," section, you'll need to provide the following fields:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"YOUR-PLUGIN-HERE"),": a name to reference this specific instance of the plugin. The same name has to be used to refer to this plugin instance everywhere across the manifest"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"method"),": the function name exported by your plugin, e.g. ",(0,i.kt)("inlineCode",{parentName:"li"},"AzureImporter")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"path"),": the path to the plugin")),(0,i.kt)("p",null,"And, if your plugin requires it, add its ",(0,i.kt)("inlineCode",{parentName:"p"},"config")," too."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: plugin-demo\ndescription: loads plugin\ntags: null\ninitialize:\n plugins:\n {n.d(t,{Zo:()=>s,kt:()=>m});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=a.createContext({}),u=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=u(e.components);return a.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),d=u(n),m=i,g=d["".concat(p,".").concat(m)]||d[m]||c[m]||r;return n?a.createElement(g,o(o({ref:t},s),{},{components:n})):a.createElement(g,o({ref:t},s))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=d;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>u});var a=n(7462),i=(n(7294),n(4137));const r={sidebar_position:1},o="From CPU utilization to carbon emissions",l={unversionedId:"pipelines/cpu-to-carbon",id:"pipelines/cpu-to-carbon",title:"From CPU utilization to carbon emissions",description:"Tags",source:"@site/docs/pipelines/cpu-to-carbon.md",sourceDirName:"pipelines",slug:"/pipelines/cpu-to-carbon",permalink:"/pipelines/cpu-to-carbon",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/cpu-to-carbon.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Pipelines",permalink:"/pipelines/"},next:{title:"Grabbing instance metadata from a CSV file",permalink:"/pipelines/instance-metadata"}},p={},u=[{value:"Tags",id:"tags",level:2},{value:"Observations",id:"observations",level:2},{value:"Impacts",id:"impacts",level:2},{value:"Scope",id:"scope",level:2},{value:"Description",id:"description",level:2},{value:"Common patterns",id:"common-patterns",level:2},{value:"Constants and coefficients:",id:"constants-and-coefficients",level:2},{value:"Assumptions and limitations",id:"assumptions-and-limitations",level:2},{value:"Components",id:"components",level:2},{value:"Plugins",id:"plugins",level:2},{value:"Interpolate",id:"interpolate",level:3},{value:"config",id:"config",level:4},{value:"Multiply",id:"multiply",level:3},{value:"config",id:"config-1",level:4},{value:"Divide",id:"divide",level:3},{value:"config",id:"config-2",level:4},{value:"Manifest",id:"manifest",level:2}],s={toc:u};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"from-cpu-utilization-to-carbon-emissions"},"From CPU utilization to carbon emissions"),(0,i.kt)("h2",{id:"tags"},"Tags"),(0,i.kt)("p",null,"carbon, teads, power-curve"),(0,i.kt)("h2",{id:"observations"},"Observations"),(0,i.kt)("p",null,"This manifest requires the following observations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"CPU utilization"),(0,i.kt)("li",{parentName:"ul"},"thermal design power of the processors"),(0,i.kt)("li",{parentName:"ul"},"number of vCPUs allocated to the application under observation"),(0,i.kt)("li",{parentName:"ul"},"total number of vCPUs available on the cloud instance being used"),(0,i.kt)("li",{parentName:"ul"},"the name of the cloud instance type being used"),(0,i.kt)("li",{parentName:"ul"},"the grid carbon intensity for the grid powering the data center")),(0,i.kt)("h2",{id:"impacts"},"Impacts"),(0,i.kt)("p",null,"This pipeline takes the observations described above, and generates carbon emissions in each timestep, expressed in gCO2e."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This pipeline takes into account the operational carbon of the server running our application. This includes the energy used to run the application, calculated from CPU and memory utilization. It does not account for any embodied carbon, nor networking energy, nor anything related to the end user. In real applications, the pipeline described here will be part of a much larger manifest that considers other parts of the system. "),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("p",null,"The Teads CPU power curve CPU utilization (as a percentage) against a scaling factor that can be applied to the CPUs thermal design power to estimate the power drawn by the CPU in Watts. "),(0,i.kt)("p",null,"The research underpinning the curve was summarized in a pair of blog posts:"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"TEADS Engineering: Buildiong an AWS EC2 Carbon Emissions Dataset"),"\n",(0,i.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/estimating-aws-ec2-instances-power-consumption-c9745e347959"},"Teads Engineering: Estimating AWS EC2 Instances Power Consumption")),(0,i.kt)("p",null,"The curve has become very widely used as a general purpose utilization-to-wattage converter for CPUs, despite the fact that it does not generalize well."),(0,i.kt)("p",null,"The wattage can be transformed into energy by doing the following:"),(0,i.kt)("p",null,"1) Measure your CPU utilization\n2) Determine the thermal design power of your processor\n3) Determine the scaling factor for your CPU utilization by interpolating the Teads curve\n4) Determine the power drawn by your CPU by multiplying your scaling factor by the CPU's thermal design power\n5) Perform a unit conversion to convert power in Watts to energy in kwH\n6) Scale the energy estimated for the entire chip to the portion of the chip that is actually in use."),(0,i.kt)("p",null,"These steps can be executed in IF using just three plugins:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Interpolate")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Multiply")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Divide"))),(0,i.kt)("h2",{id:"common-patterns"},"Common patterns"),(0,i.kt)("p",null,"The logical flow from CPU utilization to carbon via a power-curve and thermal design power is a common pattern that is likely to be re-used elsewhere."),(0,i.kt)("h2",{id:"constants-and-coefficients"},"Constants and coefficients:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"parameter"),(0,i.kt)("th",{parentName:"tr",align:null},"description"),(0,i.kt)("th",{parentName:"tr",align:null},"value"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"x"),", ",(0,i.kt)("inlineCode",{parentName:"td"},"y")),(0,i.kt)("td",{parentName:"tr",align:null},"Points on power curve relating CPU utilization to a coefficient used to scale the processor's thermal design power"),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"x: [0, 10, 50, 100], y: [0.12, 0.32, 0.75, 1.02]"))),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"grid-carbon-intensity")),(0,i.kt)("td",{parentName:"tr",align:null},"the carbon emitted per unit energy from the electrical grid"),(0,i.kt)("td",{parentName:"tr",align:null},"750")))),(0,i.kt)("h2",{id:"assumptions-and-limitations"},"Assumptions and limitations"),(0,i.kt)("p",null,"The following are assumed to be true in this manifest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"the power curve relating CPU utilization to power is appropriate for the processor being used to run our application"),(0,i.kt)("li",{parentName:"ul"},"the temporal granularity of the observations are sufficient to accurately capture the behaviour of our application"),(0,i.kt)("li",{parentName:"ul"},"the grid carbon intensity is sufficiently accurate for the location where the computational work is done")),(0,i.kt)("h2",{id:"components"},"Components"),(0,i.kt)("p",null,"There is only one component in this example. It represents the entire application. The component pipeline looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"pipeline:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - energy-to-carbon\n")),(0,i.kt)("h2",{id:"plugins"},"Plugins"),(0,i.kt)("h3",{id:"interpolate"},"Interpolate"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"interpolate")," plugin is used once. The instance is named ",(0,i.kt)("inlineCode",{parentName:"p"},"interpolate"),". It is used to interpolate the curve relating CPU utilization and thermal-design-power factor so that the right value can be retrieved for the observed CPU utilization at each timestep."),(0,i.kt)("h4",{id:"config"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"method: linear\nx: [0, 10, 50, 100]\ny:[0.12, 0.32, 0.75, 1.02]\ninput-parameter: cpu/utilization\noutput-parameter: cpu-factor\n")),(0,i.kt)("h3",{id:"multiply"},"Multiply"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"Multiply")," plugin is used several times. The instances are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cpu-factor-to-wattage"),": used to multiply the thermal design power of the processor by the factor returned from the power curve interpolation, yielding power in Watts."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wattage-times-duration"),": used to multiply the power in Watts by the duration of each timestep, yielding energy in W/duration."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"energy-to-carbon"),": used to convert energy expended to carbon emitted.")),(0,i.kt)("h4",{id:"config-1"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cpu-factor-to-wattage:\ninput-parameters:\n - cpu-factor\n - cpu/thermal-design-power\noutput-parameter: \n - cpu-wattage\n\nwattage-times-duration:\ninput-parameters:\n - cpu-wattage\n - duration\noutput-parameter: \n - cpu-wattage-times-duration\n\nenergy-to-carbon:\ninput-parameters:\n - grid-carbon-intensity\n - energy-cpu-kwh\noutput-parameter: \n - carbon\n\n")),(0,i.kt)("h3",{id:"divide"},"Divide"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"Divide")," plugin is used several times in this manifest. The instances are: "),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wattage-to-energy-kwh"),". used to convert energy in W/duration to kWh."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"calculate-vcpu-ratio"),": used to calculate the ratio of allocated vCPUs to total vCPUS"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"correct-cpu-energy-for-vcpu-ratio"),": used to scale the CPU energy by the vCPU ratio")),(0,i.kt)("h4",{id:"config-2"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"wattage-to-energy-kwh:\nnumerator: cpu-wattage-times-duration\ndenominator: 3600000\noutput: cpu-energy-raw\n\ncalculate-vcpu-ratio:\nnumerator: vcpus-total\ndenominator: vcpus-allocated\noutput: vcpu-ratio\n\ncorrect-cpu-energy-for-vcpu-ratio:\nnumerator: cpu-energy-raw\ndenominator: vcpu-ratio\noutput: cpu/energy\n\n")),(0,i.kt)("h2",{id:"manifest"},"Manifest"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: teads curve demo\ndescription: null\ntags: null\ninitialize:\n plugins:\n interpolate:\n path: builtin\n method: Interpolation\n config:\n method: linear\n x:\n - 0\n - 10\n - 50\n - 100\n 'y':\n - 0.12\n - 0.32\n - 0.75\n - 1.02\n input-parameter: cpu/utilization\n output-parameter: cpu-factor\n cpu-factor-to-wattage:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-factor\n - thermal-design-power\n output-parameter: cpu-wattage\n wattage-times-duration:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-wattage\n - duration\n output-parameter: cpu-wattage-times-duration\n wattage-to-energy-kwh:\n path: builtin\n method: Divide\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n calculate-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n correct-cpu-energy-for-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu-energy-kwh\n energy-to-carbon:\n path: builtin\n method: Multiply\n config:\n input-parameters: ['grid-carbon-intensity', 'cpu-energy-kwh']\n output-parameter: 'carbon'\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/if/src/index.ts -m manifests/examples/teads-curve.yml\n environment:\n if-version: 0.3.3-beta.0\n os: linux\n os-version: 5.15.0-107-generic\n node-version: 21.4.0\n date-time: 2024-06-06T14:33:25.188Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-unofficial-plugins@v0.3.1'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.22.4\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - energy-to-carbon\n defaults:\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n grid-carbon-intensity: 750\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 0.13999999999999999\n cpu-wattage: 13.999999999999998\n cpu-wattage-times-duration: 5039.999999999999\n cpu-energy-raw: 0.0013999999999999998\n vcpu-ratio: 4\n cpu-energy-kwh: 0.00034999999999999994\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 0.32\n cpu-wattage: 32\n cpu-wattage-times-duration: 11520\n cpu-energy-raw: 0.0032\n vcpu-ratio: 4\n cpu-energy-kwh: 0.0008\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 0.75\n cpu-wattage: 75\n cpu-wattage-times-duration: 27000\n cpu-energy-raw: 0.0075\n vcpu-ratio: 4\n cpu-energy-kwh: 0.001875\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n cpu-factor: 1.02\n cpu-wattage: 102\n cpu-wattage-times-duration: 36720\n cpu-energy-raw: 0.0102\n vcpu-ratio: 4\n cpu-energy-kwh: 0.00255\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ce9192f0.f8871535.js b/assets/js/ce9192f0.f8871535.js new file mode 100644 index 00000000..dabe2b5e --- /dev/null +++ b/assets/js/ce9192f0.f8871535.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[383],{4137:(e,t,n)=>{n.d(t,{Zo:()=>s,kt:()=>d});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var p=a.createContext({}),u=function(e){var t=a.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},s=function(e){var t=u(e.components);return a.createElement(p.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=u(n),d=i,g=m["".concat(p,".").concat(d)]||m[d]||c[d]||r;return n?a.createElement(g,o(o({ref:t},s),{},{components:n})):a.createElement(g,o({ref:t},s))}));function d(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var u=2;u{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>c,frontMatter:()=>r,metadata:()=>l,toc:()=>u});var a=n(7462),i=(n(7294),n(4137));const r={sidebar_position:1},o="From CPU utilization to carbon emissions",l={unversionedId:"pipelines/cpu-to-carbon",id:"pipelines/cpu-to-carbon",title:"From CPU utilization to carbon emissions",description:"Tags",source:"@site/docs/pipelines/cpu-to-carbon.md",sourceDirName:"pipelines",slug:"/pipelines/cpu-to-carbon",permalink:"/pipelines/cpu-to-carbon",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/pipelines/cpu-to-carbon.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Pipelines",permalink:"/pipelines/"},next:{title:"Teads CPU pipeline",permalink:"/pipelines/teads"}},p={},u=[{value:"Tags",id:"tags",level:2},{value:"Observations",id:"observations",level:2},{value:"Impacts",id:"impacts",level:2},{value:"Scope",id:"scope",level:2},{value:"Description",id:"description",level:2},{value:"Common patterns",id:"common-patterns",level:2},{value:"Constants and coefficients:",id:"constants-and-coefficients",level:2},{value:"Assumptions and limitations",id:"assumptions-and-limitations",level:2},{value:"Components",id:"components",level:2},{value:"Plugins",id:"plugins",level:2},{value:"Interpolate",id:"interpolate",level:3},{value:"config",id:"config",level:4},{value:"Multiply",id:"multiply",level:3},{value:"config",id:"config-1",level:4},{value:"Divide",id:"divide",level:3},{value:"config",id:"config-2",level:4},{value:"Manifest",id:"manifest",level:2}],s={toc:u};function c(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"from-cpu-utilization-to-carbon-emissions"},"From CPU utilization to carbon emissions"),(0,i.kt)("h2",{id:"tags"},"Tags"),(0,i.kt)("p",null,"carbon, teads, power-curve"),(0,i.kt)("h2",{id:"observations"},"Observations"),(0,i.kt)("p",null,"This manifest requires the following observations:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"CPU utilization"),(0,i.kt)("li",{parentName:"ul"},"thermal design power of the processors"),(0,i.kt)("li",{parentName:"ul"},"number of vCPUs allocated to the application under observation"),(0,i.kt)("li",{parentName:"ul"},"total number of vCPUs available on the cloud instance being used"),(0,i.kt)("li",{parentName:"ul"},"the name of the cloud instance type being used"),(0,i.kt)("li",{parentName:"ul"},"the grid carbon intensity for the grid powering the data center")),(0,i.kt)("h2",{id:"impacts"},"Impacts"),(0,i.kt)("p",null,"This pipeline takes the observations described above, and generates carbon emissions in each timestep, expressed in gCO2e."),(0,i.kt)("h2",{id:"scope"},"Scope"),(0,i.kt)("p",null,"This pipeline takes into account the operational carbon of the server running our application. This includes the energy used to run the application, calculated from CPU and memory utilization. It does not account for any embodied carbon, nor networking energy, nor anything related to the end user. In real applications, the pipeline described here will be part of a much larger manifest that considers other parts of the system."),(0,i.kt)("h2",{id:"description"},"Description"),(0,i.kt)("p",null,"The Teads CPU power curve CPU utilization (as a percentage) against a scaling factor that can be applied to the CPUs thermal design power to estimate the power drawn by the CPU in Watts."),(0,i.kt)("p",null,"The research underpinning the curve was summarized in a pair of blog posts:"),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"TEADS Engineering: Buildiong an AWS EC2 Carbon Emissions Dataset"),"\n",(0,i.kt)("a",{parentName:"p",href:"https://medium.com/teads-engineering/estimating-aws-ec2-instances-power-consumption-c9745e347959"},"Teads Engineering: Estimating AWS EC2 Instances Power Consumption")),(0,i.kt)("p",null,"The curve has become very widely used as a general purpose utilization-to-wattage converter for CPUs, despite the fact that it does not generalize well."),(0,i.kt)("p",null,"The wattage can be transformed into energy by doing the following:"),(0,i.kt)("ol",null,(0,i.kt)("li",{parentName:"ol"},"Measure your CPU utilization"),(0,i.kt)("li",{parentName:"ol"},"Determine the thermal design power of your processor"),(0,i.kt)("li",{parentName:"ol"},"Determine the scaling factor for your CPU utilization by interpolating the Teads curve"),(0,i.kt)("li",{parentName:"ol"},"Determine the power drawn by your CPU by multiplying your scaling factor by the CPU's thermal design power"),(0,i.kt)("li",{parentName:"ol"},"Perform a unit conversion to convert power in Watts to energy in kwH"),(0,i.kt)("li",{parentName:"ol"},"Scale the energy estimated for the entire chip to the portion of the chip that is actually in use.")),(0,i.kt)("p",null,"These steps can be executed in IF using just three plugins:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Interpolate")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Multiply")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"Divide"))),(0,i.kt)("h2",{id:"common-patterns"},"Common patterns"),(0,i.kt)("p",null,"The logical flow from CPU utilization to carbon via a power-curve and thermal design power is a common pattern that is likely to be re-used elsewhere."),(0,i.kt)("h2",{id:"constants-and-coefficients"},"Constants and coefficients:"),(0,i.kt)("table",null,(0,i.kt)("thead",{parentName:"table"},(0,i.kt)("tr",{parentName:"thead"},(0,i.kt)("th",{parentName:"tr",align:null},"parameter"),(0,i.kt)("th",{parentName:"tr",align:null},"description"),(0,i.kt)("th",{parentName:"tr",align:null},"value"),(0,i.kt)("th",{parentName:"tr",align:null},"unit"),(0,i.kt)("th",{parentName:"tr",align:null},"source"))),(0,i.kt)("tbody",{parentName:"table"},(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"x"),", ",(0,i.kt)("inlineCode",{parentName:"td"},"y")),(0,i.kt)("td",{parentName:"tr",align:null},"Points on power curve relating CPU utilization to a coefficient used to scale the processor's thermal design power"),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"x: [0, 10, 50, 100], y: [0.12, 0.32, 0.75, 1.02]")),(0,i.kt)("td",{parentName:"tr",align:null},"dimensionless"),(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("a",{parentName:"td",href:"https://medium.com/teads-engineering/building-an-aws-ec2-carbon-emissions-dataset-3f0fd76c98ac"},"Davy, 2021"))),(0,i.kt)("tr",{parentName:"tbody"},(0,i.kt)("td",{parentName:"tr",align:null},(0,i.kt)("inlineCode",{parentName:"td"},"grid-carbon-intensity")),(0,i.kt)("td",{parentName:"tr",align:null},"the carbon emitted per unit energy from the electrical grid"),(0,i.kt)("td",{parentName:"tr",align:null},"750"),(0,i.kt)("td",{parentName:"tr",align:null},"gCO2e/kWh"),(0,i.kt)("td",{parentName:"tr",align:null},"approximates global average")))),(0,i.kt)("h2",{id:"assumptions-and-limitations"},"Assumptions and limitations"),(0,i.kt)("p",null,"The following are assumed to be true in this manifest:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"the power curve relating CPU utilization to power is appropriate for the processor being used to run our application"),(0,i.kt)("li",{parentName:"ul"},"the temporal granularity of the observations are sufficient to accurately capture the behaviour of our application"),(0,i.kt)("li",{parentName:"ul"},"the grid carbon intensity is sufficiently accurate for the location where the computational work is done")),(0,i.kt)("h2",{id:"components"},"Components"),(0,i.kt)("p",null,"There is only one component in this example. It represents the entire application. The component pipeline looks as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"pipeline:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - energy-to-carbon\n")),(0,i.kt)("h2",{id:"plugins"},"Plugins"),(0,i.kt)("h3",{id:"interpolate"},"Interpolate"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"interpolate")," plugin is used once. The instance is named ",(0,i.kt)("inlineCode",{parentName:"p"},"interpolate"),". It is used to interpolate the curve relating CPU utilization and thermal-design-power factor so that the right value can be retrieved for the observed CPU utilization at each timestep."),(0,i.kt)("h4",{id:"config"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"method: linear\nx: [0, 10, 50, 100]\ny:[0.12, 0.32, 0.75, 1.02]\ninput-parameter: cpu/utilization\noutput-parameter: cpu-factor\n")),(0,i.kt)("h3",{id:"multiply"},"Multiply"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"Multiply")," plugin is used several times. The instances are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"cpu-factor-to-wattage"),": used to multiply the thermal design power of the processor by the factor returned from the power curve interpolation, yielding power in Watts."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wattage-times-duration"),": used to multiply the power in Watts by the duration of each timestep, yielding energy in W/duration."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"energy-to-carbon"),": used to convert energy expended to carbon emitted.")),(0,i.kt)("h4",{id:"config-1"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cpu-factor-to-wattage:\ninput-parameters:\n - cpu-factor\n - cpu/thermal-design-power\noutput-parameter:\n - cpu-wattage\n\nwattage-times-duration:\ninput-parameters:\n - cpu-wattage\n - duration\noutput-parameter:\n - cpu-wattage-times-duration\n\nenergy-to-carbon:\ninput-parameters:\n - grid-carbon-intensity\n - energy-cpu-kwh\noutput-parameter:\n - carbon\n\n")),(0,i.kt)("h3",{id:"divide"},"Divide"),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"Divide")," plugin is used several times in this manifest. The instances are:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"wattage-to-energy-kwh"),". used to convert energy in W/duration to kWh."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"calculate-vcpu-ratio"),": used to calculate the ratio of allocated vCPUs to total vCPUS"),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"correct-cpu-energy-for-vcpu-ratio"),": used to scale the CPU energy by the vCPU ratio")),(0,i.kt)("h4",{id:"config-2"},"config"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"wattage-to-energy-kwh:\nnumerator: cpu-wattage-times-duration\ndenominator: 3600000\noutput: cpu-energy-raw\n\ncalculate-vcpu-ratio:\nnumerator: vcpus-total\ndenominator: vcpus-allocated\noutput: vcpu-ratio\n\ncorrect-cpu-energy-for-vcpu-ratio:\nnumerator: cpu-energy-raw\ndenominator: vcpu-ratio\noutput: cpu/energy\n\n")),(0,i.kt)("h2",{id:"manifest"},"Manifest"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-yaml"},"name: teads curve demo\ndescription: null\ntags: null\ninitialize:\n plugins:\n interpolate:\n path: builtin\n method: Interpolation\n config:\n method: linear\n x:\n - 0\n - 10\n - 50\n - 100\n 'y':\n - 0.12\n - 0.32\n - 0.75\n - 1.02\n input-parameter: cpu/utilization\n output-parameter: cpu-factor\n cpu-factor-to-wattage:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-factor\n - thermal-design-power\n output-parameter: cpu-wattage\n wattage-times-duration:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - cpu-wattage\n - duration\n output-parameter: cpu-wattage-times-duration\n wattage-to-energy-kwh:\n path: builtin\n method: Divide\n config:\n numerator: cpu-wattage-times-duration\n denominator: 3600000\n output: cpu-energy-raw\n calculate-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: vcpus-total\n denominator: vcpus-allocated\n output: vcpu-ratio\n correct-cpu-energy-for-vcpu-ratio:\n path: builtin\n method: Divide\n config:\n numerator: cpu-energy-raw\n denominator: vcpu-ratio\n output: cpu-energy-kwh\n energy-to-carbon:\n path: builtin\n method: Multiply\n config:\n input-parameters:\n - grid-carbon-intensity\n - cpu-energy-kwh\n output-parameter: carbon\nexecution:\n command: >-\n /home/user/.npm/_npx/1bf7c3c15bf47d04/node_modules/.bin/ts-node\n /home/user/if/src/index.ts -m manifests/examples/teads-curve.yml\n environment:\n if-version: 0.6.0\n os: macOS\n os-version: 14.6.1\n node-version: 18.20.4\n date-time: 2024-10-03T15:11:48.498Z (UTC)\n dependencies:\n - '@babel/core@7.22.10'\n - '@babel/preset-typescript@7.23.3'\n - '@commitlint/cli@18.6.0'\n - '@commitlint/config-conventional@18.6.0'\n - '@grnsft/if-core@0.0.25'\n - '@jest/globals@29.7.0'\n - '@types/jest@29.5.8'\n - '@types/js-yaml@4.0.9'\n - '@types/luxon@3.4.2'\n - '@types/node@20.9.0'\n - axios-mock-adapter@1.22.0\n - axios@1.7.2\n - cross-env@7.0.3\n - csv-parse@5.5.6\n - csv-stringify@6.4.6\n - fixpack@4.0.0\n - gts@5.2.0\n - husky@8.0.3\n - jest@29.7.0\n - js-yaml@4.1.0\n - lint-staged@15.2.2\n - luxon@3.4.4\n - release-it@16.3.0\n - rimraf@5.0.5\n - ts-command-line-args@2.5.1\n - ts-jest@29.1.1\n - typescript-cubic-spline@1.0.1\n - typescript@5.2.2\n - winston@3.11.0\n - zod@3.23.8\n status: success\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - interpolate\n - cpu-factor-to-wattage\n - wattage-times-duration\n - wattage-to-energy-kwh\n - calculate-vcpu-ratio\n - correct-cpu-energy-for-vcpu-ratio\n - energy-to-carbon\n defaults:\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n grid-carbon-intensity: 750\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n outputs:\n - timestamp: 2023-08-06T00:00\n duration: 360\n cpu/utilization: 1\n carbon: 30\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n grid-carbon-intensity: 750\n cpu-factor: 0.13999999999999999\n cpu-wattage: 13.999999999999998\n cpu-wattage-times-duration: 5039.999999999999\n cpu-energy-raw: 0.0013999999999999998\n vcpu-ratio: 4\n cpu-energy-kwh: 0.00034999999999999994\n - timestamp: 2023-09-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 10\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n grid-carbon-intensity: 750\n cpu-factor: 0.32\n cpu-wattage: 32\n cpu-wattage-times-duration: 11520\n cpu-energy-raw: 0.0032\n vcpu-ratio: 4\n cpu-energy-kwh: 0.0008\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 50\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n grid-carbon-intensity: 750\n cpu-factor: 0.75\n cpu-wattage: 75\n cpu-wattage-times-duration: 27000\n cpu-energy-raw: 0.0075\n vcpu-ratio: 4\n cpu-energy-kwh: 0.001875\n - timestamp: 2023-10-06T00:00\n duration: 360\n carbon: 30\n cpu/utilization: 100\n thermal-design-power: 100\n vcpus-total: 8\n vcpus-allocated: 2\n grid-carbon-intensity: 750\n cpu-factor: 1.02\n cpu-wattage: 102\n cpu-wattage-times-duration: 36720\n cpu-energy-raw: 0.0102\n vcpu-ratio: 4\n cpu-energy-kwh: 0.00255\n")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/d57fc049.5a739529.js b/assets/js/d57fc049.c32b0ca1.js similarity index 87% rename from assets/js/d57fc049.5a739529.js rename to assets/js/d57fc049.c32b0ca1.js index 85175b12..c356cd00 100644 --- a/assets/js/d57fc049.5a739529.js +++ b/assets/js/d57fc049.c32b0ca1.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[940],{4137:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),p=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=r,f=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?i.createElement(f,o(o({ref:t},c),{},{components:n})):i.createElement(f,o({ref:t},c))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var i=n(7462),r=(n(7294),n(4137));const a={sidebar_position:8},o="Pipelines",s={unversionedId:"major-concepts/pipelines",id:"major-concepts/pipelines",title:"Pipelines",description:"Pipelines are chains of plugins that operate in sequence over the input data in your manifest file.",source:"@site/docs/major-concepts/pipelines.md",sourceDirName:"major-concepts",slug:"/major-concepts/pipelines",permalink:"/major-concepts/pipelines",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/pipelines.md",tags:[],version:"current",sidebarPosition:8,frontMatter:{sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"Aggregation",permalink:"/major-concepts/aggregation"},next:{title:"Exhaust scripts",permalink:"/major-concepts/exhaust-script"}},l={},p=[{value:"Prebuilt pipelines",id:"prebuilt-pipelines",level:2}],c={toc:p};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"pipelines"},"Pipelines"),(0,r.kt)("p",null,"Pipelines are chains of plugins that operate in sequence over the input data in your manifest file."),(0,r.kt)("p",null,"Your input data is fed through your pipeline of plugins, with each plugin adding a key-value pair to the ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," array or updating the value of an existing entry."),(0,r.kt)("p",null,"Each plugin does some specific operation. The idea is that individual plugins are simple - they do one specific thing only - but they act like Impact Legos, building up into complex logic operating on your manifest file."),(0,r.kt)("h2",{id:"prebuilt-pipelines"},"Prebuilt pipelines"),(0,r.kt)("p",null,'We have designed our "standard library" of builtins to cover many generic operations such as file i/o, arithetic, queries etc so that in any cases you can build up complex pipelines without having to install any third party dependencies. One of the downsides of this is that logic that could be abstracted away into plugin code has to be implemented inside your manifest. We think this is great for transparency, auditability and reproducability, but it does come with a moderate learning curve. For this reason, we have provided prebuilt pipelines for several of our common operations, such as implementing the Teads curve for estimating cpu energy consumption from CPU utilization, looking up metadata for given cloud instance types anc calculating software carbon intensity (SCI) scores.'),(0,r.kt)("p",null,"We recommend looking at the manifests in the ",(0,r.kt)("inlineCode",{parentName:"p"},"manifests/examples")," folder that comes bundled with IF."),(0,r.kt)("p",null,"We also have a set of pipeline walkthroughs on this website, including:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"../pipelines/teads.md"},"Teads curve"),": calculate CPU energy from CPU utilization and the thermal design power of your processor"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/pipelines/instance-metadata"},"Cloud-instance metadata"),": lookup information about your cloud instance froma CSV file"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/pipelines/sci"},"SCI"),": calculate a software carbon intensiy score")),(0,r.kt)("p",null,"These pipelines can be modified or chained together with other pipelines to make larger pipelines. Just as each plugin is a building block, pipelines themselves can be building blocks too."))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[940],{4137:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),p=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},c=function(e){var t=p(e.components);return i.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},d=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=r,f=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?i.createElement(f,o(o({ref:t},c),{},{components:n})):i.createElement(f,o({ref:t},c))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var i=n(7462),r=(n(7294),n(4137));const a={sidebar_position:8},o="Pipelines",s={unversionedId:"major-concepts/pipelines",id:"major-concepts/pipelines",title:"Pipelines",description:"Pipelines are chains of plugins that operate in sequence over the input data in your manifest file.",source:"@site/docs/major-concepts/pipelines.md",sourceDirName:"major-concepts",slug:"/major-concepts/pipelines",permalink:"/major-concepts/pipelines",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/pipelines.md",tags:[],version:"current",sidebarPosition:8,frontMatter:{sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"Aggregation",permalink:"/major-concepts/aggregation"},next:{title:"Exhaust scripts",permalink:"/major-concepts/exhaust-script"}},l={},p=[{value:"Prebuilt pipelines",id:"prebuilt-pipelines",level:2}],c={toc:p};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,i.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"pipelines"},"Pipelines"),(0,r.kt)("p",null,"Pipelines are chains of plugins that operate in sequence over the input data in your manifest file."),(0,r.kt)("p",null,"Your input data is fed through your pipeline of plugins, with each plugin adding a key-value pair to the ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," array or updating the value of an existing entry."),(0,r.kt)("p",null,"Each plugin does some specific operation. The idea is that individual plugins are simple - they do one specific thing only - but they act like Impact Legos, building up into complex logic operating on your manifest file."),(0,r.kt)("h2",{id:"prebuilt-pipelines"},"Prebuilt pipelines"),(0,r.kt)("p",null,'We have designed our "standard library" of builtins to cover many generic operations such as file i/o, arithetic, queries etc so that in any cases you can build up complex pipelines without having to install any third party dependencies. One of the downsides of this is that logic that could be abstracted away into plugin code has to be implemented inside your manifest. We think this is great for transparency, auditability and reproducability, but it does come with a moderate learning curve. For this reason, we have provided prebuilt pipelines for several of our common operations, such as implementing the Teads curve for estimating cpu energy consumption from CPU utilization, looking up metadata for given cloud instance types anc calculating software carbon intensity (SCI) scores.'),(0,r.kt)("p",null,"We recommend looking at the manifests in the ",(0,r.kt)("inlineCode",{parentName:"p"},"manifests/examples")," folder that comes bundled with IF."),(0,r.kt)("p",null,"We also have a set of pipeline walkthroughs on this website, including:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/pipelines/teads"},"Teads curve"),": calculate CPU energy from CPU utilization and the thermal design power of your processor"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/pipelines/instance-metadata"},"Cloud-instance metadata"),": lookup information about your cloud instance froma CSV file"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("a",{parentName:"li",href:"/pipelines/sci"},"SCI"),": calculate a software carbon intensiy score")),(0,r.kt)("p",null,"These pipelines can be modified or chained together with other pipelines to make larger pipelines. Just as each plugin is a building block, pipelines themselves can be building blocks too."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f744e480.72f8ee47.js b/assets/js/f744e480.0771c3e7.js similarity index 60% rename from assets/js/f744e480.72f8ee47.js rename to assets/js/f744e480.0771c3e7.js index 5d1630f4..2aba3ba5 100644 --- a/assets/js/f744e480.72f8ee47.js +++ b/assets/js/f744e480.0771c3e7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[352],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=s(n),f=a,d=c["".concat(p,".").concat(f)]||c[f]||m[f]||o;return n?r.createElement(d,i(i({ref:t},u),{},{components:n})):r.createElement(d,i({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=c;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":3},i="Impact Engine (CLI)",l={unversionedId:"major-concepts/if",id:"major-concepts/if",title:"Impact Engine (CLI)",description:"Introduction",source:"@site/docs/major-concepts/if.md",sourceDirName:"major-concepts",slug:"/major-concepts/if",permalink:"/major-concepts/if",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/if.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"Exhaust scripts",permalink:"/major-concepts/exhaust-script"},next:{title:"Manifest File",permalink:"/major-concepts/manifest-file"}},p={},s=[{value:"Introduction",id:"introduction",level:2},{value:"Phased execution",id:"phased-execution",level:2}],u={toc:s};function m(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"impact-engine-cli"},"Impact Engine (CLI)"),(0,a.kt)("h2",{id:"introduction"},"Introduction"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," is a command line tool that computes ",(0,a.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"Manifest files"),".\nIt is the portal allowing users to interact with the Impact Framework."),(0,a.kt)("p",null,"The available options and their shortcuts are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),": path to an input manifest file"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--output")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-o")," (optional): path to the output file where the results as saved"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--no-output")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-n")," (optional): suppress the output to console"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--help")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-h"),": prints out help instruction"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--debug"),": enables IF execution logs")),(0,a.kt)("p",null,"The only required command is ",(0,a.kt)("inlineCode",{parentName:"p"},"--manifest"),". Without a valid path to a manifest file, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," has nothing to execute."),(0,a.kt)("p",null,"To use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", you must first ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"write a manifest file"),". Then, you can simply pass the path to the manifest file to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," on the command line."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest /my-manifest.yml\n## or using aliases\nif-run -m /my-manifest.yml\n")),(0,a.kt)("p",null,"You can also pass a path where you would like to save the output file to. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest ./my-manifest.yml --output ./my-results.yml\n## or using aliases\nif-run -m ./my-manifest.yml -o ./my-results.yml\n")),(0,a.kt)("p",null,"If you omit the ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," command, your results will only be displayed in the console."),(0,a.kt)("p",null,"For more information on the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," commands see the ",(0,a.kt)("a",{parentName:"p",href:"/reference/cli"},"CLI reference documentation"),"."),(0,a.kt)("h2",{id:"phased-execution"},"Phased execution"),(0,a.kt)("p",null,"To enable greener and more flexible use of IF, we separate the manifest execution into distinct phases: ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute"),". This is invisible to you when you run ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," but behind the scenes all three of these phases are being run. However, you can instruct IF to run these phases individually, to avoid recomputing parts of the manifest unnecessarily. To do this, you simply pass ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"--regroup"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"--compute")," flags to IF in the combination you need. For example, to run ",(0,a.kt)("em",{parentName:"p"},"only")," the observe phase (to generate input data):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --observe\n")),(0,a.kt)("p",null,"to run the compute phase on its own:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --compute\n")),(0,a.kt)("p",null,"To run the observe and compute phases without regrouping:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --observe --compute\n")))}m.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[352],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),s=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(p.Provider,{value:t},e.children)},m={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},c=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,p=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),c=s(n),f=a,d=c["".concat(p,".").concat(f)]||c[f]||m[f]||o;return n?r.createElement(d,i(i({ref:t},u),{},{components:n})):r.createElement(d,i({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=c;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var r=n(7462),a=(n(7294),n(4137));const o={"sidebar-position":3},i="Impact Engine (CLI)",l={unversionedId:"major-concepts/if",id:"major-concepts/if",title:"Impact Engine (CLI)",description:"Introduction",source:"@site/docs/major-concepts/if.md",sourceDirName:"major-concepts",slug:"/major-concepts/if",permalink:"/major-concepts/if",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/if.md",tags:[],version:"current",frontMatter:{"sidebar-position":3},sidebar:"tutorialSidebar",previous:{title:"Exhaust scripts",permalink:"/major-concepts/exhaust-script"},next:{title:"Manifest File",permalink:"/major-concepts/manifest-file"}},p={},s=[{value:"Introduction",id:"introduction",level:2},{value:"Phased execution",id:"phased-execution",level:2}],u={toc:s};function m(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"impact-engine-cli"},"Impact Engine (CLI)"),(0,a.kt)("h2",{id:"introduction"},"Introduction"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," is a command line tool that computes ",(0,a.kt)("a",{parentName:"p",href:"/major-concepts/manifest-file"},"Manifest files"),".\nIt is the portal allowing users to interact with the Impact Framework."),(0,a.kt)("p",null,"The available options and their shortcuts are:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--manifest")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-m"),": path to an input manifest file"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--output")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-o")," (optional): path to the output file where the results as saved"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--no-output")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-n")," (optional): suppress the output to console"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--help")," or ",(0,a.kt)("inlineCode",{parentName:"li"},"-h"),": prints out help instruction"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--debug"),": enables IF execution logs"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"--append"),": allows you to rerun an already-computed manifest and append new values to the existing data.")),(0,a.kt)("p",null,"The only required command is ",(0,a.kt)("inlineCode",{parentName:"p"},"--manifest"),". Without a valid path to a manifest file, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," has nothing to execute."),(0,a.kt)("p",null,"To use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", you must first ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-write-manifests"},"write a manifest file"),". Then, you can simply pass the path to the manifest file to ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," on the command line."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest /my-manifest.yml\n## or using aliases\nif-run -m /my-manifest.yml\n")),(0,a.kt)("p",null,"You can also pass a path where you would like to save the output file to. For example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run --manifest ./my-manifest.yml --output ./my-results.yml\n## or using aliases\nif-run -m ./my-manifest.yml -o ./my-results.yml\n")),(0,a.kt)("p",null,"If you omit the ",(0,a.kt)("inlineCode",{parentName:"p"},"--output")," command, your results will only be displayed in the console."),(0,a.kt)("p",null,"For more information on the ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," commands see the ",(0,a.kt)("a",{parentName:"p",href:"/reference/cli"},"CLI reference documentation"),"."),(0,a.kt)("h2",{id:"phased-execution"},"Phased execution"),(0,a.kt)("p",null,"To enable greener and more flexible use of IF, we separate the manifest execution into distinct phases: ",(0,a.kt)("inlineCode",{parentName:"p"},"observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"regroup")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"compute"),". This is invisible to you when you run ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," but behind the scenes all three of these phases are being run. However, you can instruct IF to run these phases individually, to avoid recomputing parts of the manifest unnecessarily. To do this, you simply pass ",(0,a.kt)("inlineCode",{parentName:"p"},"--observe"),", ",(0,a.kt)("inlineCode",{parentName:"p"},"--regroup"),", and ",(0,a.kt)("inlineCode",{parentName:"p"},"--compute")," flags to IF in the combination you need. For example, to run ",(0,a.kt)("em",{parentName:"p"},"only")," the observe phase (to generate input data):"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --observe\n")),(0,a.kt)("p",null,"to run the compute phase on its own:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --compute\n")),(0,a.kt)("p",null,"To run the observe and compute phases without regrouping:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"if-run -m --observe --compute\n")))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fbcc0412.9b8e25c1.js b/assets/js/fbcc0412.90d01f02.js similarity index 72% rename from assets/js/fbcc0412.9b8e25c1.js rename to assets/js/fbcc0412.90d01f02.js index 392841bf..4f2342b8 100644 --- a/assets/js/fbcc0412.9b8e25c1.js +++ b/assets/js/fbcc0412.90d01f02.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[642],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),l=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),m=l(n),f=a,d=m["".concat(s,".").concat(f)]||m[f]||c[f]||i;return n?r.createElement(d,o(o({ref:t},u),{},{components:n})):r.createElement(d,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var p={};for(var s in t)hasOwnProperty.call(t,s)&&(p[s]=t[s]);p.originalType=e,p.mdxType="string"==typeof e?e:a,o[1]=p;for(var l=2;l{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>p,toc:()=>l});var r=n(7462),a=(n(7294),n(4137));const i={sidebar_position:9},o="Exhaust scripts",p={unversionedId:"major-concepts/exhaust-script",id:"major-concepts/exhaust-script",title:"Exhaust scripts",description:"Exhaust scripts are scripts that can run independently of IF itself that take an executed manifest file (one with outputs) as an input, parse the yaml data and reformat it into some other representation. We provide if-csv bundled with IF, but if you want other data formats, you'll have to create an exhaust script yourself.",source:"@site/docs/major-concepts/exhaust-script.md",sourceDirName:"major-concepts",slug:"/major-concepts/exhaust-script",permalink:"/major-concepts/exhaust-script",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/exhaust-script.md",tags:[],version:"current",sidebarPosition:9,frontMatter:{sidebar_position:9},sidebar:"tutorialSidebar",previous:{title:"Pipelines",permalink:"/major-concepts/pipelines"},next:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"}},s={},l=[{value:"if-run",id:"if-run",level:2},{value:"How to Use if-run",id:"how-to-use-if-run",level:3},{value:"if-csv",id:"if-csv",level:2}],u={toc:l};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"exhaust-scripts"},"Exhaust scripts"),(0,a.kt)("p",null,"Exhaust scripts are scripts that can run independently of IF itself that take an executed manifest file (one with ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs"),") as an input, parse the yaml data and reformat it into some other representation. We provide ",(0,a.kt)("inlineCode",{parentName:"p"},"if-csv")," bundled with IF, but if you want other data formats, you'll have to create an exhaust script yourself."),(0,a.kt)("h2",{id:"if-run"},"if-run"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," isn't really an exhaust script, because it also grabs input data, regroups data, computes the pipeline and aggregates. However, we're mentioning it here because it does have some built-in exhaust functionality. Specifically, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," outputs yaml data. ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," can ",(0,a.kt)("em",{parentName:"p"},"only")," output yaml data. This yaml data can be dumped to the console or saved to a yaml file."),(0,a.kt)("h3",{id:"how-to-use-if-run"},"How to Use if-run"),(0,a.kt)("p",null,"To use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", you need to provide a manifest file in ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"yml")," foramt. The output will be in ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," format if you specify the output file path."),(0,a.kt)("p",null,"Here's a simple manifest file example. This manifest sums two components, ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"network/energy")," and assigns the result to ",(0,a.kt)("inlineCode",{parentName:"p"},"energy")," in the outputs array."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n global-config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n config:\n sum:\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n")),(0,a.kt)("p",null,"To execute this manifest with ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", use the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m sum.yaml -o output-sum\n")),(0,a.kt)("p",null,"You will get the executed manifest in the ",(0,a.kt)("inlineCode",{parentName:"p"},"output-sum.yaml")," file."),(0,a.kt)("h2",{id:"if-csv"},"if-csv"),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-export-csv-file-with-if-csv"},(0,a.kt)("inlineCode",{parentName:"a"},"if-csv"))," script allows users to pass in ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"yml")," files created using ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," and save the output in ",(0,a.kt)("inlineCode",{parentName:"p"},"csv")," format. Yopu have to define the parameters you want to export from the yaml file, e.g. ",(0,a.kt)("inlineCode",{parentName:"p"},"energy")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"carbon"),"."),(0,a.kt)("p",null,"For the above example, you can get the following result:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"Path,2023-08-06T00:00\ntree.children.child.energy,0.002\n")),(0,a.kt)("p",null,"by running:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m sum.yaml -p energy -o output-sum\n")),(0,a.kt)("p",null,"This command specifies the manifest file (",(0,a.kt)("inlineCode",{parentName:"p"},"sum.yaml"),"), the parameter to export (",(0,a.kt)("inlineCode",{parentName:"p"},"energy"),"), and the output file path (",(0,a.kt)("inlineCode",{parentName:"p"},"output-sum"),")."))}c.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[642],{4137:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),l=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(s.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),m=l(n),f=a,d=m["".concat(s,".").concat(f)]||m[f]||c[f]||i;return n?r.createElement(d,o(o({ref:t},u),{},{components:n})):r.createElement(d,o({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,o=new Array(i);o[0]=m;var p={};for(var s in t)hasOwnProperty.call(t,s)&&(p[s]=t[s]);p.originalType=e,p.mdxType="string"==typeof e?e:a,o[1]=p;for(var l=2;l{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>c,frontMatter:()=>i,metadata:()=>p,toc:()=>l});var r=n(7462),a=(n(7294),n(4137));const i={sidebar_position:9},o="Exhaust scripts",p={unversionedId:"major-concepts/exhaust-script",id:"major-concepts/exhaust-script",title:"Exhaust scripts",description:"Exhaust scripts are scripts that can run independently of IF itself that take an executed manifest file (one with outputs) as an input, parse the yaml data and reformat it into some other representation. We provide if-csv bundled with IF, but if you want other data formats, you'll have to create an exhaust script yourself.",source:"@site/docs/major-concepts/exhaust-script.md",sourceDirName:"major-concepts",slug:"/major-concepts/exhaust-script",permalink:"/major-concepts/exhaust-script",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/exhaust-script.md",tags:[],version:"current",sidebarPosition:9,frontMatter:{sidebar_position:9},sidebar:"tutorialSidebar",previous:{title:"Pipelines",permalink:"/major-concepts/pipelines"},next:{title:"Impact Engine (CLI)",permalink:"/major-concepts/if"}},s={},l=[{value:"if-run",id:"if-run",level:2},{value:"How to Use if-run",id:"how-to-use-if-run",level:3},{value:"if-csv",id:"if-csv",level:2}],u={toc:l};function c(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"exhaust-scripts"},"Exhaust scripts"),(0,a.kt)("p",null,"Exhaust scripts are scripts that can run independently of IF itself that take an executed manifest file (one with ",(0,a.kt)("inlineCode",{parentName:"p"},"outputs"),") as an input, parse the yaml data and reformat it into some other representation. We provide ",(0,a.kt)("inlineCode",{parentName:"p"},"if-csv")," bundled with IF, but if you want other data formats, you'll have to create an exhaust script yourself."),(0,a.kt)("h2",{id:"if-run"},"if-run"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," isn't really an exhaust script, because it also grabs input data, regroups data, computes the pipeline and aggregates. However, we're mentioning it here because it does have some built-in exhaust functionality. Specifically, ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," outputs yaml data. ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," can ",(0,a.kt)("em",{parentName:"p"},"only")," output yaml data. This yaml data can be dumped to the console or saved to a yaml file."),(0,a.kt)("h3",{id:"how-to-use-if-run"},"How to Use if-run"),(0,a.kt)("p",null,"To use ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", you need to provide a manifest file in ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"yml")," foramt. The output will be in ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," format if you specify the output file path."),(0,a.kt)("p",null,"Here's a simple manifest file example. This manifest sums two components, ",(0,a.kt)("inlineCode",{parentName:"p"},"cpu/energy")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"network/energy")," and assigns the result to ",(0,a.kt)("inlineCode",{parentName:"p"},"energy")," in the outputs array."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-yaml"},"name: sum\ndescription: successful path\ntags:\ninitialize:\n plugins:\n sum:\n method: Sum\n path: 'builtin'\n config:\n input-parameters: ['cpu/energy', 'network/energy']\n output-parameter: 'energy'\ntree:\n children:\n child:\n pipeline:\n observe:\n regroup:\n compute:\n - sum\n inputs:\n - timestamp: 2023-08-06T00:00\n duration: 3600\n cpu/energy: 0.001\n network/energy: 0.001\n")),(0,a.kt)("p",null,"To execute this manifest with ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run"),", use the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-run -m sum.yaml -o output-sum\n")),(0,a.kt)("p",null,"You will get the executed manifest in the ",(0,a.kt)("inlineCode",{parentName:"p"},"output-sum.yaml")," file."),(0,a.kt)("h2",{id:"if-csv"},"if-csv"),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"/users/how-to-export-csv-file-with-if-csv"},(0,a.kt)("inlineCode",{parentName:"a"},"if-csv"))," script allows users to pass in ",(0,a.kt)("inlineCode",{parentName:"p"},"yaml")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"yml")," files created using ",(0,a.kt)("inlineCode",{parentName:"p"},"if-run")," and save the output in ",(0,a.kt)("inlineCode",{parentName:"p"},"csv")," format. Yopu have to define the parameters you want to export from the yaml file, e.g. ",(0,a.kt)("inlineCode",{parentName:"p"},"energy")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"carbon"),"."),(0,a.kt)("p",null,"For the above example, you can get the following result:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"Path,2023-08-06T00:00\ntree.children.child.energy,0.002\n")),(0,a.kt)("p",null,"by running:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-sh"},"if-csv -m sum.yaml -p energy -o output-sum\n")),(0,a.kt)("p",null,"This command specifies the manifest file (",(0,a.kt)("inlineCode",{parentName:"p"},"sum.yaml"),"), the parameter to export (",(0,a.kt)("inlineCode",{parentName:"p"},"energy"),"), and the output file path (",(0,a.kt)("inlineCode",{parentName:"p"},"output-sum"),")."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fd565be6.d17666dc.js b/assets/js/fd565be6.55aba2ff.js similarity index 51% rename from assets/js/fd565be6.d17666dc.js rename to assets/js/fd565be6.55aba2ff.js index 78652029..938a16d0 100644 --- a/assets/js/fd565be6.d17666dc.js +++ b/assets/js/fd565be6.55aba2ff.js @@ -1 +1 @@ -"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[168],{4137:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},m=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),u=c(n),h=r,d=u["".concat(l,".").concat(h)]||u[h]||p[h]||a;return n?i.createElement(d,o(o({ref:t},m),{},{components:n})):i.createElement(d,o({ref:t},m))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(4137));const a={"sidebar-position":5},o="Time",s={unversionedId:"major-concepts/time",id:"major-concepts/time",title:"Time",description:"Every observation in an array of inputs represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:",source:"@site/docs/major-concepts/time.md",sourceDirName:"major-concepts",slug:"/major-concepts/time",permalink:"/major-concepts/time",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/time.md",tags:[],version:"current",frontMatter:{"sidebar-position":5},sidebar:"tutorialSidebar",previous:{title:"Regroup",permalink:"/major-concepts/regroup"},next:{title:"Users",permalink:"/users/"}},l={},c=[{value:"Toggling off time sync",id:"toggling-off-time-sync",level:2}],m={toc:c};function p(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,i.Z)({},m,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"time"},"Time"),(0,r.kt)("p",null,"Every ",(0,r.kt)("inlineCode",{parentName:"p"},"observation")," in an array of ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yml"},"inputs:\n - timestamp: 2024-01-15T00:00:00.000Z\n duration: 10\n cpu-util: 20\n")),(0,r.kt)("p",null,"The total time covered by an inputs array is determined by the timestamp of the first observation in the array and the timestamp and duration in the last observation in the array. Since every observation needs both a timestamp and a duration, an inputs array is always a time series."),(0,r.kt)("h1",{id:"synchronizing-time-series"},"Synchronizing time series'"),(0,r.kt)("p",null,"The time series for each component is defined by its inputs array. However, a manifest file can contain many separate components, each with their own time series. There is no guarantee that an individual time series is continuous, or that all the components in a manifest file have the same start time, end time and resolution. This makes it difficult to aggregate, visualize or do like-for-like comparisons between components."),(0,r.kt)("p",null,"To solve this problem, we provide a built-in ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," feature that synchronizes the time series' across all the components in a tree. The time-sync feature takes a global start time, end time and interval, then forces every individual time series to conform to this global configuration."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"This works by first upsampling each time series to a common base resolution (typically 1s)."),(0,r.kt)("li",{parentName:"ul"},'Any gaps in the time series are filled in with "zero objects", which have an identical structure to the real observations but with usage metrics set to zero (we assume that when there is no data, there is no usage).'),(0,r.kt)("li",{parentName:"ul"},"Next, we check to see whether the first timestamp in each time series is before, after or identical to the global start time."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},'If a component\'s time series starts after the global start time, we pad the start of the time series with "zero objects" so that the start times are identical.\n'))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},"If the component's time series starts *before* the global start time, we trim the time series down, discarding observations from before the global start time. The same trimming logic is applied to the end times.\n"))),(0,r.kt)("li",{parentName:"ul"},"After synchronizing the start and end times and padding any discontinuities, we have a set of continuous time series' of identical length."),(0,r.kt)("li",{parentName:"ul"},"Next, we batch observations together into time bins whose size is define by the global ",(0,r.kt)("inlineCode",{parentName:"li"},"interval")," value. This means that the resolution of the final time series' are identical and equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"interval"),". ")),(0,r.kt)("p",null,"This process yields synchronized time series for all components across a tree, enabling easy visualization and intercomparison. This synchronization is also a prerequisite for our aggregation function."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(5966).Z,width:"1058",height:"595"})),(0,r.kt)("h2",{id:"toggling-off-time-sync"},"Toggling off time sync"),(0,r.kt)("p",null,"Some applications will not want to pad with zero values, and may be strict about continuous time series' being provided in the raw manifest file. In these cases, simply toggle the padding off in the manifest file."))}p.isMDXComponent=!0},5966:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/time-sync-schematic-f7b18c86827dc4b4f0551acc799ff6ab.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[168],{4137:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var i=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function o(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=i.createContext({}),c=function(e){var t=i.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},m=function(e){var t=c(e.components);return i.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return i.createElement(i.Fragment,{},t)}},u=i.forwardRef((function(e,t){var n=e.components,r=e.mdxType,a=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),u=c(n),h=r,d=u["".concat(l,".").concat(h)]||u[h]||p[h]||a;return n?i.createElement(d,o(o({ref:t},m),{},{components:n})):i.createElement(d,o({ref:t},m))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var a=n.length,o=new Array(a);o[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,o[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>c});var i=n(7462),r=(n(7294),n(4137));const a={"sidebar-position":5},o="Time",s={unversionedId:"major-concepts/time",id:"major-concepts/time",title:"Time",description:"Every observation in an array of inputs represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:",source:"@site/docs/major-concepts/time.md",sourceDirName:"major-concepts",slug:"/major-concepts/time",permalink:"/major-concepts/time",draft:!1,editUrl:"https://github.com/Green-Software-Foundation/if-docs/edit/master/docs/major-concepts/time.md",tags:[],version:"current",frontMatter:{"sidebar-position":5},sidebar:"tutorialSidebar",previous:{title:"Regroup",permalink:"/major-concepts/regroup"},next:{title:"Users",permalink:"/users/"}},l={},c=[{value:"Toggling off time sync",id:"toggling-off-time-sync",level:2}],m={toc:c};function p(e){let{components:t,...a}=e;return(0,r.kt)("wrapper",(0,i.Z)({},m,a,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"time"},"Time"),(0,r.kt)("p",null,"Every ",(0,r.kt)("inlineCode",{parentName:"p"},"observation")," in an array of ",(0,r.kt)("inlineCode",{parentName:"p"},"inputs")," represents a snapshot with a known start time and a known duration. For example, the following observation shows that the CPU utilization for a resource was 20% for the 10 second period starting at 1500 on the 22nd January 2024:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yml"},"inputs:\n - timestamp: 2024-01-15T00:00:00.000Z\n duration: 10\n cpu-util: 20\n")),(0,r.kt)("p",null,"The total time covered by an inputs array is determined by the timestamp of the first observation in the array and the timestamp and duration in the last observation in the array. Since every observation needs both a timestamp and a duration, an inputs array is always a time series."),(0,r.kt)("h1",{id:"synchronizing-time-series"},"Synchronizing time series'"),(0,r.kt)("p",null,"The time series for each component is defined by its inputs array. However, a manifest file can contain many separate components, each with their own time series. There is no guarantee that an individual time series is continuous, or that all the components in a manifest file have the same start time, end time and resolution. This makes it difficult to aggregate, visualize or do like-for-like comparisons between components."),(0,r.kt)("p",null,"To solve this problem, we provide a built-in ",(0,r.kt)("inlineCode",{parentName:"p"},"time-sync")," feature that synchronizes the time series' across all the components in a tree. The time-sync feature takes a global start time, end time and interval, then forces every individual time series to conform to this configuration."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"This works by first upsampling each time series to a common base resolution (typically 1s)."),(0,r.kt)("li",{parentName:"ul"},'Any gaps in the time series are filled in with "zero objects", which have an identical structure to the real observations but with usage metrics set to zero (we assume that when there is no data, there is no usage).'),(0,r.kt)("li",{parentName:"ul"},"Next, we check to see whether the first timestamp in each time series is before, after or identical to the global start time."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},'If a component\'s time series starts after the global start time, we pad the start of the time series with "zero objects" so that the start times are identical.\n'))),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre"},"If the component's time series starts *before* the global start time, we trim the time series down, discarding observations from before the global start time. The same trimming logic is applied to the end times.\n"))),(0,r.kt)("li",{parentName:"ul"},"After synchronizing the start and end times and padding any discontinuities, we have a set of continuous time series' of identical length."),(0,r.kt)("li",{parentName:"ul"},"Next, we batch observations together into time bins whose size is define by the global ",(0,r.kt)("inlineCode",{parentName:"li"},"interval")," value. This means that the resolution of the final time series' are identical and equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"interval"),".")),(0,r.kt)("p",null,"This process yields synchronized time series for all components across a tree, enabling easy visualization and intercomparison. This synchronization is also a prerequisite for our aggregation function."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(5966).Z,width:"1058",height:"595"})),(0,r.kt)("h2",{id:"toggling-off-time-sync"},"Toggling off time sync"),(0,r.kt)("p",null,"Some applications will not want to pad with zero values, and may be strict about continuous time series' being provided in the raw manifest file. In these cases, simply toggle the padding off in the manifest file."))}p.isMDXComponent=!0},5966:(e,t,n)=>{n.d(t,{Z:()=>i});const i=n.p+"assets/images/time-sync-schematic-f7b18c86827dc4b4f0551acc799ff6ab.png"}}]); \ No newline at end of file diff --git a/assets/js/main.24094120.js b/assets/js/main.24094120.js new file mode 100644 index 00000000..8a0dd75e --- /dev/null +++ b/assets/js/main.24094120.js @@ -0,0 +1,2 @@ +/*! For license information please see main.24094120.js.LICENSE.txt */ +(self.webpackChunkgreen_software_training=self.webpackChunkgreen_software_training||[]).push([[179],{997:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7294),a=n(7462),o=n(8356),i=n.n(o),l=n(6887);const s={"0569acb0":[()=>n.e(796).then(n.bind(n,3396)),"@site/docs/pipelines/instance-metadata.md",3396],"069226b7":[()=>n.e(760).then(n.t.bind(n,5745,19)),"/home/runner/work/if-docs/if-docs/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",5745],"0734eb39":[()=>n.e(939).then(n.bind(n,6293)),"@site/docs/users/how-to-compare-files-with-if-diff.md",6293],"0a19367a":[()=>n.e(109).then(n.bind(n,1652)),"@site/docs/users/quick-start.md",1652],"0e384e19":[()=>n.e(671).then(n.bind(n,1039)),"@site/docs/intro.md",1039],17896441:[()=>Promise.all([n.e(532),n.e(982),n.e(918)]).then(n.bind(n,5097)),"@theme/DocItem",5097],"1a3c9b31":[()=>n.e(751).then(n.bind(n,2559)),"@site/docs/reference/index.md",2559],"1be78505":[()=>Promise.all([n.e(532),n.e(514)]).then(n.bind(n,1299)),"@theme/DocPage",1299],"1df93b7f":[()=>Promise.all([n.e(532),n.e(237)]).then(n.bind(n,2833)),"@site/src/pages/index.tsx",2833],"1f391b9e":[()=>Promise.all([n.e(532),n.e(982),n.e(85)]).then(n.bind(n,6252)),"@theme/MDXPage",6252],"244c9605":[()=>n.e(426).then(n.bind(n,6869)),"@site/docs/developers/how-to-refine-plugins.md",6869],"28f080da":[()=>n.e(806).then(n.bind(n,8227)),"@site/docs/developers/how-to-build-plugins.md",8227],"29966d47":[()=>n.e(117).then(n.bind(n,1841)),"@site/docs/major-concepts/index.md",1841],"37c09b06":[()=>n.e(485).then(n.bind(n,3427)),"@site/docs/developers/how-to-create-exhaust-script.md",3427],"3dce4fd2":[()=>n.e(278).then(n.bind(n,7677)),"@site/docs/users/how-to-export-csv-file-with-if-csv.md",7677],"449bc0d9":[()=>n.e(99).then(n.bind(n,2350)),"@site/docs/users/index.md",2350],"486bc80e":[()=>n.e(162).then(n.bind(n,2843)),"@site/docs/users/how-to-install-if.md",2843],"4c7f2983":[()=>n.e(186).then(n.bind(n,5608)),"@site/docs/major-concepts/regroup.md",5608],"5c5a410f":[()=>n.e(468).then(n.bind(n,7003)),"@site/docs/major-concepts/manifest-file.md",7003],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,6809)),"@generated/docusaurus.config",6809],"63925da8":[()=>n.e(822).then(n.bind(n,7673)),"@site/docs/developers/index.md",7673],"667a5e8e":[()=>n.e(54).then(n.bind(n,4398)),"@site/docs/pipelines/index.md",4398],"68ffc9f1":[()=>n.e(644).then(n.bind(n,9365)),"@site/docs/users/how-to-write-manifests.md",9365],"75e434b4":[()=>n.e(229).then(n.bind(n,9059)),"@site/docs/reference/plugins.md",9059],"859a76f9":[()=>n.e(527).then(n.t.bind(n,3769,19)),"/home/runner/work/if-docs/if-docs/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",3769],"89d906ef":[()=>n.e(169).then(n.bind(n,9595)),"@site/docs/users/how-to-verify-files-with-if-check.md",9595],"8c7895a0":[()=>n.e(954).then(n.bind(n,8606)),"@site/docs/reference/errors.md",8606],"8e29a039":[()=>n.e(448).then(n.bind(n,5520)),"@site/docs/developers/how-to-submit-plugins.md",5520],"91c76d4c":[()=>n.e(960).then(n.bind(n,8699)),"@site/docs/reference/cli.md",8699],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"93f138be":[()=>n.e(713).then(n.bind(n,6769)),"@site/docs/developers/how-to-write-unit-tests.md",6769],"95ad2285":[()=>n.e(95).then(n.bind(n,8490)),"@site/docs/pipelines/teads.md",8490],"9e4f9ffe":[()=>n.e(684).then(n.bind(n,2698)),"@site/docs/users/how-to-use-the-explain-feature.md",2698],a34125e0:[()=>n.e(316).then(n.bind(n,3684)),"@site/docs/reference/features.md",3684],a4954f21:[()=>n.e(672).then(n.bind(n,4296)),"@site/docs/major-concepts/design-philosophy.md",4296],ab3e713c:[()=>n.e(712).then(n.bind(n,6890)),"@site/docs/pipelines/sci.md",6890],ad895e75:[()=>n.e(288).then(n.bind(n,7701)),"@site/docs/FAQ.md",7701],b5ada7f5:[()=>n.e(491).then(n.bind(n,1802)),"@site/docs/major-concepts/aggregation.md",1802],bdfbfcca:[()=>n.e(533).then(n.bind(n,325)),"@site/docs/major-concepts/plugins.md",325],cca8bc57:[()=>n.e(112).then(n.bind(n,4851)),"@site/docs/users/how-to-import-plugins.md",4851],ce9192f0:[()=>n.e(383).then(n.bind(n,9283)),"@site/docs/pipelines/cpu-to-carbon.md",9283],d57fc049:[()=>n.e(940).then(n.bind(n,4553)),"@site/docs/major-concepts/pipelines.md",4553],e7b22fe0:[()=>n.e(996).then(n.bind(n,8961)),"@site/src/pages/community.md",8961],f744e480:[()=>n.e(352).then(n.bind(n,77)),"@site/docs/major-concepts/if.md",77],fbcc0412:[()=>n.e(642).then(n.bind(n,6059)),"@site/docs/major-concepts/exhaust-script.md",6059],fd565be6:[()=>n.e(168).then(n.bind(n,9887)),"@site/docs/major-concepts/time.md",9887]};function u(e){let{error:t,retry:n,pastDelay:a}=e;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var c=n(5304),d=n(9656);function f(e,t){if("*"===e)return i()({loading:u,loader:()=>n.e(248).then(n.bind(n,4248)),modules:["@theme/NotFound"],webpack:()=>[4248],render(e,t){const n=e.default;return r.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});const o=l[e+"-"+t],f={},p=[],m=[],h=(0,c.Z)(o);return Object.entries(h).forEach((e=>{let[t,n]=e;const r=s[n];r&&(f[t]=r[0],p.push(r[1]),m.push(r[2]))})),i().Map({loading:u,loader:f,modules:p,webpack:()=>m,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,r]=t;const a=r.default;if(!a)throw new Error("The page component at "+e+" doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.");"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{a[e]=r[e]}));let o=i;const l=n.split(".");l.slice(0,-1).forEach((e=>{o=o[e]})),o[l[l.length-1]]=a}));const l=i.__comp;delete i.__comp;const s=i.__context;return delete i.__context,r.createElement(d.z,{value:s},r.createElement(l,(0,a.Z)({},i,n)))}})}const p=[{path:"/community",component:f("/community","dd5"),exact:!0},{path:"/",component:f("/","289"),exact:!0},{path:"/",component:f("/","c71"),routes:[{path:"/developers/",component:f("/developers/","745"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-build-plugins",component:f("/developers/how-to-build-plugins","e49"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-create-exhaust-script",component:f("/developers/how-to-create-exhaust-script","d4d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-refine-plugins",component:f("/developers/how-to-refine-plugins","017"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-submit-plugins",component:f("/developers/how-to-submit-plugins","498"),exact:!0,sidebar:"tutorialSidebar"},{path:"/developers/how-to-write-unit-tests",component:f("/developers/how-to-write-unit-tests","836"),exact:!0,sidebar:"tutorialSidebar"},{path:"/FAQ",component:f("/FAQ","9ff"),exact:!0,sidebar:"tutorialSidebar"},{path:"/intro",component:f("/intro","283"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/",component:f("/major-concepts/","d20"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/aggregation",component:f("/major-concepts/aggregation","aa7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/design-philosophy",component:f("/major-concepts/design-philosophy","99c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/exhaust-script",component:f("/major-concepts/exhaust-script","f04"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/if",component:f("/major-concepts/if","00c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/manifest-file",component:f("/major-concepts/manifest-file","054"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/pipelines",component:f("/major-concepts/pipelines","b7f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/plugins",component:f("/major-concepts/plugins","c56"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/regroup",component:f("/major-concepts/regroup","81c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/major-concepts/time",component:f("/major-concepts/time","279"),exact:!0,sidebar:"tutorialSidebar"},{path:"/pipelines/",component:f("/pipelines/","572"),exact:!0,sidebar:"tutorialSidebar"},{path:"/pipelines/cpu-to-carbon",component:f("/pipelines/cpu-to-carbon","f2f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/pipelines/instance-metadata",component:f("/pipelines/instance-metadata","436"),exact:!0,sidebar:"tutorialSidebar"},{path:"/pipelines/sci",component:f("/pipelines/sci","8b7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/pipelines/teads",component:f("/pipelines/teads","af4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/",component:f("/reference/","183"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/cli",component:f("/reference/cli","131"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/errors",component:f("/reference/errors","d42"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/features",component:f("/reference/features","7d5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reference/plugins",component:f("/reference/plugins","c1f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/",component:f("/users/","669"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-compare-files-with-if-diff",component:f("/users/how-to-compare-files-with-if-diff","767"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-export-csv-file-with-if-csv",component:f("/users/how-to-export-csv-file-with-if-csv","76e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-import-plugins",component:f("/users/how-to-import-plugins","c01"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-install-if",component:f("/users/how-to-install-if","a04"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-use-the-explain-feature",component:f("/users/how-to-use-the-explain-feature","edb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-verify-files-with-if-check",component:f("/users/how-to-verify-files-with-if-check","7f1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/how-to-write-manifests",component:f("/users/how-to-write-manifests","5ed"),exact:!0,sidebar:"tutorialSidebar"},{path:"/users/quick-start",component:f("/users/quick-start","164"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"*",component:f("*")}]},8121:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(7294);const a=r.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{o(!0)}),[]),r.createElement(a.Provider,{value:n},t)}},654:(e,t,n)=>{"use strict";var r=n(7294),a=n(3935),o=n(3727),i=n(405),l=n(6136);const s=[n(4313),n(984),n(2251),n(9957),n(6930)];var u=n(997),c=n(6775),d=n(8790);function f(e){let{children:t}=e;return r.createElement(r.Fragment,null,t)}var p=n(7462),m=n(1514),h=n(9962),g=n(9524),v=n(107),b=n(5463),y=n(626),w=n(8181),k=n(246),E=n(3647);function S(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,h.Z)(),n=(0,y.l)();return r.createElement(m.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:a}]=e;return r.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:a})})),r.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function x(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.Z)(),a=function(){const{siteConfig:{url:e}}=(0,h.Z)(),{pathname:t}=(0,c.TH)();return e+(0,g.Z)(t)}(),o=t?""+n+t:a;return r.createElement(m.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function _(){const{i18n:{currentLocale:e}}=(0,h.Z)(),{metadata:t,image:n}=(0,v.L)();return r.createElement(r.Fragment,null,r.createElement(m.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:w.h})),n&&r.createElement(b.d,{image:n}),r.createElement(x,null),r.createElement(S,null),r.createElement(E.Z,{tag:k.HX,locale:e}),r.createElement(m.Z,null,t.map(((e,t)=>r.createElement("meta",(0,p.Z)({key:t},e))))))}const C=new Map;function T(e){if(C.has(e.pathname))return{...e,pathname:C.get(e.pathname)};if((0,d.f)(u.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return C.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return C.set(e.pathname,t),{...e,pathname:t}}var A=n(8121),L=n(694);function R(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{var r,a;const o=null!=(r=null==(a=t.default)?void 0:a[e])?r:t[e];return null==o?void 0:o(...n)}));return()=>a.forEach((e=>null==e?void 0:e()))}const P=function(e){let{children:t,location:n,previousLocation:a}=e;return(0,r.useLayoutEffect)((()=>{a!==n&&(a&&function(e){const{hash:t}=e;if(t){const e=decodeURIComponent(t.substring(1)),n=document.getElementById(e);null==n||n.scrollIntoView()}else window.scrollTo(0,0)}(n),R("onRouteDidUpdate",{previousLocation:a,location:n}))}),[a,n]),t};function N(e){const t=(0,d.f)(u.Z,e);return Promise.all(t.map((e=>null==e.route.component.preload?void 0:e.route.component.preload())))}class O extends r.Component{constructor(e){super(e),this.previousLocation=void 0,this.routeUpdateCleanupCb=void 0,this.previousLocation=null,this.routeUpdateCleanupCb=l.Z.canUseDOM?R("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=R("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),N(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return r.createElement(P,{previousLocation:this.previousLocation,location:t},r.createElement(c.AW,{location:t,render:()=>e}))}}const D=O,I="docusaurus-base-url-issue-banner-container",M="docusaurus-base-url-issue-banner-suggestion-container",F="__DOCUSAURUS_INSERT_BASEURL_BANNER";function j(e){return"\nwindow['"+F+"'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['"+F+"'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('"+I+"');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = "+JSON.stringify(function(e){return'\n
\n

Your Docusaurus site did not load properly.

\n

A very common reason is a wrong site baseUrl configuration.

\n

Current configured baseUrl = '+e+" "+("/"===e?" (default value)":"")+'

\n

We suggest trying baseUrl =

\n
\n'}(e)).replace(/{window[F]=!1}),[]),r.createElement(r.Fragment,null,!l.Z.canUseDOM&&r.createElement(m.Z,null,r.createElement("script",null,j(e))),r.createElement("div",{id:I}))}function z(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,h.Z)(),{pathname:n}=(0,c.TH)();return t&&n===e?r.createElement(B,null):null}function U(){const{siteConfig:{favicon:e,title:t},i18n:{currentLocale:n,localeConfigs:a}}=(0,h.Z)(),o=(0,g.Z)(e),{htmlLang:i,direction:l}=a[n];return r.createElement(m.Z,null,r.createElement("html",{lang:i,dir:l}),r.createElement("title",null,t),r.createElement("meta",{property:"og:title",content:t}),e&&r.createElement("link",{rel:"icon",href:o}))}var q=n(3256);function G(){const e=(0,d.H)(u.Z),t=(0,c.TH)();return r.createElement(q.Z,null,r.createElement(L.M,null,r.createElement(A.t,null,r.createElement(f,null,r.createElement(U,null),r.createElement(_,null),r.createElement(z,null),r.createElement(D,{location:T(t)},e)))))}var $=n(6887);const H=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{var r,a;if("undefined"==typeof document)return void n();const o=document.createElement("link");o.setAttribute("rel","prefetch"),o.setAttribute("href",e),o.onload=()=>t(),o.onerror=()=>n();const i=null!=(r=document.getElementsByTagName("head")[0])?r:null==(a=document.getElementsByName("script")[0])?void 0:a.parentNode;null==i||i.appendChild(o)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var Z=n(5304);const V=new Set,W=new Set,Y=()=>{var e,t;return(null==(e=navigator.connection)?void 0:e.effectiveType.includes("2g"))||(null==(t=navigator.connection)?void 0:t.saveData)},Q={prefetch(e){if(!(e=>!Y()&&!W.has(e)&&!V.has(e))(e))return!1;V.add(e);const t=(0,d.f)(u.Z,e).flatMap((e=>{return t=e.route.path,Object.entries($).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Z.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?H(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!Y()&&!W.has(e))(e)&&(W.add(e),N(e))},K=Object.freeze(Q);if(l.Z.canUseDOM){window.docusaurus=K;const e=a.hydrate;N(window.location.pathname).then((()=>{e(r.createElement(i.B6,null,r.createElement(o.VK,null,r.createElement(G,null))),document.getElementById("__docusaurus"))}))}},694:(e,t,n)=>{"use strict";n.d(t,{_:()=>c,M:()=>d});var r=n(7294),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-google-gtag":{"default":{"trackingID":"G-P59RXWWG7S","anonymizeIP":false,"id":"default"}},"docusaurus-plugin-content-docs":{"default":{"path":"/","versions":[{"name":"current","label":"Next","isLast":true,"path":"/","mainDocId":"intro","docs":[{"id":"developers/how-to-build-plugins","path":"/developers/how-to-build-plugins","sidebar":"tutorialSidebar"},{"id":"developers/how-to-create-exhaust-script","path":"/developers/how-to-create-exhaust-script","sidebar":"tutorialSidebar"},{"id":"developers/how-to-refine-plugins","path":"/developers/how-to-refine-plugins","sidebar":"tutorialSidebar"},{"id":"developers/how-to-submit-plugins","path":"/developers/how-to-submit-plugins","sidebar":"tutorialSidebar"},{"id":"developers/how-to-write-unit-tests","path":"/developers/how-to-write-unit-tests","sidebar":"tutorialSidebar"},{"id":"developers/index","path":"/developers/","sidebar":"tutorialSidebar"},{"id":"FAQ","path":"/FAQ","sidebar":"tutorialSidebar"},{"id":"intro","path":"/intro","sidebar":"tutorialSidebar"},{"id":"major-concepts/aggregation","path":"/major-concepts/aggregation","sidebar":"tutorialSidebar"},{"id":"major-concepts/design-philosophy","path":"/major-concepts/design-philosophy","sidebar":"tutorialSidebar"},{"id":"major-concepts/exhaust-script","path":"/major-concepts/exhaust-script","sidebar":"tutorialSidebar"},{"id":"major-concepts/if","path":"/major-concepts/if","sidebar":"tutorialSidebar"},{"id":"major-concepts/index","path":"/major-concepts/","sidebar":"tutorialSidebar"},{"id":"major-concepts/manifest-file","path":"/major-concepts/manifest-file","sidebar":"tutorialSidebar"},{"id":"major-concepts/pipelines","path":"/major-concepts/pipelines","sidebar":"tutorialSidebar"},{"id":"major-concepts/plugins","path":"/major-concepts/plugins","sidebar":"tutorialSidebar"},{"id":"major-concepts/regroup","path":"/major-concepts/regroup","sidebar":"tutorialSidebar"},{"id":"major-concepts/time","path":"/major-concepts/time","sidebar":"tutorialSidebar"},{"id":"pipelines/cpu-to-carbon","path":"/pipelines/cpu-to-carbon","sidebar":"tutorialSidebar"},{"id":"pipelines/index","path":"/pipelines/","sidebar":"tutorialSidebar"},{"id":"pipelines/instance-metadata","path":"/pipelines/instance-metadata","sidebar":"tutorialSidebar"},{"id":"pipelines/sci","path":"/pipelines/sci","sidebar":"tutorialSidebar"},{"id":"pipelines/teads","path":"/pipelines/teads","sidebar":"tutorialSidebar"},{"id":"reference/cli","path":"/reference/cli","sidebar":"tutorialSidebar"},{"id":"reference/errors","path":"/reference/errors","sidebar":"tutorialSidebar"},{"id":"reference/features","path":"/reference/features","sidebar":"tutorialSidebar"},{"id":"reference/index","path":"/reference/","sidebar":"tutorialSidebar"},{"id":"reference/plugins","path":"/reference/plugins","sidebar":"tutorialSidebar"},{"id":"users/how-to-compare-files-with-if-diff","path":"/users/how-to-compare-files-with-if-diff","sidebar":"tutorialSidebar"},{"id":"users/how-to-export-csv-file-with-if-csv","path":"/users/how-to-export-csv-file-with-if-csv","sidebar":"tutorialSidebar"},{"id":"users/how-to-import-plugins","path":"/users/how-to-import-plugins","sidebar":"tutorialSidebar"},{"id":"users/how-to-install-if","path":"/users/how-to-install-if","sidebar":"tutorialSidebar"},{"id":"users/how-to-use-the-explain-feature","path":"/users/how-to-use-the-explain-feature","sidebar":"tutorialSidebar"},{"id":"users/how-to-verify-files-with-if-check","path":"/users/how-to-verify-files-with-if-check","sidebar":"tutorialSidebar"},{"id":"users/how-to-write-manifests","path":"/users/how-to-write-manifests","sidebar":"tutorialSidebar"},{"id":"users/index","path":"/users/","sidebar":"tutorialSidebar"},{"id":"users/quick-start","path":"/users/quick-start","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/intro","label":"intro"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en"}}}');var l=n(7529);const s=JSON.parse('{"docusaurusVersion":"2.0.1","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.0.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.0.1"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"2.0.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.0.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.0.1"},"docusaurus-plugin-ideal-image":{"type":"package","name":"@docusaurus/plugin-ideal-image","version":"2.0.1"}}}'),u={siteConfig:a.default,siteMetadata:s,globalData:o,i18n:i,codeTranslations:l},c=r.createContext(u);function d(e){let{children:t}=e;return r.createElement(c.Provider,{value:u},t)}},3256:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});var r=n(7294),a=n(6136),o=n(1514),i=n(1982);function l(e){let{error:t,tryAgain:n}=e;return r.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",height:"50vh",width:"100%",fontSize:"20px"}},r.createElement("h1",null,"This page crashed."),r.createElement("p",null,t.message),r.createElement("button",{type:"button",onClick:n},"Try again"))}function s(e){let{error:t,tryAgain:n}=e;return r.createElement(c,{fallback:()=>r.createElement(l,{error:t,tryAgain:n})},r.createElement(o.Z,null,r.createElement("title",null,"Page Error")),r.createElement(i.Z,null,r.createElement(l,{error:t,tryAgain:n})))}const u=e=>r.createElement(s,e);class c extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){var n;const e={error:t,tryAgain:()=>this.setState({error:null})};return(null!=(n=this.props.fallback)?n:u)(e)}return null!=e?e:null}}},6136:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},1514:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(405);function o(e){return r.createElement(a.ql,e)}},3699:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7462),a=n(7294),o=n(3727),i=n(3905),l=n(9962),s=n(2735),u=n(6136);const c=a.createContext({collectLink:()=>{}});var d=n(9524);function f(e,t){var n,f;let{isNavLink:p,to:m,href:h,activeClassName:g,isActive:v,"data-noBrokenLinkCheck":b,autoAddBaseUrl:y=!0,...w}=e;const{siteConfig:{trailingSlash:k,baseUrl:E}}=(0,l.Z)(),{withBaseUrl:S}=(0,d.C)(),x=(0,a.useContext)(c),_=(0,a.useRef)(null);(0,a.useImperativeHandle)(t,(()=>_.current));const C=m||h;const T=(0,s.Z)(C),A=null==C?void 0:C.replace("pathname://","");let L=void 0!==A?(R=A,y&&(e=>e.startsWith("/"))(R)?S(R):R):void 0;var R;L&&T&&(L=(0,i.applyTrailingSlash)(L,{trailingSlash:k,baseUrl:E}));const P=(0,a.useRef)(!1),N=p?o.OL:o.rU,O=u.Z.canUseIntersectionObserver,D=(0,a.useRef)();(0,a.useEffect)((()=>(!O&&T&&null!=L&&window.docusaurus.prefetch(L),()=>{O&&D.current&&D.current.disconnect()})),[D,L,O,T]);const I=null!=(n=null==(f=L)?void 0:f.startsWith("#"))&&n,M=!L||!T||I;return M||b||x.collectLink(L),M?a.createElement("a",(0,r.Z)({ref:_,href:L},C&&!T&&{target:"_blank",rel:"noopener noreferrer"},w)):a.createElement(N,(0,r.Z)({},w,{onMouseEnter:()=>{P.current||null==L||(window.docusaurus.preload(L),P.current=!0)},innerRef:e=>{_.current=e,O&&e&&T&&(D.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(D.current.unobserve(e),D.current.disconnect(),null!=L&&window.docusaurus.prefetch(L))}))})),D.current.observe(e))},to:L},p&&{isActive:v,activeClassName:g}))}const p=a.forwardRef(f)},7325:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s,I:()=>l});var r=n(7294);function a(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=null==t?void 0:t[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(7529);function i(e){var t,n;let{id:r,message:a}=e;if(void 0===r&&void 0===a)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return null!=(t=null!=(n=o[null!=r?r:a])?n:a)?t:r}function l(e,t){let{message:n,id:r}=e;return a(i({message:n,id:r}),t)}function s(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");const l=i({message:t,id:n});return r.createElement(r.Fragment,null,a(l,o))}},6875:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});const r="default"},2735:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},9524:(e,t,n)=>{"use strict";n.d(t,{C:()=>o,Z:()=>i});var r=n(9962),a=n(2735);function o(){const{siteConfig:{baseUrl:e,url:t}}=(0,r.Z)();return{withBaseUrl:(n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:o=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,a.b)(n))return n;if(o)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const l=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+l:l}(t,e,n,r)}}function i(e,t){void 0===t&&(t={});const{withBaseUrl:n}=o();return n(e,t)}},9962:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(694);function o(){return(0,r.useContext)(a._)}},1048:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8121);function o(){return(0,r.useContext)(a._)}},5304:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});function r(e){const t={};return function e(n,r){Object.entries(n).forEach((n=>{let[a,o]=n;const i=r?r+"."+a:a;var l;"object"==typeof(l=o)&&l&&Object.keys(l).length>0?e(o,i):t[i]=o}))}(e),t}},9656:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(7294);const a=r.createContext(null);function o(e){let{children:t,value:n}=e;const o=r.useContext(a),i=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...null==n?void 0:n.data};return{plugin:t.plugin,data:r}}({parent:o,value:n})),[o,n]);return r.createElement(a.Provider,{value:i},t)}},9871:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>h,gA:()=>f,_r:()=>c,Jo:()=>g,zh:()=>d,yW:()=>m,gB:()=>p});var r=n(6775),a=n(9962),o=n(6875);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.Z)();return e}()[e];if(!n&&t.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin.');return n}const l=e=>e.versions.find((e=>e.isLast));function s(e,t){const n=function(e,t){const n=l(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=null==n?void 0:n.docs.find((e=>!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const u={},c=()=>{var e;return null!=(e=i("docusaurus-plugin-content-docs"))?e:u},d=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const r=i(e),a=null==r?void 0:r[t];if(!a&&n.failfast)throw new Error('Docusaurus plugin global data not found for "'+e+'" plugin with id "'+t+'".');return a}("docusaurus-plugin-content-docs",e,{failfast:!0});function f(e){void 0===e&&(e={});const t=c(),{pathname:n}=(0,r.TH)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error("Can't find active docs plugin for \""+t+'" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: '+Object.values(e).map((e=>e.path)).join(", "));return o}(t,n,e)}function p(e){return d(e).versions}function m(e){const t=d(e);return l(t)}function h(e){const t=d(e),{pathname:n}=(0,r.TH)();return s(t,n)}function g(e){const t=d(e),{pathname:n}=(0,r.TH)();return function(e,t){const n=l(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},4313:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={onRouteDidUpdate(e){let{location:t,previousLocation:n}=e;!n||t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash||setTimeout((()=>{window.gtag("event","page_view",{page_title:document.title,page_location:window.location.href,page_path:t.pathname+t.search+t.hash})}))}}},9957:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4865),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},2251:(e,t,n)=>{"use strict";n.r(t);var r=n(7410),a=n(6809);!function(e){const{themeConfig:{prism:t}}=a.default,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{n(6726)("./prism-"+e)})),delete globalThis.Prism}(r.Z)},4082:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294);const a="iconExternalLink_nPIU";function o(e){let{width:t=13.5,height:n=13.5}=e;return r.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:a},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},1982:(e,t,n)=>{"use strict";n.d(t,{Z:()=>at});var r=n(7294),a=n(6010),o=n(3256),i=n(5463),l=n(3702),s=n(8181),u=n(7325),c=n(6775),d=n(3266);function f(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}const p="skipToContent_fXgn";function m(){const{containerRef:e,handleSkip:t}=function(){const e=(0,r.useRef)(null),{action:t}=(0,c.k6)(),n=(0,r.useCallback)((e=>{var t;e.preventDefault();const n=null!=(t=document.querySelector("main:first-of-type"))?t:document.querySelector("."+l.k.wrapper.main);n&&f(n)}),[]);return(0,d.S)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&f(e.current)})),{containerRef:e,handleSkip:n}}();return r.createElement("div",{ref:e,role:"region"},r.createElement("a",{href:"#",className:p,onClick:t},r.createElement(u.Z,{id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"},"Skip to main content")))}var h=n(107),g=n(5830),v=n(7462);function b(e){let{width:t=21,height:n=21,color:a="currentColor",strokeWidth:o=1.2,className:i,...l}=e;return r.createElement("svg",(0,v.Z)({viewBox:"0 0 15 15",width:t,height:n},l),r.createElement("g",{stroke:a,strokeWidth:o},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const y="announcementBar_mb4j",w="announcementBarPlaceholder_vyr4",k="announcementBarClose_gvF7",E="announcementBarContent_xLdY";function S(){const{isActive:e,close:t}=(0,g.nT)(),{announcementBar:n}=(0,h.L)();if(!e)return null;const{content:o,backgroundColor:i,textColor:l,isCloseable:s}=n;return r.createElement("div",{className:y,style:{backgroundColor:i,color:l},role:"banner"},s&&r.createElement("div",{className:w}),r.createElement("div",{className:E,dangerouslySetInnerHTML:{__html:o}}),s?r.createElement("button",{type:"button",className:(0,a.Z)("clean-btn close",k),onClick:t,"aria-label":(0,u.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},r.createElement(b,{width:14,height:14,strokeWidth:3.1})):null)}var x=n(2600),_=n(2957);var C=n(3768),T=n(3086);const A=r.createContext(null);function L(e){let{children:t}=e;const n=function(){const e=(0,x.e)(),t=(0,T.HY)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,C.D9)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return r.createElement(A.Provider,{value:n},t)}function R(e){if(e.component){const t=e.component;return r.createElement(t,e.props)}}function P(){const e=(0,r.useContext)(A);if(!e)throw new C.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,T.HY)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:R(o)})),[a,o,t])}function N(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=P();return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var O=n(9200),D=n(1048);function I(e){return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function M(e){return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const F={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function j(e){let{className:t,value:n,onChange:o}=e;const i=(0,D.Z)(),l=(0,u.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===n?(0,u.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,u.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(F.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",F.toggleButton,!i&&F.toggleButtonDisabled),type:"button",onClick:()=>o("dark"===n?"light":"dark"),disabled:!i,title:l,"aria-label":l},r.createElement(I,{className:(0,a.Z)(F.toggleIcon,F.lightToggleIcon)}),r.createElement(M,{className:(0,a.Z)(F.toggleIcon,F.darkToggleIcon)})))}const B=r.memo(j);function z(e){let{className:t}=e;const n=(0,h.L)().colorMode.disableSwitch,{colorMode:a,setColorMode:o}=(0,O.I)();return n?null:r.createElement(B,{className:t,value:a,onChange:o})}var U=n(6811);function q(){return r.createElement(U.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function G(){const e=(0,x.e)();return r.createElement("button",{type:"button",className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},r.createElement(b,{color:"var(--ifm-color-emphasis-600)"}))}function $(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement(q,null),r.createElement(z,{className:"margin-right--md"}),r.createElement(G,null))}var H=n(3699),Z=n(9524),V=n(2735);function W(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var Y=n(4082);function Q(e){let{activeBasePath:t,activeBaseRegex:n,to:a,href:o,label:i,html:l,isDropdownLink:s,prependBaseUrlToHref:u,...c}=e;const d=(0,Z.Z)(a),f=(0,Z.Z)(t),p=(0,Z.Z)(o,{forcePrependBaseUrl:!0}),m=i&&o&&!(0,V.Z)(o),h=l?{dangerouslySetInnerHTML:{__html:l}}:{children:r.createElement(r.Fragment,null,i,m&&r.createElement(Y.Z,s&&{width:12,height:12}))};return o?r.createElement(H.Z,(0,v.Z)({href:u?p:o},c,h)):r.createElement(H.Z,(0,v.Z)({to:d,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?W(n,t.pathname):t.pathname.startsWith(f)},c,h))}function K(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=r.createElement(Q,(0,v.Z)({className:(0,a.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?r.createElement("li",null,i):i}function X(e){let{className:t,isDropdownItem:n,...o}=e;return r.createElement("li",{className:"menu__list-item"},r.createElement(Q,(0,v.Z)({className:(0,a.Z)("menu__link",t)},o)))}function J(e){var t;let{mobile:n=!1,position:a,...o}=e;const i=n?X:K;return r.createElement(i,(0,v.Z)({},o,{activeClassName:null!=(t=o.activeClassName)?t:n?"menu__link--active":"navbar__link--active"}))}var ee=n(4639),te=n(9003),ne=n(9962);function re(e,t){return e.some((e=>function(e,t){return!!(0,te.Mg)(e.to,t)||!!W(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function ae(e){var t;let{items:n,position:o,className:i,onClick:l,...s}=e;const u=(0,r.useRef)(null),[c,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{u.current&&!u.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e)}}),[u]),r.createElement("div",{ref:u,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===o,"dropdown--show":c})},r.createElement(Q,(0,v.Z)({"aria-haspopup":"true","aria-expanded":c,role:"button",href:s.to?void 0:"#",className:(0,a.Z)("navbar__link",i)},s,{onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!c))}}),null!=(t=s.children)?t:s.label),r.createElement("ul",{className:"dropdown__menu"},n.map(((e,t)=>r.createElement(be,(0,v.Z)({isDropdownItem:!0,onKeyDown:e=>{if(t===n.length-1&&"Tab"===e.key){e.preventDefault(),d(!1);const t=u.current.nextElementSibling;if(t){(t instanceof HTMLAnchorElement?t:t.querySelector("a")).focus()}}},activeClassName:"dropdown__link--active"},e,{key:t}))))))}function oe(e){var t;let{items:n,className:o,position:i,onClick:l,...s}=e;const u=function(){const{siteConfig:{baseUrl:e}}=(0,ne.Z)(),{pathname:t}=(0,c.TH)();return t.replace(e,"/")}(),d=re(n,u),{collapsed:f,toggleCollapsed:p,setCollapsed:m}=(0,ee.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[u,d,m]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":f})},r.createElement(Q,(0,v.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",o)},s,{onClick:e=>{e.preventDefault(),p()}}),null!=(t=s.children)?t:s.label),r.createElement(ee.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:f},n.map(((e,t)=>r.createElement(be,(0,v.Z)({mobile:!0,isDropdownItem:!0,onClick:l,activeClassName:"menu__link--active"},e,{key:t}))))))}function ie(e){let{mobile:t=!1,...n}=e;const a=t?oe:ae;return r.createElement(a,n)}var le=n(626);function se(e){let{width:t=20,height:n=20,...a}=e;return r.createElement("svg",(0,v.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},a),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const ue="iconLanguage_nlXk";const ce=()=>null,de="searchBox_ZlJk";function fe(e){let{children:t,className:n}=e;return r.createElement("div",{className:(0,a.Z)(n,de)},t)}var pe=n(9871),me=n(3734);var he=n(6409);const ge=e=>e.docs.find((t=>t.id===e.mainDocId));const ve={default:J,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:a,...o}=e;const{i18n:{currentLocale:i,locales:l,localeConfigs:s}}=(0,ne.Z)(),c=(0,le.l)(),d=[...n,...l.map((e=>{const n="pathname://"+c.createUrl({locale:e,fullyQualified:!1});return{label:s[e].label,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...a],f=t?(0,u.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):s[i].label;return r.createElement(ie,(0,v.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(se,{className:ue}),f),items:d}))},search:function(e){let{mobile:t,className:n}=e;return t?null:r.createElement(fe,{className:n},r.createElement(ce,null))},dropdown:ie,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const l=i?"li":"div";return r.createElement(l,{className:(0,a.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,pe.Iw)(a),l=(0,me.vY)(t,a);return null===l?null:r.createElement(J,(0,v.Z)({exact:!0},o,{isActive:()=>(null==i?void 0:i.path)===l.path||!(null==i||!i.sidebar)&&i.sidebar===l.sidebar,label:null!=n?n:l.id,to:l.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,pe.Iw)(a),l=(0,me.oz)(t,a).link;if(!l)throw new Error('DocSidebarNavbarItem: Sidebar with ID "'+t+"\" doesn't have anything to be linked to.");return r.createElement(J,(0,v.Z)({exact:!0},o,{isActive:()=>(null==i?void 0:i.sidebar)===t,label:null!=n?n:l.label,to:l.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:a,...o}=e;const i=(0,me.lO)(a)[0],l=null!=t?t:i.label,s=null!=n?n:(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return r.createElement(J,(0,v.Z)({},o,{label:l,to:s}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:a,dropdownItemsBefore:o,dropdownItemsAfter:i,...l}=e;const s=(0,pe.Iw)(n),c=(0,pe.gB)(n),{savePreferredVersionName:d}=(0,he.J)(n),f=[...o,...c.map((e=>{var t;const n=null!=(t=s.alternateDocVersions[e.name])?t:ge(e);return{label:e.label,to:n.path,isActive:()=>e===s.activeVersion,onClick:()=>d(e.name)}})),...i],p=(0,me.lO)(n)[0],m=t&&f.length>1?(0,u.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):p.label,h=t&&f.length>1?void 0:ge(p).path;return f.length<=1?r.createElement(J,(0,v.Z)({},l,{mobile:t,label:m,to:h,isActive:a?()=>!1:void 0})):r.createElement(ie,(0,v.Z)({},l,{mobile:t,label:m,to:h,items:f,isActive:a?()=>!1:void 0}))}};function be(e){let{type:t,...n}=e;const a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=ve[a];if(!o)throw new Error('No NavbarItem component found for type "'+t+'".');return r.createElement(o,n)}function ye(){const e=(0,x.e)(),t=(0,h.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map(((t,n)=>r.createElement(be,(0,v.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function we(e){return r.createElement("button",(0,v.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(u.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function ke(){const e=0===(0,h.L)().navbar.items.length,t=P();return r.createElement(r.Fragment,null,!e&&r.createElement(we,{onClick:()=>t.hide()}),t.content)}function Ee(){const e=(0,x.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?r.createElement(N,{header:r.createElement($,null),primaryMenu:r.createElement(ye,null),secondaryMenu:r.createElement(ke,null)}):null}const Se="navbarHideable_m1mJ",xe="navbarHidden_jGov";function _e(e){return r.createElement("div",(0,v.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function Ce(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,h.L)(),i=(0,x.e)(),{navbarRef:l,isNavbarVisible:s}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,_.RF)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i=l?n(!1):i+u{if(e)return t.location.hash?(a.current=!0,void n(!1)):void n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return r.createElement("nav",{ref:l,className:(0,a.Z)("navbar","navbar--fixed-top",n&&[Se,!s&&xe],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,r.createElement(_e,{onClick:i.toggle}),r.createElement(Ee,null))}function Te(e){let{width:t=30,height:n=30,className:a,...o}=e;return r.createElement("svg",(0,v.Z)({className:a,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),r.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function Ae(){const e=(0,x.e)();return r.createElement("button",{onClick:e.toggle,onKeyDown:e.toggle,"aria-label":"Navigation bar toggle",className:"navbar__toggle clean-btn",type:"button",tabIndex:0},r.createElement(Te,null))}const Le="colorModeToggle_DEke";function Re(e){let{items:t}=e;return r.createElement(r.Fragment,null,t.map(((e,t)=>r.createElement(be,(0,v.Z)({},e,{key:t})))))}function Pe(e){let{left:t,right:n}=e;return r.createElement("div",{className:"navbar__inner"},r.createElement("div",{className:"navbar__items"},t),r.createElement("div",{className:"navbar__items navbar__items--right"},n))}function Ne(){const e=(0,x.e)(),t=(0,h.L)().navbar.items,[n,a]=function(e){function t(e){var t;return"left"===(null!=(t=e.position)?t:"right")}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return r.createElement(Pe,{left:r.createElement(r.Fragment,null,!e.disabled&&r.createElement(Ae,null),r.createElement(q,null),r.createElement(Re,{items:n})),right:r.createElement(r.Fragment,null,r.createElement(Re,{items:a}),r.createElement(z,{className:Le}),!o&&r.createElement(fe,null,r.createElement(ce,null)))})}function Oe(){return r.createElement(Ce,null,r.createElement(Ne,null))}function De(e){return r.createElement(r.Fragment,null,r.createElement("div",{style:{background:"#f0ffc4",textAlign:"center",padding:"16px 32px",color:"var(--ifm-color-primary-dark)",fontWeight:"bold",fontSize:"0.875rem"}},"This project is an ",r.createElement("b",null,"incubation project")," being run inside the Green Software Foundation; as such, we DON\u2019T recommend using it in any critical use case. Incubation projects are experimental, offer no support guarantee, have minimal governance and process, and may be retired at any moment. This project may one day Graduate, in which case this disclaimer will be removed."),r.createElement(Oe,e))}function Ie(e){let{item:t}=e;const{to:n,href:a,label:o,prependBaseUrlToHref:i,...l}=t,s=(0,Z.Z)(n),u=(0,Z.Z)(a,{forcePrependBaseUrl:!0});return r.createElement(H.Z,(0,v.Z)({className:"footer__link-item"},a?{href:i?u:a}:{to:s},l),o,a&&!(0,V.Z)(a)&&r.createElement(Y.Z,null))}function Me(e){var t;let{item:n}=e;return n.html?r.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:n.html}}):r.createElement("li",{key:null!=(t=n.href)?t:n.to,className:"footer__item"},r.createElement(Ie,{item:n}))}function Fe(e){let{column:t}=e;return r.createElement("div",{className:"col footer__col"},r.createElement("div",{className:"footer__title"},t.title),r.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>r.createElement(Me,{key:t,item:e})))))}function je(e){let{columns:t}=e;return r.createElement("div",{className:"row footer__links"},t.map(((e,t)=>r.createElement(Fe,{key:t,column:e}))))}function Be(){return r.createElement("span",{className:"footer__link-separator"},"\xb7")}function ze(e){let{item:t}=e;return t.html?r.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement(Ie,{item:t})}function Ue(e){let{links:t}=e;return r.createElement("div",{className:"footer__links text--center"},r.createElement("div",{className:"footer__links"},t.map(((e,n)=>r.createElement(r.Fragment,{key:n},r.createElement(ze,{item:e}),t.length!==n+1&&r.createElement(Be,null))))))}function qe(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?r.createElement(je,{columns:t}):r.createElement(Ue,{links:t})}var Ge=n(7909);const $e="footerLogoLink_BH7S";function He(e){var t;let{logo:n}=e;const{withBaseUrl:o}=(0,Z.C)(),i={light:o(n.src),dark:o(null!=(t=n.srcDark)?t:n.src)};return r.createElement(Ge.Z,{className:(0,a.Z)("footer__logo",n.className),alt:n.alt,sources:i,width:n.width,height:n.height,style:n.style})}function Ze(e){let{logo:t}=e;return t.href?r.createElement(H.Z,{href:t.href,className:$e,target:t.target},r.createElement(He,{logo:t})):r.createElement(He,{logo:t})}function Ve(e){let{copyright:t}=e;return r.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function We(e){let{style:t,links:n,logo:o,copyright:i}=e;return r.createElement("footer",{className:(0,a.Z)("footer",{"footer--dark":"dark"===t})},r.createElement("div",{className:"container container-fluid"},n,(o||i)&&r.createElement("div",{className:"footer__bottom text--center"},o&&r.createElement("div",{className:"margin-bottom--sm"},o),i)))}function Ye(){const{footer:e}=(0,h.L)();if(!e)return null;const{copyright:t,links:n,logo:a,style:o}=e;return r.createElement(We,{style:o,links:n&&n.length>0&&r.createElement(qe,{links:n}),logo:a&&r.createElement(Ze,{logo:a}),copyright:t&&r.createElement(Ve,{copyright:t})})}const Qe=r.memo(Ye);var Ke=n(2560);const Xe="docusaurus.tab.",Je=r.createContext(void 0);const et=(0,C.Qc)([O.S,g.pl,function(e){let{children:t}=e;const n=function(){const[e,t]=(0,r.useState)({}),n=(0,r.useCallback)(((e,t)=>{(0,Ke.W)("docusaurus.tab."+e).set(t)}),[]);(0,r.useEffect)((()=>{try{const e={};(0,Ke._)().forEach((t=>{if(t.startsWith(Xe)){const n=t.substring(Xe.length);e[n]=(0,Ke.W)(t).get()}})),t(e)}catch(e){console.error(e)}}),[]);const a=(0,r.useCallback)(((e,r)=>{t((t=>({...t,[e]:r}))),n(e,r)}),[n]);return(0,r.useMemo)((()=>({tabGroupChoices:e,setTabGroupChoices:a})),[e,a])}();return r.createElement(Je.Provider,{value:n},t)},_.OC,he.L5,i.VC,function(e){let{children:t}=e;return r.createElement(T.n2,null,r.createElement(x.M,null,r.createElement(L,null,t)))}]);function tt(e){let{children:t}=e;return r.createElement(et,null,t)}function nt(e){let{error:t,tryAgain:n}=e;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(u.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("p",null,t.message),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},r.createElement(u.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again when the page crashed"},"Try again"))))))}const rt="mainWrapper_z2l0";function at(e){const{children:t,noFooter:n,wrapperClassName:u,title:c,description:d}=e;return(0,s.t)(),r.createElement(tt,null,r.createElement(i.d,{title:c,description:d}),r.createElement(m,null),r.createElement(S,null),r.createElement(De,null),r.createElement("div",{className:(0,a.Z)(l.k.wrapper.main,rt,u)},r.createElement(o.Z,{fallback:e=>r.createElement(nt,e)},t)),!n&&r.createElement(Qe,null))}},6811:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var r=n(7462),a=n(7294),o=n(3699),i=n(9524),l=n(9962),s=n(107),u=n(7909);function c(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},l=a.createElement(u.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?a.createElement("div",{className:r},l):l}function d(e){var t;const{siteConfig:{title:n}}=(0,l.Z)(),{navbar:{title:u,logo:d}}=(0,s.L)(),{imageClassName:f,titleClassName:p,...m}=e,h=(0,i.Z)((null==d?void 0:d.href)||"/"),g=u?"":n,v=null!=(t=null==d?void 0:d.alt)?t:g;return a.createElement(o.Z,(0,r.Z)({to:h},m,(null==d?void 0:d.target)&&{target:d.target}),d&&a.createElement(c,{logo:d,alt:v,imageClassName:f}),null!=u&&a.createElement("b",{className:p},u))}},3647:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(1514);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},7909:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(7462),a=n(7294),o=n(6010),i=n(1048),l=n(9200);const s={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};function u(e){const t=(0,i.Z)(),{colorMode:n}=(0,l.I)(),{sources:u,className:c,alt:d,...f}=e,p=t?"dark"===n?["dark"]:["light"]:["light","dark"];return a.createElement(a.Fragment,null,p.map((e=>a.createElement("img",(0,r.Z)({key:e,src:u[e],alt:d,className:(0,o.Z)(s.themedImage,s["themedImage--"+e],c)},f)))))}},4639:(e,t,n)=>{"use strict";n.d(t,{u:()=>i,z:()=>m});var r=n(7462),a=n(7294),o=n(6136);function i(e){let{initialState:t}=e;const[n,r]=(0,a.useState)(null!=t&&t),o=(0,a.useCallback)((()=>{r((e=>!e))}),[]);return{collapsed:n,setCollapsed:r,toggleCollapsed:o}}const l={display:"none",overflow:"hidden",height:"0px"},s={display:"block",overflow:"visible",height:"auto"};function u(e,t){const n=t?l:s;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function c(e){let{collapsibleRef:t,collapsed:n,animation:r}=e;const o=(0,a.useRef)(!1);(0,a.useEffect)((()=>{const e=t.current;function a(){var t,n;const a=e.scrollHeight,o=null!=(t=null==r?void 0:r.duration)?t:function(e){const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(a);return{transition:"height "+o+"ms "+(null!=(n=null==r?void 0:r.easing)?n:"ease-in-out"),height:a+"px"}}function i(){const t=a();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return u(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(i(),requestAnimationFrame((()=>{e.style.height=l.height,e.style.overflow=l.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{i()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,r])}function d(e){if(!o.Z.canUseDOM)return e?l:s}function f(e){let{as:t="div",collapsed:n,children:r,animation:o,onCollapseTransitionEnd:i,className:l,disableSSRStyle:s}=e;const f=(0,a.useRef)(null);return c({collapsibleRef:f,collapsed:n,animation:o}),a.createElement(t,{ref:f,style:s?void 0:d(n),onTransitionEnd:e=>{"height"===e.propertyName&&(u(f.current,n),null==i||i(n))},className:l},r)}function p(e){let{collapsed:t,...n}=e;const[o,i]=(0,a.useState)(!t),[l,s]=(0,a.useState)(t);return(0,a.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,a.useLayoutEffect)((()=>{o&&s(t)}),[o,t]),o?a.createElement(f,(0,r.Z)({},n,{collapsed:l})):null}function m(e){let{lazy:t,...n}=e;const r=t?p:f;return a.createElement(r,n)}},5830:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>p});var r=n(7294),a=n(1048),o=n(2560),i=n(3768),l=n(107);const s=(0,o.W)("docusaurus.announcement.dismiss"),u=(0,o.W)("docusaurus.announcement.id"),c=()=>"true"===s.get(),d=e=>s.set(String(e)),f=r.createContext(null);function p(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,l.L)(),t=(0,a.Z)(),[n,o]=(0,r.useState)((()=>!!t&&c()));(0,r.useEffect)((()=>{o(c())}),[]);const i=(0,r.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=u.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;u.set(t),r&&d(!1),!r&&c()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return r.createElement(f.Provider,{value:n},t)}function m(){const e=(0,r.useContext)(f);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},9200:(e,t,n)=>{"use strict";n.d(t,{I:()=>g,S:()=>h});var r=n(7294),a=n(6136),o=n(3768),i=n(2560),l=n(107);const s=r.createContext(void 0),u="theme",c=(0,i.W)(u),d="light",f="dark",p=e=>e===f?f:d;function m(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,l.L)(),[o,i]=(0,r.useState)((e=>a.Z.canUseDOM?p(document.documentElement.getAttribute("data-theme")):p(e))(e));(0,r.useEffect)((()=>{t&&c.del()}),[t]);const s=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(i(t),a&&(e=>{c.set(p(e))})(t)):(i(n?window.matchMedia("(prefers-color-scheme: dark)").matches?f:d:e),c.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",p(o))}),[o]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==u)return;const t=c.get();null!==t&&s(p(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,s]);const m=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||m.current?m.current=window.matchMedia("print").matches:s(null)};return e.addListener(r),()=>e.removeListener(r)}),[s,t,n]),(0,r.useMemo)((()=>({colorMode:o,setColorMode:s,get isDarkTheme(){return o===f},setLightTheme(){s(d)},setDarkTheme(){s(f)}})),[o,s])}function h(e){let{children:t}=e;const n=m();return r.createElement(s.Provider,{value:n},t)}function g(){const e=(0,r.useContext)(s);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},6409:(e,t,n)=>{"use strict";n.d(t,{J:()=>y,L5:()=>v});var r=n(7294),a=n(9871),o=n(6875),i=n(107),l=n(3734),s=n(3768),u=n(2560);const c=e=>"docs-preferred-version-"+e,d=(e,t,n)=>{(0,u.W)(c(e),{persistence:t}).set(n)},f=(e,t)=>(0,u.W)(c(e),{persistence:t}).get(),p=(e,t)=>{(0,u.W)(c(e),{persistence:t}).del()};const m=r.createContext(null);function h(){const e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,l]=(0,r.useState)((()=>(e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}]))))(n)));(0,r.useEffect)((()=>{l(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=f(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){d(e,t,n),l((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function g(e){let{children:t}=e;const n=h();return r.createElement(m.Provider,{value:n},t)}function v(e){let{children:t}=e;return l.cE?r.createElement(g,null,t):r.createElement(r.Fragment,null,t)}function b(){const e=(0,r.useContext)(m);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function y(e){var t;void 0===e&&(e=o.m);const n=(0,a.zh)(e),[i,l]=b(),{preferredVersionName:s}=i[e];return{preferredVersion:null!=(t=n.versions.find((e=>e.name===s)))?t:null,savePreferredVersionName:(0,r.useCallback)((t=>{l.savePreferredVersion(e,t)}),[l,e])}}},4432:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,b:()=>l});var r=n(7294),a=n(3768);const o=Symbol("EmptyContext"),i=r.createContext(o);function l(e){let{children:t,name:n,items:a}=e;const o=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return r.createElement(i.Provider,{value:o},t)}function s(){const e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},2600:(e,t,n)=>{"use strict";n.d(t,{M:()=>f,e:()=>p});var r=n(7294),a=n(3086),o=n(3488),i=n(6775),l=n(3768);function s(e){!function(e){const t=(0,i.k6)(),n=(0,l.zX)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var u=n(107);const c=r.createContext(void 0);function d(){const e=function(){const e=(0,a.HY)(),{items:t}=(0,u.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[i,l]=(0,r.useState)(!1);s((()=>{if(i)return l(!1),!1}));const c=(0,r.useCallback)((()=>{l((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&l(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:c,shown:i})),[e,n,c,i])}function f(e){let{children:t}=e;const n=d();return r.createElement(c.Provider,{value:n},t)}function p(){const e=r.useContext(c);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},3086:(e,t,n)=>{"use strict";n.d(t,{HY:()=>l,Zo:()=>s,n2:()=>i});var r=n(7294),a=n(3768);const o=r.createContext(null);function i(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){let{component:t,props:n}=e;const i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");const[,l]=i,s=(0,a.Ql)(n);return(0,r.useEffect)((()=>{l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((()=>()=>l({component:null,props:null})),[l]),null}},8181:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(7294);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},3488:(e,t,n)=>{"use strict";n.d(t,{i:()=>u});var r=n(7294),a=n(6136);const o="desktop",i="mobile",l="ssr";function s(){return a.Z.canUseDOM?window.innerWidth>996?o:i:l}function u(){const[e,t]=(0,r.useState)((()=>s()));return(0,r.useEffect)((()=>{function e(){t(s())}return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(undefined)}}),[]),e}},3702:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>"theme-admonition-"+e},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>"theme-doc-sidebar-item-category-level-"+e,docSidebarItemLinkLevel:e=>"theme-doc-sidebar-item-link-level-"+e},blog:{}}},3734:(e,t,n)=>{"use strict";n.d(t,{Wl:()=>f,_F:()=>m,cE:()=>d,hI:()=>y,lO:()=>g,vY:()=>b,oz:()=>v,s1:()=>h});var r=n(7294),a=n(6775),o=n(8790),i=n(9871),l=n(6409),s=n(4432);function u(e){return Array.from(new Set(e))}var c=n(9003);const d=!!i._r;function f(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=f(t);if(e)return e}}}const p=(e,t)=>void 0!==e&&(0,c.Mg)(e,t);function m(e,t){return"link"===e.type?p(e.href,t):"category"===e.type&&(p(e.href,t)||((e,t)=>e.some((e=>m(e,t))))(e.items,t))}function h(){var e;const t=(0,s.V)(),{pathname:n}=(0,a.TH)();if(!1===(null==(e=(0,i.gA)())?void 0:e.pluginData.breadcrumbs)||!t)return null;const r=[];return function e(t){for(const a of t)if("category"===a.type&&((0,c.Mg)(a.href,n)||e(a.items))||"link"===a.type&&(0,c.Mg)(a.href,n))return r.push(a),!0;return!1}(t.items),r.reverse()}function g(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,l.J)(e),a=(0,i.yW)(e);return(0,r.useMemo)((()=>u([t,n,a].filter(Boolean))),[t,n,a])}function v(e,t){const n=g(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error("Can't find any sidebar with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((e=>e.name)).join(", ")+'".\n Available sidebar ids are:\n - '+Object.keys(t).join("\n- "));return r[1]}),[e,n])}function b(e,t){const n=g(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error("DocNavbarItem: couldn't find any doc with id \""+e+'" in version'+(n.length>1?"s":"")+" "+n.map((e=>e.name)).join(", ")+'".\nAvailable doc ids are:\n- '+u(t.map((e=>e.id))).join("\n- "))}return r}),[e,n])}function y(e){let{route:t,versionMetadata:n}=e;const r=(0,a.TH)(),i=t.routes,l=i.find((e=>(0,a.LX)(r.pathname,e)));if(!l)return null;const s=l.sidebar,u=s?n.docsSidebars[s]:void 0;return{docElement:(0,o.H)(i),sidebarName:s,sidebarItems:u}}},5463:(e,t,n)=>{"use strict";n.d(t,{FG:()=>f,d:()=>c,VC:()=>p});var r=n(7294),a=n(6010),o=n(1514),i=n(9656);function l(){const e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=n(9524),u=n(9962);function c(e){let{title:t,description:n,keywords:a,image:i,children:l}=e;const c=function(e){const{siteConfig:t}=(0,u.Z)(),{title:n,titleDelimiter:r}=t;return null!=e&&e.trim().length?e.trim()+" "+r+" "+n:n}(t),{withBaseUrl:d}=(0,s.C)(),f=i?d(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,c),t&&r.createElement("meta",{property:"og:title",content:c}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),f&&r.createElement("meta",{property:"og:image",content:f}),f&&r.createElement("meta",{name:"twitter:image",content:f}),l)}const d=r.createContext(void 0);function f(e){let{className:t,children:n}=e;const i=r.useContext(d),l=(0,a.Z)(i,t);return r.createElement(d.Provider,{value:l},r.createElement(o.Z,null,r.createElement("html",{className:l})),n)}function p(e){let{children:t}=e;const n=l(),o="plugin-"+n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"");const i="plugin-id-"+n.plugin.id;return r.createElement(f,{className:(0,a.Z)(o,i)},t)}},3768:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>u,Ql:()=>s,i6:()=>l,zX:()=>o});var r=n(7294);const a=n(6136).Z.canUseDOM?r.useLayoutEffect:r.useEffect;function o(e){const t=(0,r.useRef)(e);return a((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,r.useRef)();return a((()=>{t.current=e})),t.current}class l extends Error{constructor(e,t){var n,r,a,o;super(),this.name="ReactContextError",this.message="Hook "+(null!=(n=null==(r=this.stack)||null==(a=r.split("\n")[1])||null==(o=a.match(/at (?:\w+\.)?(?\w+)/))?void 0:o.groups.name)?n:"")+" is called outside the <"+e+">. "+(null!=t?t:"")}}function s(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function u(e){return t=>{let{children:n}=t;return r.createElement(r.Fragment,null,e.reduceRight(((e,t)=>r.createElement(t,null,e)),n))}}},9003:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>l});var r=n(7294),a=n(997),o=n(9962);function i(e,t){const n=e=>{var t;return null==(t=!e||e.endsWith("/")?e:e+"/")?void 0:t.toLowerCase()};return n(e)===n(t)}function l(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>{var t;return null!=(t=e.routes)?t:[]})))}(n)}({routes:a.Z,baseUrl:e})),[e])}},2957:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>f,OC:()=>s,RF:()=>d});var r=n(7294),a=n(6136),o=n(1048),i=n(3768);const l=r.createContext(void 0);function s(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return r.createElement(l.Provider,{value:n},t)}function u(){const e=(0,r.useContext)(l);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const c=()=>a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=u(),a=(0,r.useRef)(c()),o=(0,i.zX)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=c();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function f(){const e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&at&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>null==e.current?void 0:e.current()}}},246:(e,t,n)=>{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(9962);const r="default";function a(e,t){return"docs-"+e+"-"+t}},2560:(e,t,n)=>{"use strict";n.d(t,{W:()=>l,_:()=>s});const r="localStorage";function a(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,o||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),o=!0),null}var t}let o=!1;const i={get:()=>null,set:()=>{},del:()=>{}};function l(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error('Illegal storage API usage for storage key "'+e+'".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.')}return{get:t,set:t,del:t}}(e);const n=a(null==t?void 0:t.persistence);return null===n?i:{get:()=>{try{return n.getItem(e)}catch(t){return console.error("Docusaurus storage error, can't get key="+e,t),null}},set:t=>{try{n.setItem(e,t)}catch(r){console.error("Docusaurus storage error, can't set "+e+"="+t,r)}},del:()=>{try{n.removeItem(e)}catch(t){console.error("Docusaurus storage error, can't delete key="+e,t)}}}}function s(e){void 0===e&&(e=r);const t=a(e);if(!t)return[];const n=[];for(let r=0;r{"use strict";n.d(t,{l:()=>o});var r=n(9962),a=n(6775);function o(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:n,currentLocale:o}}=(0,r.Z)(),{pathname:i}=(0,a.TH)(),l=o===n?e:e.replace("/"+o+"/","/"),s=i.replace(e,"");return{createUrl:function(e){let{locale:r,fullyQualified:a}=e;return""+(a?t:"")+function(e){return e===n?""+l:""+l+e+"/"}(r)+s}}}},3266:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(7294),a=n(6775),o=n(3768);function i(e){const t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},107:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(9962);function a(){return(0,r.Z)().siteConfig.themeConfig}},4136:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[a]=e.split(/[#?]/),o="/"===a||a===r?a:(i=a,n?function(e){return e.endsWith("/")?e:e+"/"}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(a,o)}},3905:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="post-content";var a=n(4136);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}})},6010:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;ta});const a=function(){for(var e,t,n=0,a="";n{"use strict";n.d(t,{lX:()=>w,q_:()=>C,ob:()=>p,PP:()=>A,Ep:()=>f});var r=n(7462);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r=0;f--){var p=i[f];"."===p?o(i,f):".."===p?(o(i,f),d++):d&&(o(i,f),d--)}if(!u)for(;d--;d)i.unshift("..");!u||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var l=n(2177);function s(e){return"/"===e.charAt(0)?e:"/"+e}function u(e){return"/"===e.charAt(0)?e.substr(1):e}function c(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function f(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function p(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(l){throw l instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):l}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=p(e,t,h(),w.location);c.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t{"use strict";var r=n(9864),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var u=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=p(n);a&&a!==m&&e(t,a,r)}var i=c(n);d&&(i=i.concat(d(n)));for(var l=s(t),h=s(n),g=0;g{"use strict";e.exports=function(e,t,n,r,a,o,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[n,r,a,o,i,l],c=0;(s=new Error(t.replace(/%s/g,(function(){return u[c++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},5826:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},984:(e,t,n)=>{"use strict";n.r(t)},6930:(e,t,n)=>{"use strict";n.r(t)},4865:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};function a(e,t,n){return en?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),u=o.querySelector(r.barSelector),c=r.speed,d=r.easing;return o.offsetWidth,l((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(u,i(e,c,d)),1===e?(s(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){s(o,{transition:"all "+c+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),c)}),c)):setTimeout(t,c)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");c(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),l=e?"-100":o(n.status||0),u=document.querySelector(r.parent);return s(i,{transition:"all 0 linear",transform:"translate3d("+l+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&p(a),u!=document.body&&c(u,"nprogress-custom-parent"),u.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&p(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var l=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function u(e,t){return("string"==typeof e?e:f(e)).indexOf(" "+t+" ")>=0}function c(e,t){var n=f(e),r=n+t;u(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=f(e);u(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function f(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function p(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},7418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,o){for(var i,l,s=a(e),u=1;u{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);S+=E.value.length,E=E.next){var x=E.value;if(t.length>e.length)return;if(!(x instanceof a)){var _,C=1;if(b){if(!(_=o(k,S,e,v))||_.index>=e.length)break;var T=_.index,A=_.index+_[0].length,L=S;for(L+=E.value.length;T>=L;)L+=(E=E.next).value.length;if(S=L-=E.value.length,E.value instanceof a)continue;for(var R=E;R!==t.tail&&(Ld.reach&&(d.reach=D);var I=E.prev;if(N&&(I=s(t,I,N),S+=N.length),u(t,I,C),E=s(t,I,new a(f,g?r.tokenize(P,g):P,y,P)),O&&s(t,E,O),C>1){var M={cause:f+","+m,reach:D};i(e,t,n,E.prev,S,M),d&&M.reach>d.reach&&(d.reach=M.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function u(e,t,n){for(var r=t.next,a=0;a"+o.content+""},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n0)){var l=f(/^\{$/,/^\}$/);if(-1===l)continue;for(var s=n;s=0&&p(u,"variable-input")}}}}function c(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function l(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function u(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,s(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function c(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,c={},d=s(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=l(i++,r)););return c[n]=a,n})).join(""),n,r),f=Object.keys(c);return i=0,function e(t){for(var n=0;n=f.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=f[i],o="string"==typeof r?r:r.content,l=o.indexOf(a);if(-1!==l){++i;var s=o.substring(0,l),d=u(c[a]),p=o.substring(l+a.length),m=[];if(s&&m.push(s),m.push(d),p){var h=[p];e(h),m.push.apply(m,h)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var g=r.content;Array.isArray(g)?e(g):e([g])}}}(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function f(e){return"string"==typeof e?e:Array.isArray(e)?e.map(f).join(""):f(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var r=0,a=n.length;r]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r*\.{3}(?:[^{}]|)*\})/.source;function o(e,t){return e=e.replace(//g,(function(){return n})).replace(//g,(function(){return r})).replace(//g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},l=function(t){for(var n=[],r=0;r0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var s=i(a);r0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(s=i(t[r-1])+s,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",s,null,s)}a.content&&"string"!=typeof a.content&&l(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||l(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,l=i.length;-1!==n.code.indexOf(a=t(r,l));)++l;return i[l]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(l){for(var s=0;s=o.length);s++){var u=l[s];if("string"==typeof u||u.content&&"string"==typeof u.content){var c=o[a],d=n.tokenStack[c],f="string"==typeof u?u:u.content,p=t(r,c),m=f.indexOf(p);if(m>-1){++a;var h=f.substring(0,m),g=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),v=f.substring(m+p.length),b=[];h&&b.push.apply(b,i([h])),b.push(g),v&&b.push.apply(b,i([v])),"string"==typeof u?l.splice.apply(l,[s,1].concat(b)):u.content=b}}else u.content&&i(u.content)}return l}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},9901:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:"shell",aliasTitles:{shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (Scss)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to WebPlatform.org documentation. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},2885:(e,t,n)=>{const r=n(9901),a=n(9642),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(6500).resolve(t)],delete Prism.languages[e],n(6500)(t),o.add(e)}))}i.silent=!1,e.exports=i},6726:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6726},6500:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6500},9642:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n "));var l={},s=e[r];if(s){function u(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in l))for(var i in a(t,o),l[t]=!0,n[t])l[i]=!0}t(s.require,u),t(s.optional,u),t(s.modify,u)}n[r]=l,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,l){var s=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),u=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(s);i=i.map(u),l=(l||[]).map(u);var c=n(i),d=n(l);i.forEach((function e(n){var r=s[n];t(r&&r.require,(function(t){t in d||(c[t]=!0,e(t))}))}));for(var f,p=r(s),m=c;a(m);){for(var h in f={},m){var g=s[h];t(g&&g.modify,(function(e){e in d&&(f[e]=!0)}))}for(var v in d)if(!(v in c))for(var b in p(v))if(b in c){f[v]=!0;break}for(var y in m=f)c[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,l={},s={};function u(e){if(e in l)return l[e];s[e]=!0;var a,c=[];for(var d in t(e))d in n&&c.push(d);if(0===c.length)a=r(e);else{var f=i(c.map((function(e){var t=u(e);return delete s[e],t})));o?a=o(f,(function(){return r(e)})):r(e)}return l[e]=a}for(var c in n)u(c);var d=[];for(var f in s)d.push(l[f]);return i(d)}(p,c,t,n)}};return w}}();e.exports=t},2703:(e,t,n)=>{"use strict";var r=n(414);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5697:(e,t,n)=>{e.exports=n(2703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},4448:(e,t,n)=>{"use strict";var r=n(7294),a=n(7418),o=n(3840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n