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

docs: feature flag handling #217

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

MaximilianSoerenPollak
Copy link
Contributor

@MaximilianSoerenPollak MaximilianSoerenPollak commented Jan 24, 2025

This PR is still a WIP as there is tests missing and some implementation details may change.

Any feedback is appreciated.

Todo

  • Add docstrings to functions
  • Add integration tests
  • Write small document explaining usage and how it works
  • Move the whole extension to docs/_tooling/extensions
  • Remove bazel BUILD files.
  • Add Unit tests to achieve 95%+ coverage
  • Rebase it onto new score structure
  • Find a solution to ensure needtable, needpie etc. also ignore hidden requirements
    • Create tests for these?
  • Find a (nice) way to make it work via docs:incremental
  • Improve QoL (more logging statements & asserts)
  • Improve documentation with reasoning for decisions etc.

PR provides a way to disable requirements based on feature flags set in bazel.
It has a 'translation' layer where we can map feature flags to tags, which are then used to disable/hide requirements.

Closes #202

This PR provdies a way to disable requirements based on feature flags
set in bazel.
It has a 'translation' layer where we can map feature flags to tags,
which are then used to disable/hide requirements.
Added integration tests to the packaging.
Added docstrings to functions inside the extension.
Formatting also done.
Comment on lines 41 to 64
```bzl
FEATURE_TAG_MAPPING = {
"feature1": ["some-ip", "tag2"], # --//docs:feature1=true -> will expand to 'some-ip', 'tag2'
"second-feature": ["test-feat", "tag6"],
}
```

The default values are defined like such:
```bzl
def define_feature_flags(name):
bool_flag(
name = "feature1",
build_setting_default = False,
)
bool_flag(
name = "second-feature",
build_setting_default = False,
)

feature_flag_translator(
name = name,
flags = {":feature1": "True", ":second-feature": "True"},
)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

This restricts us to only bool flags. We also need to consider other feature_flag types of Skylark. But for now this is a great start.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is true, but the foundation at least should allow this to be expanded into other flag types in the future. The translation then might have to be adapted to account for those.

@AlexanderLanin AlexanderLanin added community:infrastructure General Score infrastructure topics docs-as-code labels Jan 28, 2025
Revised logic, so now feature=true means that those requirements are
enabled, not disabled.
Added a decision record to lay out why 'hide' was choosen
Adapted README to make it a bit clearer.
Added wrappers for filtering functions used by needpie, etc.
@MaximilianSoerenPollak MaximilianSoerenPollak force-pushed the MaximilianSoerenPollak-packaging-concept branch from e1fa9ab to 6ddf262 Compare January 29, 2025 16:55
docs/BUILD Outdated
@@ -171,3 +177,28 @@ score_py_pytest(
visibility = ["//visibility:public"],
deps = [":score_metamodel"],
)

py_library(
name = "modularity",
Copy link
Member

Choose a reason for hiding this comment

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

Can we pick a name that is less ambiguous? "modularity" is too close to "module" which we will be using for the SCORE code implementations. Something with "variant", "feature" or "flag" maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The name was chosen as packaging didn't sound right and might interfere with other things. I think for sure though a more descriptive name would be better.
Maybe one of these?

  • FlagConfiguration
  • VariantHandling
  • FeatureFlagsConfig
  • ConfigureRequirements

Copy link
Member

Choose a reason for hiding this comment

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

FeatureFlaggedRequirements 😆

This requirement will not be disabled.
```
We can then build it with our feature flag enabled via `bazel build //docs:docs --//docs:second-feature=true`
This will expand via our translation layer `feature_flag.bzl` into the tags `test-feat` and `tag5`.
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need such a translation layer? Unless I missed it, it's not described anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main reason why it is needed is to make it possible to expand one flag into multiple tags. We theoretically could work without this layer with some adaptations, but then you need to add EACH tag as a separate argument in the command.

Edit:
It also allows for more complex 'flags' in the future instead of just 'boolean' as it is currently.

Copy link
Member

Choose a reason for hiding this comment

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

Why do we have more flags than commands? Can you mention that somewhere in md/rst?

Comment on lines 32 to 36
fake_app_ok = SimpleNamespace()
fake_app_ok.config = SimpleNamespace()
fake_app_ok.config.filter_tags_file_path = str(
feature_flags_dir / "filter_tags.txt"
)
Copy link
Member

Choose a reason for hiding this comment

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

e.g.: fake_app_ok = fake_app({filter_tags_file_path: str( feature_flags_dir / "filter_tags.txt" )})

def test_modularity_hide_ok(
sphinx_app_setup, basic_conf, basic_rst_file, positive_filter_tags, sphinx_base_dir
):
app = sphinx_app_setup(basic_conf, basic_rst_file, positive_filter_tags)
Copy link
Member

Choose a reason for hiding this comment

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

👍

Reworked README to add clarity
Renamed extension from modularity to give a more meaningful name
Reverted upgraded lockfile
Integrated extension/feature into 'incremental'
@MaximilianSoerenPollak MaximilianSoerenPollak marked this pull request as ready for review January 30, 2025 10:43
@MaximilianSoerenPollak MaximilianSoerenPollak changed the title docs: WIP packaging concept docs: feature flag handling Jan 31, 2025
@danwos
Copy link
Contributor

danwos commented Feb 6, 2025

Can't the extension score_feature_flag_handling be a contribution to Sphinx-Needs itself?

As some Sphinx-Needs internal functions get monkey patched, I see a huge risk that this may not work with upcoming Sphinx-Needs releases and it keeps the maintenance high on the SCORE side.

So would it be okay to create an upstream PR with this and get rid of the SCORE-specific implementation for the Sphinx-Needs part?
For sure the discussion in Sphinx-Needs may take some time, so the SCORE-specific solution is still needed during this time.

@MaximilianSoerenPollak
Copy link
Contributor Author

Can't the extension score_feature_flag_handling be a contribution to Sphinx-Needs itself?

As some Sphinx-Needs internal functions get monkey patched, I see a huge risk that this may not work with upcoming Sphinx-Needs releases and it keeps the maintenance high on the SCORE side.

So would it be okay to create an upstream PR with this and get rid of the SCORE-specific implementation for the Sphinx-Needs part? For sure the discussion in Sphinx-Needs may take some time, so the SCORE-specific solution is still needed during this time.

This for sure would make future compatibility nicer and easier. I could make this an upstream PR, although some questions would need to be clarified etc.
Should we make an issue in the Sphinx-needs repo to discuss this further?

def setup(app):
logger.debug("score_feature_flag_handling extension loaded")
app.add_config_value("filter_tags_file_path", None, "env")
app.connect("env-updated", hide_needs)

Choose a reason for hiding this comment

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

There is a specific event for this needs-before-sealing; func(app, needs), as mentioned in https://sphinx-needs.readthedocs.io/en/latest/changelog.html#improvements-to-filtering-at-scale

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm interesting, I must have made a mistake then. I tried this in the beginning but decided not go go with it as something didn't quite work. I will check it out again, as if we have such specific events it would be nicer to call them correctly.
Thanks for pointing it out.

@danwos
Copy link
Contributor

danwos commented Feb 6, 2025

An issue in the Sphinx-Needs repo would be super.
I will also support as much as I can.

@danwos
Copy link
Contributor

danwos commented Feb 7, 2025

I have created a related Sphinx-Needs ticket: useblocks/sphinx-needs#1399

@MaximilianSoerenPollak Can you please check, if my description/idea supports your use case or if anything is missing? Thanks.

@AlexanderLanin
Copy link
Member

@danwos how about a small issue for sphinx-needs to correctly account for hide in tables, pies etc.? That should be trivially implementable without further discussions?

@danwos
Copy link
Contributor

danwos commented Feb 26, 2025

@danwos how about a small issue for sphinx-needs to correctly account for hide in tables, pies etc.? That should be trivially implementable without further discussions?

That's tough because the current implementation is the expected one.
hide shall hide it, but it shall be part of the internal needs dictionary and also be part of all filtering. Some projects are using it this way.

For "hiding" it completely, delete was introduced, but it does not support dynamic functions and variants support right now. So this needs to be fixed somehow.

@AlexanderLanin
Copy link
Member

@danwos well it can be there. It should just not show up in needpie etc?!

@danwos
Copy link
Contributor

danwos commented Feb 26, 2025

That's the point for hide, it shall be part of needpie and co. But it shall not have its own representation in the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community:infrastructure General Score infrastructure topics docs-as-code
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

docs: Add Functionality to Enable/Disable Requirements
5 participants