Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some layers inconsistencies #589

Merged
merged 31 commits into from
Oct 22, 2024
Merged

Conversation

jkloetzke
Copy link
Member

@jkloetzke jkloetzke commented Oct 4, 2024

This fixes a couple of subtle corner cases that so far got unnoticed:

  • We still parsed the system- and user-global default.yaml
    configuration files. The data from these files was not used except
    the URM SCM mirrors. Now everything depends just on the layer
    configuration files.
  • The layersScmOverrides and layersWhitelist keys were only
    appended when traversing the layers. This causes settings of
    unrelated layers to affect each other. Now the layer settings are
    handled strictly hierarchical.
  • It was possible to switch to the "import" SCM via layersScmOverrides.
    This is prevented now by always using the layers SCM schema.
  • There were no default environment white list entries. This was
    inconsistent to the regular white list handling.

Additionally, it improves the following things:

  • Use relative paths for layers. This shortens all log messages.
  • Show new/attic/overridden flags on "bob layers status"

Copy link

codecov bot commented Oct 4, 2024

Codecov Report

Attention: Patch coverage is 93.75000% with 12 lines in your changes missing coverage. Please review.

Project coverage is 88.89%. Comparing base (e683f9b) to head (cbd8d23).
Report is 32 commits behind head on master.

Files with missing lines Patch % Lines
pym/bob/layers.py 91.96% 9 Missing ⚠️
pym/bob/input.py 95.45% 2 Missing ⚠️
pym/bob/utils.py 87.50% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #589      +/-   ##
==========================================
+ Coverage   88.88%   88.89%   +0.01%     
==========================================
  Files          48       48              
  Lines       15270    15339      +69     
==========================================
+ Hits        13572    13636      +64     
- Misses       1698     1703       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jkloetzke
Copy link
Member Author

@rhubert It would be good if you could test this on your side...

@rhubert
Copy link
Contributor

rhubert commented Oct 5, 2024 via email

@jkloetzke jkloetzke added this to the 0.25 milestone Oct 13, 2024
The layers do not use the regular user configurations files at all. It
thus makes no sense to accept them either.
No need to parse after an update because the result won't be used
anyway.
Move the environment whitelist default set to the utils module so that
it can be reused.
No need to recalculate it all the time.
We always check for the existence of a yaml file before where necessary.
Makes it possible to use it in a "with" expression.
Make it a class method so that it can be used independently.
Make it a class method so that it can be used independently.
The handling of layers just depends on their config.yaml. Actually, only
a subset of the project configuration is needed. Instead of using the
regular RecipeSet parsing function with an early exit, do all
config.yaml processing in the layers module itself.

This fixes a couple of subtle corner cases that so far got unnoticed:

 * We still parsed the system- and user-global default.yaml
   configuration files. The data from these files was not used except
   the URM SCM mirrors. Now everything depends just on the layer
   configuration files.
 * The layersScmOverrides and layersWhitelist keys were only
   appended when traversing the layers. This causes settings of
   unrelated layers to affect each other. Now the layer settings are
   handled strictly hierarchical.
 * It was possible to switch to the "import" SCM via layersScmOverrides.
   This is prevented now by always using the layers SCM schema.
 * There were no default environment white list entries. This was
   inconsistent to the regular white list handling.
Now that the layers logic handles all settings itself, the noLayers
early parsing exit can be removed.
Layer configuration files on the command line should have the highest
precedence. If multiple files are passed, later ones should have a
higher precedence.

Settings in layer config.yaml files affect only their sub-layers. The
SCM overrides of a layer have a higher precedence than the SCM overrides
of a layer beneath it. This ensures that higher layers (or the command
line configuration files) can override and SCMs beneath it.
The event loop is needed when doing something. It is not part of the
layers state.
No need to use long, absolute paths. The current working directory will
not change so there is no need to prepend it.

This change is incompatible to existing workspace states! The workspace
state saved the absolute path previously which won't match now.
Always parse the action first. The available command line options
actually depend on the action.
Get feature parity with the "bob status" command in this respect.
@jkloetzke jkloetzke force-pushed the polish-layers branch 2 times, most recently from 6c697c4 to 31f84ee Compare October 20, 2024 19:24
@jkloetzke
Copy link
Member Author

Ping @rhubert. I have to admit this got quite large. 🙈 Anyway, could you give it a try? I would like to make the 0.25 release once this has been merged...

@rhubert
Copy link
Contributor

rhubert commented Oct 21, 2024

Sorry for the long delay. I had some days off. I'm not sure if it makes sense to review your changes in detail but I made a few tests this morning.

First thing I noticed is that bob layers status shows the layers twice. For Attic with absolute path, for CN with a relative path? There is also no change in the config.yaml, so I'm not sure why the layers will be moved to attic and the collision also doesn't make sense to me. I guess this is because of a internal change from bob-master to polish-layers?

   STATUS A    /home/ralf/foo/layers/ada
   STATUS A    /home/ralf/foo/layers/basement
   STATUS AS   /home/ralf/foo/layers/basement-gnu-linux
   STATUS A    /home/ralf/foo/layers/codelabs
   STATUS CN   layers/ada
   STATUS CN   layers/basement
   STATUS CN   layers/basement-gnu-linux
   STATUS CN   layers/codelabs

Anyway, I removed the old layers directory at this point:

$ bob layers status 
   STATUS N    layers/ada
   STATUS N    layers/basement
   STATUS N    layers/basement-gnu-linux
   STATUS N    layers/codelabs

looks as expected now but

$ bob layers update 
** CHECKOUT: Layer 'basement' .. ok
** CHECKOUT: Layer 'basement-gnu-linux' .. ok
** CHECKOUT: Layer 'ada' .. ok
** CHECKOUT: Layer 'codelabs' .. ok
** ATTIC: Layer /home/ralf/foo/layers/basement (move to ../../layers.attic/2024-10-21T09:23:23.489469_basement)
** ATTIC: Layer /home/ralf/foo/layers/basement-gnu-linux (move to ../../layers.attic/2024-10-21T09:23:23.492539_basement-gnu-linux)
** ATTIC: Layer /home/ralf/foo/layers/ada (move to ../../layers.attic/2024-10-21T09:23:23.495666_ada)
** ATTIC: Layer /home/ralf/foo/layers/codelabs (move to ../../layers.attic/2024-10-21T09:23:23.498789_codelabs)

immediately move the layers to attic after checkout?

$ ls layers/
$ ls layers.attic/
2024-10-21T09:23:23.489469_basement            2024-10-21T09:23:23.495666_ada
2024-10-21T09:23:23.492539_basement-gnu-linux  2024-10-21T09:23:23.498789_codelabs

bob ls is now unhappy, but bob dev works:

$ bob dev demo*
** CHECKOUT: Layer 'basement' .. ok
** CHECKOUT: Layer 'basement-gnu-linux' .. ok
** CHECKOUT: Layer 'ada' .. ok
** CHECKOUT: Layer 'codelabs' .. ok

<build successful>

$ bob layers status --show-clean
   STATUS      layers/ada
   STATUS      layers/basement
   STATUS      layers/basement-gnu-linux
   STATUS      layers/codelabs

I can reproduce the move-to-attic-after-checkout behavior by deleting the layers dir and switch back to bob-master:

$ rm layers -rf
$ bob --version
Bob version 0.24.1.dev139+ge683f9b
$ bob layers update
** CHECKOUT: Layer 'basement' .. ok
** CHECKOUT: Layer 'basement-gnu-linux' .. ok
** CHECKOUT: Layer 'ada' .. ok
** CHECKOUT: Layer 'codelabs' .. ok
** ATTIC: Layer layers/basement (move to ../../layers.attic/2024-10-21T09:35:45.326939_basement)
** ATTIC: Layer layers/basement-gnu-linux (move to ../../layers.attic/2024-10-21T09:35:45.330674_basement-gnu-linux)
** ATTIC: Layer layers/ada (move to ../../layers.attic/2024-10-21T09:35:45.334062_ada)
** ATTIC: Layer layers/codelabs (move to ../../layers.attic/2024-10-21T09:35:45.337136_codelabs)
$ rm layers -rf
$ bob layers update
** CHECKOUT: Layer 'basement' .. ok
** CHECKOUT: Layer 'basement-gnu-linux' .. ok
** CHECKOUT: Layer 'ada' .. ok
** CHECKOUT: Layer 'codelabs' .. ok

# switch back to `jkloetzke:polish-layers`
$ rm -rf layers
$ bob --version
Bob version 0.24.1.dev167+g31f84ee
$ bob layers status 
   STATUS N    layers/ada
   STATUS N    layers/basement
   STATUS N    layers/basement-gnu-linux
   STATUS N    layers/codelabs
(bob_venv) ralf@bob-server:~/muen$ bob layers update 
** CHECKOUT: Layer 'basement' .. ok
** CHECKOUT: Layer 'basement-gnu-linux' .. ok
** CHECKOUT: Layer 'ada' .. ok
** CHECKOUT: Layer 'codelabs' .. ok
** ATTIC: Layer /home/ralf/foo/layers/basement (move to ../../layers.attic/2024-10-21T09:39:04.471627_basement)
** ATTIC: Layer /home/ralf/foo/layers/basement-gnu-linux (move to ../../layers.attic/2024-10-21T09:39:04.475883_basement-gnu-linux)
** ATTIC: Layer /home/ralf/foo/layers/ada (move to ../../layers.attic/2024-10-21T09:39:04.479796_ada)
** ATTIC: Layer /home/ralf/foo/layers/codelabs (move to ../../layers.attic/2024-10-21T09:39:04.483009_codelabs)

Also note the absolute attic-layer-path in your version compared to master. Not sure if this is intended...

@rhubert
Copy link
Contributor

rhubert commented Oct 21, 2024

After looking into your commits I think the findings are expected. But bash-completion need some polishing as well:

diff --git a/contrib/bash-completion/bob b/contrib/bash-completion/bob
index f48a4f0..3565ca9 100644
--- a/contrib/bash-completion/bob
+++ b/contrib/bash-completion/bob
@@ -388,12 +388,12 @@ __bob_status()
 
 __bob_layers_status()
 {
-   __bob_complete_path "-c -D -v -lc --attic --no-attic"
+   __bob_complete_words "-D -v -lc --show-clean --show-overrides"
 }
 
 __bob_layers_update()
 {
-   __bob_complete_path "-c -D -v -lc --attic --no-attic"
+   __bob_complete_words "-D -v -lc --attic --no-attic"
 }
 
 __bob_layers()

(Maybe even more as -c / -lc is not completing to .yaml files for all the commands using bob_complete_words instead of bob_complete_path... )

@rhubert
Copy link
Contributor

rhubert commented Oct 21, 2024

layersWhitelist is also not working (same on master 😭 )

Fix:

diff --git a/pym/bob/layers.py b/pym/bob/layers.py
index 26ab5fb..bc5cee7 100644
--- a/pym/bob/layers.py
+++ b/pym/bob/layers.py
@@ -26,7 +26,7 @@ class LayersConfig:
         ret.__policies = self.__policies
 
         ret.__whiteList.update([c.upper() if self.__platform == "win32" else c
-            for c in config.get("layersWhiteList", []) ])
+            for c in config.get("layersWhitelist", []) ])
         ret.__scmOverrides[0:0] = [ ScmOverride(o) for o in config.get("layersScmOverrides", []) ]
 
         if rootLayer:

@rhubert
Copy link
Contributor

rhubert commented Oct 21, 2024

Call stack (most recent call first)
    #0: Recipe core::busybox, layer b/a/s/e/m/e/n/t/-/g/n/u/-/l/i/n/u/x, line 4, in main


Parse error: Layer 'b/a/s/e/m/e/n/t/-/g/n/u/-/l/i/n/u/x' requires a higher Bob version than root project!

->

diff --git a/pym/bob/input.py b/pym/bob/input.py
index 9922a0b..1b6b8d7 100644
--- a/pym/bob/input.py
+++ b/pym/bob/input.py
@@ -2032,7 +2032,7 @@ class Recipe(object):
         self.__layer = layer
 
         sourceName = ("Recipe " if isRecipe else "Class  ") + packageName + (
-            ", layer "+"/".join(layer) if layer else "")
+            ", layer "+ layer if layer else "")
         incHelperBash = IncludeHelper(BashLanguage, recipeSet.loadBinary,
                                       baseDir, packageName, sourceName).resolve
         incHelperPwsh = IncludeHelper(PwshLanguage, recipeSet.loadBinary,
@@ -3567,7 +3567,7 @@ class RecipeSet:
         minVer = config.get("bobMinimumVersion", "0.16")
         if compareVersion(maxVer, minVer) < 0:
             raise ParseError("Layer '{}' requires a higher Bob version than root project!"
-                                .format("/".join(layer)))
+                                .format(layer))
         maxVer = minVer # sub-layers must not have a higher bobMinimumVersion
 
         # Determine policies. The root layer determines the default settings
@@ -3577,7 +3577,7 @@ class RecipeSet:
             for (name, behaviour) in config.get("policies", {}).items():
                 if bool(self.__policies[name][0]) != behaviour:
                     raise ParseError("Layer '{}' requires different behaviour for policy '{}' than root project!"
-                                        .format("/".join(layer), name))
+                                        .format(layer, name))
         else:
             self.__policies = self.calculatePolicies(config)

This actually caused the layer digest to be saved as tuple rather than
the digest string itself.
@jkloetzke
Copy link
Member Author

Sorry for the long delay. I had some days off. I'm not sure if it makes sense to review your changes in detail but I made a few tests this morning.

No worries. My fault. I somehow overlooked that you have already written that you're not able to look into it until now.

Anyway, thanks for testing the PR! I don't think it makes sense to review each commit in detail.

After looking into your commits I think the findings are expected.

Indeed. The behaviour is caused by the state storing now the relative path instead of the absolute path. So far, only relative paths are stored in the project state and I wanted to align this for the layers too.

layersWhitelist is also not working (same on master 😭 )

Fix:

Thanks! I've picked up your fixes and added them to the stack.

@rhubert
Copy link
Contributor

rhubert commented Oct 22, 2024

Thanks. I did some more testing and took this also to our CI to test the layersScmOverrides:

Traceback (most recent call last):
  File "/home/hubert/projects/bob/pym/bob/scripts.py", line 152, in catchErrors
    ret = fun(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^
  File "/home/hubert/projects/bob/pym/bob/scripts.py", line 261, in cmd
    ret = cmd(args.args, bobRoot)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hubert/projects/bob/pym/bob/scripts.py", line 64, in __layers
    doLayers(*args, **kwargs)
  File "/home/hubert/projects/bob/pym/bob/cmds/layers.py", line 63, in doLayers
    doLayersStatus(args.args)
  File "/home/hubert/projects/bob/pym/bob/cmds/layers.py", line 29, in doLayersStatus
    layers.status(pp.show)
  File "/home/hubert/projects/bob/pym/bob/layers.py", line 321, in status
    status.add(ScmTaint.overridden, joinLines("> Overridden by:",
                                    ^^^^^^^^^
NameError: name 'joinLines' is not defined
Traceback (most recent call last):
  File "/home/hubert/projects/bob/pym/bob/scripts.py", line 152, in catchErrors
    ret = fun(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^
  File "/home/hubert/projects/bob/pym/bob/scripts.py", line 261, in cmd
    ret = cmd(args.args, bobRoot)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hubert/projects/bob/pym/bob/scripts.py", line 64, in __layers
    doLayers(*args, **kwargs)
  File "/home/hubert/projects/bob/pym/bob/cmds/layers.py", line 63, in doLayers
    doLayersStatus(args.args)
  File "/home/hubert/projects/bob/pym/bob/cmds/layers.py", line 29, in doLayersStatus
    layers.status(pp.show)
  File "/home/hubert/projects/bob/pym/bob/layers.py", line 322, in status
    indent(str(o), '   ')))
    ^^^^^^
NameError: name 'indent' is not defined
diff --git a/pym/bob/layers.py b/pym/bob/layers.py
index bc5cee7e..99f4be59 100644
--- a/pym/bob/layers.py
+++ b/pym/bob/layers.py
@@ -2,13 +2,14 @@ import datetime
 import os
 import schema
 import shutil
+from textwrap import indent
 from .errors import BuildError
 from .invoker import CmdFailedError, InvocationError, Invoker
 from .scm import getScm, ScmOverride, ScmStatus, ScmTaint
 from .state import BobState
 from .stringparser import Env
 from .input import RecipeSet, Scm, YamlCache
-from .utils import INVALID_CHAR_TRANS, getPlatformString, getPlatformEnvWhiteList, compareVersion
+from .utils import INVALID_CHAR_TRANS, getPlatformString, getPlatformEnvWhiteList, compareVersion, joinLines
 from .tty import DEBUG, EXECUTED, INFO, NORMAL, IMPORTANT, SKIPPED, WARNING, log

 class LayersConfig:

jkloetzke and others added 11 commits October 22, 2024 10:28
The A (attic), N (new) and O (overridden) flags need to be calculated
separately.
Switching a layer SCM is just like doing a fresh checkout. Use the same
severity.
The setting if obsolete layers shall be moved to the attic is not a
property of the layer but a tunable of the checkout function.
To stop traversing at layers without SCM is dangerous. Such layers can
still hold a reference other layers by means of an SCM.
Usually, the project tree (where the recipes are located) and the build
tree (dev/work directory) are in the same directory. But the build
directory can be separated by using "bob init".

In this case, layers that are backed by an SCM, should go to the build
directory. There are two main reasons for this:

  1. There can be multiple build directories per project directory. Each
     build directory is managed individually and thus the layers can be
     updated independently from each other. Doing that in the common
     project directory would create conflicts.
  2. The state of the layers is stored in the build directory.
     Consequently, there is no common state that would describe the
     layer SCMs in the project tree.

Fixes BobBuildTool#590.
Fix the typo when fetching layersWhitelist.

Co-authored-by: Ralf Hubert <[email protected]>
There were some remnants of the removed nested layers when generating
error messages. Addendum to 6f84d58.

Co-authored-by: Ralf Hubert <[email protected]>
@jkloetzke
Copy link
Member Author

Thanks for the patch!

@rhubert
Copy link
Contributor

rhubert commented Oct 22, 2024

I haven't found any other issue and all my tests+use cases are working as expected :) Thanks for "polishing" this 👍

@jkloetzke
Copy link
Member Author

Thanks for testing. 👍 Next step, 0.25 release... 😉

@jkloetzke jkloetzke merged commit 103c83b into BobBuildTool:master Oct 22, 2024
11 checks passed
@jkloetzke jkloetzke deleted the polish-layers branch October 22, 2024 09:07
@rhubert
Copy link
Contributor

rhubert commented Oct 22, 2024

Hmm...the attic handling is broken...

$ bob dev <package>
Layer 'foo' inline switch not possible and move to attic disabled 'layers/foo'

attic is None in this case

            if not attic:
                raise BuildError("Layer '{}' inline switch not possible and move to attic disabled '{}'!"
                    .format(self.__name, self.__layerDir))

@jkloetzke
Copy link
Member Author

Grmpf. Would you mind creating a PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants