if icon indicating copy to clipboard operation
if copied to clipboard

Build `if-env`

Open jmcook1186 opened this issue 1 year ago • 10 comments

Sub of: #629 -> #650

What: A CLI tool that helps you setup your local environment to run a manifest file.

Why: As a user I want to be able to quickly create an environment to run my manifests, taking all my plugins into account.

Context: We can provide a command line tool that will inspect the package.jsons of the if and all the plugins listed in a manifest and create a unified package.json that can be used to define the environment in which the manifest can run. This will make it easy for users to run manifests and will also be useful scaffolding for our sandboxes, playgrounds and scenario testing. This ticket covers the design and specification but not the actual building of the tool.

DoD

  • [ ] Feature merged into if main branch
  • [ ] Feature documented on if.greensoftware.foundation

SoW

  • [ ] TODO

Pre-requisites

  • #591

Acceptance Criteria

  • [ ] Running if-env to setup a blank starter environment

GIVEN the user is in a folder WHEN they run if-env with no parameters THEN it generates a package.json file in the local directory which contains everything they need to start writing a manifest file with. AND it generates a template manifest file called manifest.yml with a selection of fields prepopulated to help with development.

Here is a proposal for a template manifest.yml to create if if-env is executed from a folder with no local yaml files:

name: template manifest # rename me!
description: auto-generated template # update description!
tags: # add any tags that will help you to track your manifests
initialize:
  outputs:
    - yaml # you can add - csv to export to csv
  plugins: # add more plugins for your use-case
    memory-energy-from-memory-util: # you can name this any way you like!
      method: Coefficient # the name of the function exported from the plugin
      path: '@grnsft/if-plugins' # the import path
      global-config: # anmy config required by the plugin
        input-parameter: ['memory/utilization']
        coefficient: 0.0001 #kwH/GB
        output-parameter: 'memory/energy'
tree:
  children: # add a chile for each distinct component you want to measure
    child:
      pipeline: # the pipeline is an ordered list of plugins you want to execute
        - memory-energy-from-memory-util # must match the name in initialize!
      config: # any plugin specific, node-level config
      inputs:
        - timestamp: 2023-12-12T00:00:00.000Z # ISO 8061 string
          duration: 3600 # units of seconds
          memory/utilization: 10 
  • [ ] Running if-env to setup an environment to run a given manifest file

GIVEN the user has this manifest file

name: basic-demo
description:
tags:
initialize:
  plugins:
    teads-curve: 
      path: '@grnsft/if-unofficial-plugins'
      method: TeadsCurve
      global-config:
        interpolation: spline
execution:
  command: ie --manifest examples/basic.yml
  environment:
    os: ubuntu
    os-version: 22.04.6
    node version: v21.4.0
    date-time: 2023-12-12T00:00:00.000Z (UTC)
    dependencies: 
      - '@babel/[email protected]'
      - '@babel/[email protected]'
      - '@commitlint/[email protected]'
      - '@commitlint/[email protected]'
      - '@grnsft/[email protected]'
      - '@grnsft/[email protected] (git+ssh://[email protected]/Green-Software-Foundation/if-plugins.git#8056a9593e3d41786d32dddb3e080bba75a8aa54)
      - '@jest/[email protected]'
      - '@types/[email protected]'
      - '@types/[email protected]'
      - '@types/[email protected]'
      - '@types/[email protected]'
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
tree:
  children:
    child-0:
      defaults:
        cpu/thermal-design-power: 100
      pipeline:
        - teads-curve
      inputs:
        - timestamp: 2023-07-06T00:00
          duration: 1
          cpu/utilization: 20

AND they are in a folder which doesn't contain a package.json WHEN they run if-env /path/to/<manifest>.yml THEN it generates a package.json file in the local directory which contains all the script and dependencies required to run the manifest file.

  • [ ] Running if-env to create and install an environment to run a given manifest file

GIVEN the user has this manifest file

name: basic-demo
description:
tags:
initialize:
  plugins:
    teads-curve: 
      path: '@grnsft/if-unofficial-plugins'
      method: TeadsCurve
      global-config:
        interpolation: spline
execution:
  command: ie --manifest examples/basic.yml
  environment:
    os: ubuntu
    os-version: 22.04.6
    node version: v21.4.0
    date-time: 2023-12-12T00:00:00.000Z (UTC)
    dependencies: 
      - '@babel/[email protected]'
      - '@babel/[email protected]'
      - '@commitlint/[email protected]'
      - '@commitlint/[email protected]'
      - '@grnsft/[email protected]'
      - '@grnsft/[email protected] (git+ssh://[email protected]/Green-Software-Foundation/if-plugins.git#8056a9593e3d41786d32dddb3e080bba75a8aa54)
      - '@jest/[email protected]'
      - '@types/[email protected]'
      - '@types/[email protected]'
      - '@types/[email protected]'
      - '@types/[email protected]'
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
      - [email protected]
tree:
  children:
    child-0:
      defaults:
        cpu/thermal-design-power: 100
      pipeline:
        - teads-curve
      inputs:
        - timestamp: 2023-07-06T00:00
          duration: 1
          cpu/utilization: 20

AND they are in a folder which doesn't contain a package.json WHEN they run if-env /path/to/manifest.yml --install THEN it generates a package.json file in the local directory which contains all the script and dependencies required to run the manifest file AND it runs npm install to install the dependencies.

jmcook1186 avatar Apr 11 '24 08:04 jmcook1186

@jmcook1186 and @narekhovhannisyan

Acceptance Criteria

Given the user has this manifest file: TODO - File with mix of plugibs installed from npm, GitHub, local npm link, and builtins When runs if-env /path/to/manifest.yml Then it generates a file in the local directory called package.json containing this info: TODO

Questions

To flesh out the above acceptance criteria I think we need to answer some questions:

How do we handle when a package that is being used is not in NPM? E.g. the user is using a non published plugin that's just on GitHub.

@narekhovhannisyan what happens when we install a package from GitHub? E.g. npm i https://github.com/user_name/node_project_name

  • What does the package.json contain?
  • How do we configure the manifest file to use this package? I'm assuming just by looking at the manifest file we won't be able to understand that you need to install it from GH.

How do we get the version numbers?

A) We have a ticket to add version numbers to the manifest file for each plugin, how would we get those? The output manifest file should update the initialise.plugins section to add a version parameter to each plugin which contains the current version of that plugin this manifest was run using B) If the manifest file does not have version numbers should we just install the latest?

jawache avatar Apr 17 '24 20:04 jawache

Maybe one way is to log the latest commit hash or just the utc date/time if there is no version number available. At least those information allow you to recover a specific state of a repository that matches what you installed.

jmcook1186 avatar Apr 18 '24 10:04 jmcook1186

@jawache The Github project is registered in package.json's dependencies.

  1. package.json contains something like this when a Github dependency is installed:
{
  "dependencies":  {
  "yourpackage": "https://github.com/yourpackage" // repository name is registered as a package
  }
}
  1. Manifest can include either Github repository link or package name (both can do). But as you said we don't have guarantee that user will put it rather than repo name.

narekhovhannisyan avatar Apr 20 '24 09:04 narekhovhannisyan

Thanks @narekhovhannisyan, I think we have enough now to refine this properly.

When you install from GitHub you call this command.

npm install https://github.com/Green-Software-Foundation/if-plugins

This adds this record to package.json

{
  "name": "copy-plugin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@grnsft/if-plugins": "github:Green-Software-Foundation/if-plugins"
  }
}

And when you run npm list you get this result

[email protected] /Users/jawache/Development/gsf/copy-plugin
└── @grnsft/[email protected] (git+ssh://[email protected]/Green-Software-Foundation/if-plugins.git#8056a9593e3d41786d32dddb3e080bba75a8aa54)

But when you configure the plugin in the manifest file you lose the link to github and version info.

Since we only contain information like so

    operational-carbon:
      path: '@grnsft/if-plugins'
      method: Multiply
      global-config:
        input-parameters: ['cpu/energy', 'grid/carbon-intensity']
        output-parameter: 'carbon'     

Similarly when we install from npm directly like so

npm install @grnsft/if-unofficial-plugins

The package.json looks like this:

{
  "name": "copy-plugin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@grnsft/if-plugins": "github:Green-Software-Foundation/if-plugins",
    "@grnsft/if-unofficial-plugins": "^0.3.1"
  }
}

And npm list returns

➜  copy-plugin npm list                                 
[email protected] /Users/jawache/Development/gsf/copy-plugin
├── @grnsft/[email protected] (git+ssh://[email protected]/Green-Software-Foundation/if-plugins.git#8056a9593e3d41786d32dddb3e080bba75a8aa54)
└── @grnsft/[email protected]

So if-env needs to know the exact package names, versions and sources so it can compute the same packge.json as the manifest file was originally run in.

I'll update #591 with the relevant information but to summarize when computing a manifest file we should include the output from npm list in the execution node so that if-env has enough information to be able to generate a package.json that can run this manifest file.

jawache avatar Apr 21 '24 14:04 jawache

@narekhovhannisyan are you happy with the AC and could implement?

zanete avatar May 07 '24 09:05 zanete

@jmcook1186 and @narekhovhannisyan the only way this works in all cases is if the manifest file is an output manifest file and therefore has information regarding the execution and the dependancies. I've adjusted the AC above so the manifest files has execution blocks and added in a more complex use case with a plugin that's been installed from GitHub instead of NPM.

If it doesn't have an execution node with dependancies, or the plugin being configured isn't in the dependency list, then we perhaps can just assume the name of the package is the same as the path param and assume it's the latest version? We should warn at least if that's the case. If we agree on that approach we need another AC @jmcook1186.

In terms of implementation I'm leaning towards if-env being selective and just output the configured plugins in the package.json. So in the above example rather than ouput all the same in package.json as exists in the dependancies node, just @grnsft/[email protected] since teads-curve is the only one we are using in the manifest file. What do you think? There are also pros to just listing everything in dependancies in the output package.json.

jawache avatar May 07 '24 14:05 jawache

Makes sense @jawache - I had in mind that the main use case for this is re-execution and verification, so normally you would have an output file for this, not a raw unexecuted manifest.

So what I understand about this is:

  • if-env has to check that each of the plugins in the initialize block has an associated entry in the package.json - if it doesn't it tries to grab it using the path, either grabbing a numbered version or falling back to latest and if that fails too (the path is just wrong) then it just errors out (invalid path for plugin x or similar).

  • if if-env has to execute any of the logic described in bullet point 1, then it warns the user as its possible the versions are different to that originally used to execute the given file.

I personally have a weak preference towards if-env being comprehensive in the packages it reports, because you never know where an error might originate from and having that information available could be useful in ways that are hard to anticipate.

jmcook1186 avatar May 07 '24 15:05 jmcook1186

@jmcook1186 I see your point and agree, speaking to the intention of this script, it is to mirror the environment the manifest file was run in so perhaps that's the right approach, just output what's in dependencies in manifest to package.json. if there is something screwy in there causing issues we'd want to replicate it.

Perhaps in future updates we can explore other flags like --update to update all the packages to latest version, or --essential-only to mirror what I proposed with just the ones in the manifest file.

jawache avatar May 07 '24 20:05 jawache

I haven't kept the list of estimations, so maybe L or XL

narekhovhannisyan avatar May 09 '24 14:05 narekhovhannisyan

  • XS - 1 hr
  • S - 1-3 hrs
  • M - 4-8 hrs
  • L - 1-2 days
  • XL - 3-5 days
  • XXL - 5+ days

zanete avatar May 09 '24 15:05 zanete

Expected to start working on this on Wed afternoon

zanete avatar Jun 04 '24 15:06 zanete

@jmcook1186 Should I add a command like -m in this if-env /path/to/<manifest>.yml to be consistent with the other command line tools?

manushak avatar Jun 06 '24 10:06 manushak

yes please, keep the shortened commands consistent with the other if tooling

jmcook1186 avatar Jun 06 '24 10:06 jmcook1186

@jmcook1186, what is the difference between if-env /path/to/<manifest>.yml and if-env /path/to/<manifest>.yml --install? If there isn't a package.json in the first case (if-env /path/to/.yml), it should give an error. It wouldn't be good to have only the if-env /path/to/<manifest>.yml command; if there is a package.json, should it only install dependencies?

manushak avatar Jun 06 '24 11:06 manushak

if there is no package.json then if-env should create one with the IF core dependencies in the local repository, but not install. If the --install command is included but there is no package.json available then if-env should create it then install it.

jmcook1186 avatar Jun 06 '24 11:06 jmcook1186

@jmcook1186 The manifest.yml template file is not an executed manifest file. However, we require an executed manifest file from the user. Should we provide an executed manifest template?

manushak avatar Jun 06 '24 13:06 manushak

No, the template will only be used when if-env finds no local yaml files.

The idea is that if-env can recreate a local environment from a given manifest, but if no manifest or package.json is provided then if-env creates templates for you to bootstrap you into your IF project development. Like using create-react-app or similar.

so:

  • IF if-env finds an output file with an execution node in the local folder then it uses the packages in the execution node to create a local package.json
  • IF if-env finds a manifest file in the local folder then it adds the template package.json with the IF dependencies
  • IF if-env finds no yaml files in the local folder then it adds the template package.json with the IF dependencies and adds the template manifest

jmcook1186 avatar Jun 06 '24 13:06 jmcook1186

will finalise today/tomorrow morning

zanete avatar Jun 10 '24 12:06 zanete

@manushak and @jmcook1186 to have a call in order to try and replicate the issue

zanete avatar Jun 18 '24 12:06 zanete

Looks like it would be a great idea for @MariamKhalatova to retest this 🙏

zanete avatar Jun 19 '24 15:06 zanete

@jmcook1186, could you please update the ticket according to our discussion so that @MariamKhalatova can refer to it while testing?

manushak avatar Jun 19 '24 15:06 manushak

@narekhovhannisyan please review the latest push :)

zanete avatar Jun 20 '24 15:06 zanete