Refactor step names.

Previously in recipe engine steps were identified by a ragtag group of
three attributes:
  * base_name: a '.' concatenated series of namespaces
  * name: the last bit of base_name
  * nest_level: A weird integer which sort-of counted how many '.'s were in
    base_name. ish.

Additionally, the context of 'how nested' we were was passed around
internally as a single 'name_prefix' string, plus the nest_level. It
was possible to modify name_prefix without also using nest_level, but
you could add '.' to name_prefix this way and it was a big mess.

Because build.proto represents steps as '|' separated series of strings
(and reserves the '|' character for this purpose), we need to push this
abstraction down to the StreamEngine level (i.e. what converts recipes'
internal state to @@@annotation@@@ or build.proto).

This CL:

Replaces all the previous name state with `namespace`; A tuple of string bits
(none of which contain "|"). The last hunk of the namespace is typically '', but
can be added to with name_prefix.

Replaces all internal representations of a step's `name` (and base_name and
nest_level) with `name_tokens`: as a tuple of string bits (none of which contain
"|").

For backwards compatibility with existing user code (whose tests explicitly
include strings joined with ".") this CL keeps the test-case step name
representation as `'.'.join(s)`. We'll change these in a followup CL so that:
  * Users identify step names in their tests by their tuple representation.
  * The test expectation format records name as a list of string bits.

This CL also fixes three long-standing bugs:
  * api.step.nest and api.context are now fully consistent with each other.
    api.step.nest is still preferred (because then you get a manipulable step)
    but at least the state won't be hosed if you use api.context.
  * Step name deduplication now knows about namespaces. Now if you ran:
      Nest
        Step 1
        Cool
      Nest
        Step 2
        Cool

    It will correctly result in:
      Nest
      Nest|Step 1
      Nest|Cool
      Nest (2)
      Nest (2)|Step 2
      Nest (2)|Cool

    Instead of the illegal:
      Nest
      Nest|Step 1
      Nest|Cool
      Nest
      Nest|Step 2
      Nest|Cool (2)
  * Previously, because 'nest' and 'context' didn't agree with each other
    it was possible to tweak 'name_prefix' independently of 'nest_level'
    with unpredictable results. Now 'name_prefix' and 'namespace' have a
    fixed relationship: name_prefix applies a string prefix to any steps
    and new namespaces created. When creating a new namespace, name_prefix
    resets to ''. Internally they are stored together as `context._namespace`
    which has the form of `(some, namespace, strings, name_prefix)`, with
    this initially set to `('',)`.

R=martiniss@chromium.org

Recipe-Nontrivial-Roll: infra
Recipe-Nontrivial-Roll: build
Recipe-Nontrivial-Roll: build_limited_scripts_slave
Recipe-Nontrivial-Roll: release_scripts
Bug: 909848
Change-Id: I37c57c04a324206f16f69d3660f0784fcb3e3be3
Reviewed-on: https://chromium-review.googlesource.com/c/infra/luci/recipes-py/+/1512355
Commit-Queue: Robbie Iannucci <iannucci@chromium.org>
Reviewed-by: Stephen Martinis <martiniss@chromium.org>
14 files changed
tree: aa7299fb1d44623562a5b568af362bf42c091193
  1. doc/
  2. infra/
  3. misc/
  4. recipe_engine/
  5. recipe_modules/
  6. recipe_proto/
  7. recipes/
  8. unittests/
  9. .gitattributes
  10. .gitignore
  11. .vpython
  12. AUTHORS
  13. codereview.settings
  14. CONTRIBUTORS
  15. LICENSE
  16. OWNERS
  17. PRESUBMIT.py
  18. README.md
  19. README.recipes.md
  20. recipes.py
README.md

Recipes

Recipes are a domain-specific language (embedded in python) for specifying sequences of subprocess calls in a cross-platform and testable way.

Files

  • README.md

    This file!

  • doc/

    Documentation for the recipe engine (including this file!). See the design doc for more detailed design information about the recipe engine.

  • infra/

    Chrome infra config files.

  • recipes.py

    The main entry point to the recipe engine. It has many subcommands and flags; run recipes.py -h to see them. Include this in your repository to start using recipes.

  • recipes/

    Recipes in the recipe engine. These are either example recipes, or recipes which are used to test the engine (see run_test.py to see these run)

  • recipe_modules/

    Built in recipe modules. These are very useful when writing recipes; take a look in there, and look at each of their examples subfolders to get an idea how to use them in a recipe.

  • recipe_engine/

    The core functionality of the recipe engine. Noteworthy files include:

    • main.py -- The main entrypoint for the recipe engine.
    • recipes_cfg.proto -- The protobuf file which defines the format of a recipes.cfg file.
    • third_party/ -- third_party code which is vendored into the recipe engine.
    • recipe_api.py -- The api exposed to a recipe module.
    • unittests -- Unittests for the engine.

    There are also several files which correspond to a subcommand of recipes.py; run, and autoroll are some examples.

  • unittests/

    Somewhat poorly named, these are higher level integration tests.

Contributing

  • Sign the Google CLA.
  • Make sure your user.email and user.name are configured in git config.

Run the following to setup the code review tool and create your first review:

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $HOME/src/depot_tools
export PATH="$PATH:$HOME/src/depot_tools"
git checkout -b work origin/master

# hack hack

git commit -a -m "This is awesome"
# This will ask for your Google Account credentials.
git cl upload -s -r joe@example.com
# Wait for approval over email.
# Click "Submit to CQ" button or ask reviewer to do it for you.
# Wait for the change to be tested and landed automatically.

Use git cl help and git cl help <cmd> for more details.