Skip to content

ENT-13841: Fixed linting not working with namespaced bundle/body#41

Merged
olehermanse merged 1 commit intocfengine:mainfrom
SimonThalvorsen:ENT-13841
Apr 1, 2026
Merged

ENT-13841: Fixed linting not working with namespaced bundle/body#41
olehermanse merged 1 commit intocfengine:mainfrom
SimonThalvorsen:ENT-13841

Conversation

@SimonThalvorsen
Copy link
Copy Markdown
Contributor

Ticket: ENT-13841

Ticket: ENT-13841

Signed-off-by: Simon Halvorsen <simon.halvorsen@northern.tech>
Comment on lines +69 to +71
if guard is None: # Should never happen
print("ERROR: Bundle section without a promise guard")
return
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if guard is None: # Should never happen
print("ERROR: Bundle section without a promise guard")
return
assert guard

return _State(block_type=self.block_type)
# A bundle_section is always: promise_guard, [promises], [class_guarded_promises...]
# The promise_guard is guaranteed to exist by the grammar
guard = next((c for c in node.children if c.type == "promise_guard"), None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SimonThalvorsen Why is this this necessary, isn't promise_guard always node.children[0] ?

Suggested change
guard = next((c for c in node.children if c.type == "promise_guard"), None)
assert node.children
guard = node.children[0]

Comment on lines +84 to +87
@staticmethod
def qualify(name: str, namespace: str) -> str:
"""If name is already qualified (contains ':'), return as-is. Otherwise prepend namespace."""
return name if ":" in name else f"{namespace}:{name}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No big deal, but I would probably not make this a staticmethod. Can just be a top level function. Maybe prefixed with underscore to make it "internal".

Comment on lines +78 to +80
self.attribute_name = _text(child)
if self.attribute_name == "namespace":
self.namespace = _text(child.next_named_sibling).strip("\"'")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see 2 problems with this:

  1. We don't want to do this whenever attribute name is namespace. We want to do it only inside body file control. So there should be another condition in the if to see if we are inside the right place.
  2. Why use named sibling when we've set up the perfect state tracker to solve this kind of check? IMO we should set this when we get to the rval string (and check what block and attribute we are inside with an appropriate if check).

Comment on lines +269 to +272
state = _State()
ret = _stateful_walk(filename, lines, node, user_definition, strict, state=state)
state = _State() # Clear state
return ret
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clear state for what? Isn't this exactly the same as:

Suggested change
state = _State()
ret = _stateful_walk(filename, lines, node, user_definition, strict, state=state)
state = _State() # Clear state
return ret
return _stateful_walk(filename, lines, node, user_definition, strict, state=_State())

None,
)
if name_node is not None:
promise_blocks.add(_text(name_node))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work. I think you need to change your approach to reuse the same code across both passes. At a high level we need to:

  1. Parse all the files and convert them into syntax trees. This step will error on syntax errors.
  2. Loop over all the syntax trees and "discover" definitions. This step should not error (actually might in the future, if we want to add detection for duplicate definitions, for example).
  3. Loop over all the syntax trees again and actually perform our checks. This is where we will error for all the linting rules.

Step 2 and 3 should both use the _stateful_walk function IMO. It can have an argument for mode - for example "discovery" | "linting".

I understand the desire to do step 2 in a simple way as you have it here (just find and collect all the bundles and bodies). However, I don't think it's that simple, you need to have the same / similar context available as in step 3. You need to know which namespace you are in. In the future, if we do something similar for classes and variables, you need to know which bundle you are in, etc.

Another thing we have not handled at all yet is macros - the state could keep track of which macros we are "inside" (@if minimum_version for example).

None,
)
if ns_attr is not None:
ns = _text(ns_attr.next_named_sibling).strip("\"'")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has the same problem as your other logic for detecting namespace - we only want to change namespace using a body file control block, not any attribute or any body.

@olehermanse olehermanse merged commit 6c40b04 into cfengine:main Apr 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants