From c403e121dcbf2d7782d4d57ca422efb467ef4638 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 14 Jun 2022 20:22:07 -0500 Subject: [PATCH 01/16] stub sample_conf pants-plugin --- conf/BUILD | 8 ++- pants-plugins/sample_conf/BUILD | 1 + pants-plugins/sample_conf/__init__.py | 0 pants-plugins/sample_conf/register.py | 24 +++++++ pants-plugins/sample_conf/rules.py | 83 +++++++++++++++++++++++ pants-plugins/sample_conf/target_types.py | 40 +++++++++++ pants.toml | 1 + 7 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 pants-plugins/sample_conf/BUILD create mode 100644 pants-plugins/sample_conf/__init__.py create mode 100644 pants-plugins/sample_conf/register.py create mode 100644 pants-plugins/sample_conf/rules.py create mode 100644 pants-plugins/sample_conf/target_types.py diff --git a/conf/BUILD b/conf/BUILD index c434d58228..e9b7abec41 100644 --- a/conf/BUILD +++ b/conf/BUILD @@ -3,10 +3,14 @@ file( source="st2rc.sample.ini", ) -file( +sample_conf( name="st2.conf.sample", - source="st2.conf.sample", + # source="st2.conf.sample", ) +#file( +# name="st2.conf.sample", +# source="st2.conf.sample", +#) file( name="logrotate", diff --git a/pants-plugins/sample_conf/BUILD b/pants-plugins/sample_conf/BUILD new file mode 100644 index 0000000000..db46e8d6c9 --- /dev/null +++ b/pants-plugins/sample_conf/BUILD @@ -0,0 +1 @@ +python_sources() diff --git a/pants-plugins/sample_conf/__init__.py b/pants-plugins/sample_conf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pants-plugins/sample_conf/register.py b/pants-plugins/sample_conf/register.py new file mode 100644 index 0000000000..284c375c37 --- /dev/null +++ b/pants-plugins/sample_conf/register.py @@ -0,0 +1,24 @@ +# Copyright 2022 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from sample_conf.rules import rules as sample_conf_rules +from sample_conf.target_types import SampleConf + + +def rules(): + return [*sample_conf_rules()] + + +def target_types(): + return [SampleConf] diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py new file mode 100644 index 0000000000..039c6f1605 --- /dev/null +++ b/pants-plugins/sample_conf/rules.py @@ -0,0 +1,83 @@ +# Copyright 2021 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from pants.backend.python.target_types import PythonSourceField +from pants.core.target_types import FileSourceField +from pants.core.util_rules.source_files import SourceFilesRequest +from pants.core.util_rules.stripped_source_files import StrippedSourceFiles +from pants.engine.fs import ( + CreateDigest, + Digest, + DigestContents, + FileContent, + Snapshot, +) +from pants.engine.rules import Get, MultiGet, collect_rules, rule +from pants.engine.target import ( + GeneratedSources, + GenerateSourcesRequest, + SourcesField, + TransitiveTargets, + TransitiveTargetsRequest, +) +from pants.engine.unions import UnionRule + +from sample_conf.target_types import ( + #GenerateSampleConfSourceField, + SampleConfSourceField, + SampleConf, +) + + +class GenerateSampleConfRequest(GenerateSourcesRequest): + input = SampleConfSourceField + output = SampleConfSourceField + #output = FileSourceField + + +@rule +async def generate_sample_conf( + request: GenerateSampleConfRequest, +) -> GeneratedSources: + target = request.protocol_target + + # Find all the dependencies of our target + transitive_targets = await Get(TransitiveTargets, TransitiveTargetsRequest([target.address])) + + # Get the source files without the source-root prefix. + #stripped_sources = await Get(StrippedSourceFiles, SourceFilesRequest( + # (tgt.get(SourcesField) for tgt in transitive_targets.closure) + #)) + + #contents = await Get(DigestContents, Digest, stripped_sources) + + # actually generate it with an external script. + # Generation cannot be inlined here because it needs to import the st2 code. + sample_conf = "asdf\n" + + output_path = f"{target.address.spec_path}/{target[SampleConfSourceField].value}" + content = FileContent(output_path, sample_conf.encode("utf-8")) + + output_digest = await Get(Digest, CreateDigest([content])) + output_snapshot = await Get(Snapshot, Digest, output_digest) + return GeneratedSources(output_snapshot) + + +def rules(): + return [ + *collect_rules(), + UnionRule( + GenerateSourcesRequest, + GenerateSampleConfRequest, + ), + ] diff --git a/pants-plugins/sample_conf/target_types.py b/pants-plugins/sample_conf/target_types.py new file mode 100644 index 0000000000..2a625577a6 --- /dev/null +++ b/pants-plugins/sample_conf/target_types.py @@ -0,0 +1,40 @@ +# Copyright 2022 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from pants.engine.fs import GlobMatchErrorBehavior +from pants.engine.target import ( + COMMON_TARGET_FIELDS, + Dependencies, + OptionalSingleSourceField, + Target, +) + + +class SampleConfSourceField(OptionalSingleSourceField): + default = "st2.conf.sample" + + default_glob_match_error_behavior = GlobMatchErrorBehavior.ignore + + +#class SampleConfSourceField(SingleSourceField): +# alias = "output" +# required = False +# default = "st2.conf.sample" + + +class SampleConf(Target): + alias = "sample_conf" + core_fields = (*COMMON_TARGET_FIELDS, Dependencies, SampleConfSourceField) + help = ( + "Generate st2.conf.sample file from python sources." + ) diff --git a/pants.toml b/pants.toml index 922e7984d9..15a057cf02 100644 --- a/pants.toml +++ b/pants.toml @@ -25,6 +25,7 @@ backend_packages = [ # internal plugins in pants-plugins/ "pants.backend.plugin_development", "api_spec", + "sample_conf", "schemas", ] # pants ignores files in .gitignore, .*/ directories, /dist/ directory, and __pycache__. From 0507b114c588ff5250b25a002e65467d2d6b517a Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 14 Jun 2022 22:37:39 -0500 Subject: [PATCH 02/16] try using fmt to generate st2.conf.sample --- pants-plugins/sample_conf/rules.py | 44 ++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index 039c6f1605..5e4c0e7130 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -11,7 +11,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from dataclasses import dataclass + from pants.backend.python.target_types import PythonSourceField +from pants.core.goals.fmt import FmtResult, FmtRequest +from pants.core.goals.lint import LintResult, LintResults, LintTargetsRequest from pants.core.target_types import FileSourceField from pants.core.util_rules.source_files import SourceFilesRequest from pants.core.util_rules.stripped_source_files import StrippedSourceFiles @@ -24,21 +28,25 @@ ) from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import ( + FieldSet, GeneratedSources, GenerateSourcesRequest, SourcesField, + Target, TransitiveTargets, TransitiveTargetsRequest, ) from pants.engine.unions import UnionRule from sample_conf.target_types import ( - #GenerateSampleConfSourceField, SampleConfSourceField, SampleConf, ) +# CODEGEN ######################################################### + + class GenerateSampleConfRequest(GenerateSourcesRequest): input = SampleConfSourceField output = SampleConfSourceField @@ -73,11 +81,37 @@ async def generate_sample_conf( return GeneratedSources(output_snapshot) +# FMT/LINT ######################################################### + + +@dataclass(frozen=True) +class GenerateSampleConfFieldSet(FieldSet): + required_fields = (SampleConfSourceField,) + + source: SampleConfSourceField + + +class GenerateSampleConfViaFmtRequest(FmtRequest, LintTargetsRequest): + field_set_type = GenerateSampleConfFieldSet + name = "st2.conf.sample" + + +@rule(desc="Generate st2.conf.sample") +async def gen_sample_conf_via_fmt(request: GenerateSampleConfViaFmtRequest) -> FmtResult: + ... + return FmtResult(..., formatter_name=request.name) + + +#@rule(desc="Ensure st2.conf.sample is up-to-date") +#async def sample_conf_lint(request: GenerateSampleConfViaFmtRequest) -> LintResults: +# ... +# return LintResults([], linter_name=request.name) + + def rules(): return [ *collect_rules(), - UnionRule( - GenerateSourcesRequest, - GenerateSampleConfRequest, - ), + UnionRule(GenerateSourcesRequest, GenerateSampleConfRequest), + UnionRule(FmtRequest, GenerateSampleConfViaFmtRequest), +# UnionRule(LintTargetsRequest, GenerateSampleConfViaFmtRequest), ] From 58f01897628fe03a7ee70fc0bbf9d2e3b8c0e1d4 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 15 Jun 2022 15:53:35 -0500 Subject: [PATCH 03/16] wire up pants to run config_gen to generate st2.conf.sample --- BUILD | 14 ++++++++ conf/BUILD | 11 +++--- pants-plugins/sample_conf/rules.py | 56 +++++++++++++++++++++++------- tools/BUILD | 25 ++++++++++++- tools/config_gen.py | 4 +++ 5 files changed, 90 insertions(+), 20 deletions(-) diff --git a/BUILD b/BUILD index a54068afed..f7b31b622a 100644 --- a/BUILD +++ b/BUILD @@ -31,9 +31,23 @@ python_requirements( "//:reqs#zake", ] }, + # make sure anything that uses st2-auth-ldap gets the st2auth constant + "st2-auth-ldap": { + "dependencies": [ + "st2auth/st2auth/backends/constants.py", + ] + } }, ) +target( + name="auth_backends", + dependencies=[ + "//:reqs#st2-auth-backend-flat-file", + "//:reqs#st2-auth-ldap", + ], +) + python_test_utils( name="test_utils", skip_pylint=True, diff --git a/conf/BUILD b/conf/BUILD index e9b7abec41..a3374bfca3 100644 --- a/conf/BUILD +++ b/conf/BUILD @@ -3,14 +3,13 @@ file( source="st2rc.sample.ini", ) -sample_conf( +sample_conf( # defined in pants-plugins/sample_conf name="st2.conf.sample", - # source="st2.conf.sample", + source="st2.conf.sample", + dependencies=[ + "tools/config_gen.py", + ], ) -#file( -# name="st2.conf.sample", -# source="st2.conf.sample", -#) file( name="logrotate", diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index 5e4c0e7130..a59ca79eb2 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -13,19 +13,32 @@ # limitations under the License. from dataclasses import dataclass -from pants.backend.python.target_types import PythonSourceField +from pants.backend.python.target_types import EntryPoint, PythonSourceField +from pants.backend.python.util_rules.pex import ( + Pex, + PexRequest, + VenvPex, + VenvPexProcess, +) +from pants.backend.python.util_rules.pex_from_targets import PexFromTargetsRequest +from pants.backend.python.util_rules.python_sources import ( + PythonSourceFiles, + PythonSourceFilesRequest, +) from pants.core.goals.fmt import FmtResult, FmtRequest from pants.core.goals.lint import LintResult, LintResults, LintTargetsRequest from pants.core.target_types import FileSourceField -from pants.core.util_rules.source_files import SourceFilesRequest -from pants.core.util_rules.stripped_source_files import StrippedSourceFiles +from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest +from pants.engine.addresses import Address, UnparsedAddressInputs from pants.engine.fs import ( CreateDigest, Digest, DigestContents, FileContent, + MergeDigests, Snapshot, ) +from pants.engine.process import Process, ProcessResult from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import ( FieldSet, @@ -60,21 +73,38 @@ async def generate_sample_conf( target = request.protocol_target # Find all the dependencies of our target - transitive_targets = await Get(TransitiveTargets, TransitiveTargetsRequest([target.address])) - - # Get the source files without the source-root prefix. - #stripped_sources = await Get(StrippedSourceFiles, SourceFilesRequest( - # (tgt.get(SourcesField) for tgt in transitive_targets.closure) - #)) - - #contents = await Get(DigestContents, Digest, stripped_sources) + transitive_targets = await Get( + TransitiveTargets, TransitiveTargetsRequest([target.address]) + ) # actually generate it with an external script. # Generation cannot be inlined here because it needs to import the st2 code. - sample_conf = "asdf\n" + script = "config_gen" + pex_get = Get( + VenvPex, + PexFromTargetsRequest( + [Address("tools", target_name="tools", relative_file_path=f"{script}.py")], + output_filename=f"{script}.pex", + internal_only=True, + main=EntryPoint(script), + ), + ) + sources_get = Get( + PythonSourceFiles, + PythonSourceFilesRequest(transitive_targets.closure, include_files=True), + ) + pex, sources = await MultiGet(pex_get, sources_get) + + result = await Get( + ProcessResult, + VenvPexProcess( + pex, + description=f"Regenerating {request.protocol_target.address}.", + ), + ) output_path = f"{target.address.spec_path}/{target[SampleConfSourceField].value}" - content = FileContent(output_path, sample_conf.encode("utf-8")) + content = FileContent(output_path, result.stdout) output_digest = await Get(Digest, CreateDigest([content])) output_snapshot = await Get(Snapshot, Digest, output_digest) diff --git a/tools/BUILD b/tools/BUILD index 17e8df14c8..a9a7a9e3c7 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -1,4 +1,27 @@ -python_sources() +python_sources( + overrides={ + "config_gen.py": { + "dependencies": [ + # the auth backends get listed in the conf file + "//:auth_backends", + # the following should match CONFIGS in config_gen.py + # grep -rl '^def register_opts(ignore_errors=False):' st2* + "st2actions/st2actions/scheduler/config.py", + "st2actions/st2actions/workflows/config.py", + "st2actions/st2actions/notifier/config.py", + "st2actions/st2actions/config.py", + "st2api/st2api/config.py", + "st2auth/st2auth/config.py", + "st2common/st2common/config.py", + "st2reactor/st2reactor/garbage_collector/config.py", + "st2reactor/st2reactor/timer/config.py", + "st2reactor/st2reactor/sensor/config.py", + "st2reactor/st2reactor/rules/config.py", + "st2stream/st2stream/config.py", + ] + }, + }, +) shell_sources( name="shell", diff --git a/tools/config_gen.py b/tools/config_gen.py index aeba38ff0e..c86360173b 100755 --- a/tools/config_gen.py +++ b/tools/config_gen.py @@ -25,6 +25,10 @@ CONFIGS = [ + # this is duplicated in conf/BUILD + # TODO: replace this with a heuristic that searches for config.py + # maybe with an exclude list (eg st2tests.config st2client) + # grep -rl 'def register_opts(ignore_errors=False):' st2* "st2actions.config", "st2actions.scheduler.config", "st2actions.notifier.config", From 64dd1599b79bdd7a7b77efd54687bac25c9ad6b5 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 15 Jun 2022 15:53:48 -0500 Subject: [PATCH 04/16] reformat with black --- pants-plugins/sample_conf/rules.py | 13 ++++--------- pants-plugins/sample_conf/target_types.py | 6 ++---- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index a59ca79eb2..b9dcb15e39 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -63,7 +63,6 @@ class GenerateSampleConfRequest(GenerateSourcesRequest): input = SampleConfSourceField output = SampleConfSourceField - #output = FileSourceField @rule @@ -127,21 +126,17 @@ class GenerateSampleConfViaFmtRequest(FmtRequest, LintTargetsRequest): @rule(desc="Generate st2.conf.sample") -async def gen_sample_conf_via_fmt(request: GenerateSampleConfViaFmtRequest) -> FmtResult: +async def gen_sample_conf_via_fmt( + request: GenerateSampleConfViaFmtRequest, +) -> FmtResult: ... return FmtResult(..., formatter_name=request.name) -#@rule(desc="Ensure st2.conf.sample is up-to-date") -#async def sample_conf_lint(request: GenerateSampleConfViaFmtRequest) -> LintResults: -# ... -# return LintResults([], linter_name=request.name) - - def rules(): return [ *collect_rules(), UnionRule(GenerateSourcesRequest, GenerateSampleConfRequest), UnionRule(FmtRequest, GenerateSampleConfViaFmtRequest), -# UnionRule(LintTargetsRequest, GenerateSampleConfViaFmtRequest), + # UnionRule(LintTargetsRequest, GenerateSampleConfViaFmtRequest), ] diff --git a/pants-plugins/sample_conf/target_types.py b/pants-plugins/sample_conf/target_types.py index 2a625577a6..f71d93eeec 100644 --- a/pants-plugins/sample_conf/target_types.py +++ b/pants-plugins/sample_conf/target_types.py @@ -26,7 +26,7 @@ class SampleConfSourceField(OptionalSingleSourceField): default_glob_match_error_behavior = GlobMatchErrorBehavior.ignore -#class SampleConfSourceField(SingleSourceField): +# class SampleConfSourceField(SingleSourceField): # alias = "output" # required = False # default = "st2.conf.sample" @@ -35,6 +35,4 @@ class SampleConfSourceField(OptionalSingleSourceField): class SampleConf(Target): alias = "sample_conf" core_fields = (*COMMON_TARGET_FIELDS, Dependencies, SampleConfSourceField) - help = ( - "Generate st2.conf.sample file from python sources." - ) + help = "Generate st2.conf.sample file from python sources." From e26a6c199f8c85650c3be8af7991ecf5225d3dda Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 15 Jun 2022 17:09:11 -0500 Subject: [PATCH 05/16] finish sample_conf plugin as fmt instead of codegen codegen cannot update workspace files. It only puts files under dist/codegen. So, we will use fmt instead to make sure this gets updated as needed. --- pants-plugins/sample_conf/rules.py | 100 ++++++++++------------ pants-plugins/sample_conf/target_types.py | 13 +-- 2 files changed, 45 insertions(+), 68 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index b9dcb15e39..fe0d777320 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -13,9 +13,8 @@ # limitations under the License. from dataclasses import dataclass -from pants.backend.python.target_types import EntryPoint, PythonSourceField +from pants.backend.python.target_types import EntryPoint from pants.backend.python.util_rules.pex import ( - Pex, PexRequest, VenvPex, VenvPexProcess, @@ -26,30 +25,23 @@ PythonSourceFilesRequest, ) from pants.core.goals.fmt import FmtResult, FmtRequest -from pants.core.goals.lint import LintResult, LintResults, LintTargetsRequest -from pants.core.target_types import FileSourceField -from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest -from pants.engine.addresses import Address, UnparsedAddressInputs +from pants.engine.addresses import Address from pants.engine.fs import ( CreateDigest, Digest, - DigestContents, FileContent, - MergeDigests, Snapshot, ) -from pants.engine.process import Process, ProcessResult +from pants.engine.process import FallibleProcessResult from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import ( FieldSet, - GeneratedSources, - GenerateSourcesRequest, SourcesField, - Target, TransitiveTargets, TransitiveTargetsRequest, ) from pants.engine.unions import UnionRule +from pants.util.logging import LogLevel from sample_conf.target_types import ( SampleConfSourceField, @@ -57,35 +49,49 @@ ) -# CODEGEN ######################################################### +SCRIPT = "config_gen" -class GenerateSampleConfRequest(GenerateSourcesRequest): - input = SampleConfSourceField - output = SampleConfSourceField +@dataclass(frozen=True) +class GenerateSampleConfFieldSet(FieldSet): + required_fields = (SampleConfSourceField,) + + source: SampleConfSourceField + +class GenerateSampleConfViaFmtRequest(FmtRequest): + field_set_type = GenerateSampleConfFieldSet + name = SCRIPT -@rule -async def generate_sample_conf( - request: GenerateSampleConfRequest, -) -> GeneratedSources: - target = request.protocol_target + +@rule( + desc="Update conf/st2.conf.sample with tools/config_gen.py", + level=LogLevel.DEBUG, +) +async def generate_sample_conf_via_fmt( + request: GenerateSampleConfViaFmtRequest, +) -> FmtResult: + # There will only be one target+field_set, but we iterate + # to satisfy how fmt expects that there could be more than one. + # If there is more than one, they will all get the same contents. # Find all the dependencies of our target transitive_targets = await Get( - TransitiveTargets, TransitiveTargetsRequest([target.address]) + TransitiveTargets, + TransitiveTargetsRequest( + [field_set.address for field_set in request.field_sets] + ), ) # actually generate it with an external script. # Generation cannot be inlined here because it needs to import the st2 code. - script = "config_gen" pex_get = Get( VenvPex, PexFromTargetsRequest( - [Address("tools", target_name="tools", relative_file_path=f"{script}.py")], - output_filename=f"{script}.pex", + [Address("tools", target_name="tools", relative_file_path=f"{SCRIPT}.py")], + output_filename=f"{SCRIPT}.pex", internal_only=True, - main=EntryPoint(script), + main=EntryPoint(SCRIPT), ), ) sources_get = Get( @@ -95,48 +101,28 @@ async def generate_sample_conf( pex, sources = await MultiGet(pex_get, sources_get) result = await Get( - ProcessResult, + FallibleProcessResult, VenvPexProcess( pex, - description=f"Regenerating {request.protocol_target.address}.", + description=f"Regenerating st2.conf.sample", ), ) - output_path = f"{target.address.spec_path}/{target[SampleConfSourceField].value}" - content = FileContent(output_path, result.stdout) + contents = [ + FileContent( + f"{field_set.address.spec_path}/{field_set.source.value}", + result.stdout, + ) + for field_set in request.field_sets + ] - output_digest = await Get(Digest, CreateDigest([content])) + output_digest = await Get(Digest, CreateDigest(contents)) output_snapshot = await Get(Snapshot, Digest, output_digest) - return GeneratedSources(output_snapshot) - - -# FMT/LINT ######################################################### - - -@dataclass(frozen=True) -class GenerateSampleConfFieldSet(FieldSet): - required_fields = (SampleConfSourceField,) - - source: SampleConfSourceField - - -class GenerateSampleConfViaFmtRequest(FmtRequest, LintTargetsRequest): - field_set_type = GenerateSampleConfFieldSet - name = "st2.conf.sample" - - -@rule(desc="Generate st2.conf.sample") -async def gen_sample_conf_via_fmt( - request: GenerateSampleConfViaFmtRequest, -) -> FmtResult: - ... - return FmtResult(..., formatter_name=request.name) + return FmtResult.create(request, result, output_snapshot, strip_chroot_path=True) def rules(): return [ *collect_rules(), - UnionRule(GenerateSourcesRequest, GenerateSampleConfRequest), UnionRule(FmtRequest, GenerateSampleConfViaFmtRequest), - # UnionRule(LintTargetsRequest, GenerateSampleConfViaFmtRequest), ] diff --git a/pants-plugins/sample_conf/target_types.py b/pants-plugins/sample_conf/target_types.py index f71d93eeec..420e6d0a6e 100644 --- a/pants-plugins/sample_conf/target_types.py +++ b/pants-plugins/sample_conf/target_types.py @@ -11,26 +11,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from pants.engine.fs import GlobMatchErrorBehavior from pants.engine.target import ( COMMON_TARGET_FIELDS, Dependencies, - OptionalSingleSourceField, + SingleSourceField, Target, ) -class SampleConfSourceField(OptionalSingleSourceField): +class SampleConfSourceField(SingleSourceField): default = "st2.conf.sample" - default_glob_match_error_behavior = GlobMatchErrorBehavior.ignore - - -# class SampleConfSourceField(SingleSourceField): -# alias = "output" -# required = False -# default = "st2.conf.sample" - class SampleConf(Target): alias = "sample_conf" From 9451abbbd6056ef7a6e45a1dc9c8956c5294b120 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Wed, 15 Jun 2022 20:02:17 -0500 Subject: [PATCH 06/16] simplify sample_conf pants-plugin --- pants-plugins/sample_conf/rules.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index fe0d777320..adbc884ee5 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -85,7 +85,7 @@ async def generate_sample_conf_via_fmt( # actually generate it with an external script. # Generation cannot be inlined here because it needs to import the st2 code. - pex_get = Get( + pex = await Get( VenvPex, PexFromTargetsRequest( [Address("tools", target_name="tools", relative_file_path=f"{SCRIPT}.py")], @@ -94,11 +94,6 @@ async def generate_sample_conf_via_fmt( main=EntryPoint(SCRIPT), ), ) - sources_get = Get( - PythonSourceFiles, - PythonSourceFilesRequest(transitive_targets.closure, include_files=True), - ) - pex, sources = await MultiGet(pex_get, sources_get) result = await Get( FallibleProcessResult, From f849fbb4fbd8b5d4858b2cd38e73e22793ff42b3 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Fri, 17 Jun 2022 12:29:55 -0500 Subject: [PATCH 07/16] cleanup dead code in pants-plugins --- pants-plugins/sample_conf/rules.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index adbc884ee5..58d9777606 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -75,14 +75,6 @@ async def generate_sample_conf_via_fmt( # to satisfy how fmt expects that there could be more than one. # If there is more than one, they will all get the same contents. - # Find all the dependencies of our target - transitive_targets = await Get( - TransitiveTargets, - TransitiveTargetsRequest( - [field_set.address for field_set in request.field_sets] - ), - ) - # actually generate it with an external script. # Generation cannot be inlined here because it needs to import the st2 code. pex = await Get( From f99b76a004db468174b274150e62b7cfa602e459 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 21 Jun 2022 14:10:56 -0500 Subject: [PATCH 08/16] resolve lint issues in pants-plugins --- pants-plugins/sample_conf/rules.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index 58d9777606..ac6a42a638 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -15,15 +15,10 @@ from pants.backend.python.target_types import EntryPoint from pants.backend.python.util_rules.pex import ( - PexRequest, VenvPex, VenvPexProcess, ) from pants.backend.python.util_rules.pex_from_targets import PexFromTargetsRequest -from pants.backend.python.util_rules.python_sources import ( - PythonSourceFiles, - PythonSourceFilesRequest, -) from pants.core.goals.fmt import FmtResult, FmtRequest from pants.engine.addresses import Address from pants.engine.fs import ( @@ -33,20 +28,12 @@ Snapshot, ) from pants.engine.process import FallibleProcessResult -from pants.engine.rules import Get, MultiGet, collect_rules, rule -from pants.engine.target import ( - FieldSet, - SourcesField, - TransitiveTargets, - TransitiveTargetsRequest, -) +from pants.engine.rules import Get, collect_rules, rule +from pants.engine.target import FieldSet from pants.engine.unions import UnionRule from pants.util.logging import LogLevel -from sample_conf.target_types import ( - SampleConfSourceField, - SampleConf, -) +from sample_conf.target_types import SampleConfSourceField SCRIPT = "config_gen" @@ -91,7 +78,7 @@ async def generate_sample_conf_via_fmt( FallibleProcessResult, VenvPexProcess( pex, - description=f"Regenerating st2.conf.sample", + description="Regenerating st2.conf.sample", ), ) From ca6562be378eaa046316ae69dc4a9cb5fb9f1d4d Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Tue, 11 Oct 2022 20:04:08 -0500 Subject: [PATCH 09/16] pants plugins updates --- pants-plugins/sample_conf/rules.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index ac6a42a638..bf10c01306 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -19,7 +19,7 @@ VenvPexProcess, ) from pants.backend.python.util_rules.pex_from_targets import PexFromTargetsRequest -from pants.core.goals.fmt import FmtResult, FmtRequest +from pants.core.goals.fmt import FmtResult, FmtTargetsRequest from pants.engine.addresses import Address from pants.engine.fs import ( CreateDigest, @@ -46,7 +46,7 @@ class GenerateSampleConfFieldSet(FieldSet): source: SampleConfSourceField -class GenerateSampleConfViaFmtRequest(FmtRequest): +class GenerateSampleConfViaFmtTargetsRequest(FmtTargetsRequest): field_set_type = GenerateSampleConfFieldSet name = SCRIPT @@ -56,7 +56,7 @@ class GenerateSampleConfViaFmtRequest(FmtRequest): level=LogLevel.DEBUG, ) async def generate_sample_conf_via_fmt( - request: GenerateSampleConfViaFmtRequest, + request: GenerateSampleConfViaFmtTargetsRequest, ) -> FmtResult: # There will only be one target+field_set, but we iterate # to satisfy how fmt expects that there could be more than one. @@ -98,5 +98,5 @@ async def generate_sample_conf_via_fmt( def rules(): return [ *collect_rules(), - UnionRule(FmtRequest, GenerateSampleConfViaFmtRequest), + UnionRule(FmtTargetsRequest, GenerateSampleConfViaFmtTargetsRequest), ] From bd08c58b68346f9f4b7cb1e37caaf71d4c6f520f Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 00:34:54 -0600 Subject: [PATCH 10/16] add dependent rules for sample_conf pants plugin --- pants-plugins/sample_conf/rules.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index bf10c01306..09e2eaaf68 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -14,6 +14,7 @@ from dataclasses import dataclass from pants.backend.python.target_types import EntryPoint +from pants.backend.python.util_rules import pex, pex_from_targets from pants.backend.python.util_rules.pex import ( VenvPex, VenvPexProcess, @@ -99,4 +100,6 @@ def rules(): return [ *collect_rules(), UnionRule(FmtTargetsRequest, GenerateSampleConfViaFmtTargetsRequest), + *pex.rules(), + *pex_from_targets.rules(), ] From 8c0b8cc07f2b906a8a8a3147415ae143070f991c Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 01:41:53 -0600 Subject: [PATCH 11/16] add tests for pants-plugins/sample_conf --- pants-plugins/sample_conf/BUILD | 4 + pants-plugins/sample_conf/rules.py | 12 +- pants-plugins/sample_conf/rules_test.py | 156 ++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 pants-plugins/sample_conf/rules_test.py diff --git a/pants-plugins/sample_conf/BUILD b/pants-plugins/sample_conf/BUILD index db46e8d6c9..0eea8b1cf1 100644 --- a/pants-plugins/sample_conf/BUILD +++ b/pants-plugins/sample_conf/BUILD @@ -1 +1,5 @@ python_sources() + +python_tests( + name="tests", +) diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index 09e2eaaf68..09d99ac64c 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -37,6 +37,8 @@ from sample_conf.target_types import SampleConfSourceField +# these constants are also used in the tests. +SCRIPT_DIR = "tools" SCRIPT = "config_gen" @@ -53,7 +55,7 @@ class GenerateSampleConfViaFmtTargetsRequest(FmtTargetsRequest): @rule( - desc="Update conf/st2.conf.sample with tools/config_gen.py", + desc=f"Update conf/st2.conf.sample with {SCRIPT_DIR}/{SCRIPT}.py", level=LogLevel.DEBUG, ) async def generate_sample_conf_via_fmt( @@ -68,7 +70,13 @@ async def generate_sample_conf_via_fmt( pex = await Get( VenvPex, PexFromTargetsRequest( - [Address("tools", target_name="tools", relative_file_path=f"{SCRIPT}.py")], + [ + Address( + SCRIPT_DIR, + target_name=SCRIPT_DIR, + relative_file_path=f"{SCRIPT}.py", + ) + ], output_filename=f"{SCRIPT}.pex", internal_only=True, main=EntryPoint(SCRIPT), diff --git a/pants-plugins/sample_conf/rules_test.py b/pants-plugins/sample_conf/rules_test.py new file mode 100644 index 0000000000..4f949975dc --- /dev/null +++ b/pants-plugins/sample_conf/rules_test.py @@ -0,0 +1,156 @@ +# Copyright 2022 The StackStorm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import os + +import pytest + +from pants.backend.python import target_types_rules +from pants.backend.python.target_types import PythonSourcesGeneratorTarget + +from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest +from pants.engine.addresses import Address +from pants.engine.fs import CreateDigest, Digest, FileContent, Snapshot +from pants.engine.target import Target +from pants.core.goals.fmt import FmtResult +from pants.testutil.rule_runner import QueryRule, RuleRunner + +from .rules import ( + SCRIPT, + SCRIPT_DIR, + GenerateSampleConfFieldSet, + GenerateSampleConfViaFmtTargetsRequest, + rules as sample_conf_rules, +) +from .target_types import SampleConf + + +@pytest.fixture +def rule_runner() -> RuleRunner: + return RuleRunner( + rules=[ + *sample_conf_rules(), + *target_types_rules.rules(), + QueryRule(FmtResult, (GenerateSampleConfViaFmtTargetsRequest,)), + QueryRule(SourceFiles, (SourceFilesRequest,)), + ], + target_types=[SampleConf, PythonSourcesGeneratorTarget], + ) + + +def run_st2_generate_sample_conf( + rule_runner: RuleRunner, + targets: list[Target], + *, + extra_args: list[str] | None = None, +) -> FmtResult: + rule_runner.set_options( + [ + "--backend-packages=sample_conf", + f"--source-root-patterns=/{SCRIPT_DIR}", + *(extra_args or ()), + ], + env_inherit={"PATH", "PYENV_ROOT", "HOME"}, + ) + field_sets = [GenerateSampleConfFieldSet.create(tgt) for tgt in targets] + input_sources = rule_runner.request( + SourceFiles, + [ + SourceFilesRequest(field_set.source for field_set in field_sets), + ], + ) + fmt_result = rule_runner.request( + FmtResult, + [ + GenerateSampleConfViaFmtTargetsRequest( + field_sets, snapshot=input_sources.snapshot + ), + ], + ) + return fmt_result + + +# copied from pantsbuild/pants.git/src/python/pants/backend/python/lint/black/rules_integration_test.py +def get_snapshot(rule_runner: RuleRunner, source_files: dict[str, str]) -> Snapshot: + files = [ + FileContent(path, content.encode()) for path, content in source_files.items() + ] + digest = rule_runner.request(Digest, [CreateDigest(files)]) + return rule_runner.request(Snapshot, [digest]) + + +# add dummy script at tools/config_gen.py that the test can load. +SCRIPT_PY = """ +def main(): + sample_conf_text = "{sample_conf_text}" + print(sample_conf_text) + + +if __name__ == "__main__": + main() +""" + + +def write_files( + sample_conf_dir: str, sample_conf_file: str, before: str, after: str, rule_runner: RuleRunner +) -> None: + files = { + f"{sample_conf_dir}/{sample_conf_file}": before, + f"{sample_conf_dir}/BUILD": f"sample_conf(name='t', source='{sample_conf_file}')", + # add in the target that's hard-coded in the generate_sample_conf_via_fmt rue + f"{SCRIPT_DIR}/{SCRIPT}.py": SCRIPT_PY.format(sample_conf_text=after), + f"{SCRIPT_DIR}/__init__.py": "", + f"{SCRIPT_DIR}/BUILD": "python_sources()", + } + + rule_runner.write_files(files) + + +def test_changed(rule_runner: RuleRunner) -> None: + write_files( + sample_conf_dir="my_dir", + sample_conf_file="dummy.conf", + before="BEFORE", + after="AFTER", + rule_runner=rule_runner, + ) + + tgt = rule_runner.get_target( + Address("my_dir", target_name="t", relative_file_path="dummy.conf") + ) + fmt_result = run_st2_generate_sample_conf(rule_runner, [tgt]) + assert fmt_result.output == get_snapshot( + rule_runner, {"my_dir/dummy.conf": "AFTER\n"} + ) + assert fmt_result.did_change is True + + +def test_unchanged(rule_runner: RuleRunner) -> None: + write_files( + sample_conf_dir="my_dir", + sample_conf_file="dummy.conf", + before="AFTER\n", + after="AFTER", # print() adds a newline + rule_runner=rule_runner, + ) + + tgt = rule_runner.get_target( + Address("my_dir", target_name="t", relative_file_path="dummy.conf") + ) + fmt_result = run_st2_generate_sample_conf(rule_runner, [tgt]) + assert fmt_result.output == get_snapshot( + rule_runner, {"my_dir/dummy.conf": "AFTER\n"} + ) + assert fmt_result.did_change is False From 684c13b692a2c9c4b6cb6a963dad9cbfd5c732c5 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 02:04:00 -0600 Subject: [PATCH 12/16] add description of sample_conf plugin to pants-plugins/README.md --- pants-plugins/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pants-plugins/README.md b/pants-plugins/README.md index 29289c32ae..d7b5729758 100644 --- a/pants-plugins/README.md +++ b/pants-plugins/README.md @@ -10,6 +10,7 @@ To see available goals, do "./pants help goals" and "./pants help $goal". These StackStorm-specific plugins are probably only useful for the st2 repo. - `api_spec` +- `sample_conf` - `schemas` ### `api_spec` plugin @@ -25,6 +26,15 @@ This plugin also wires up pants so that the `lint` goal runs additional api spec validation on `st2common/st2common/openapi.yaml` with something like `./pants lint st2common/st2common/openapi.yaml`. +### `sample_conf` plugin + +This plugin wires up pants to make sure `conf/st2.conf.sample` gets +regenerated whenever the source files change. Now, whenever someone runs +the `fmt` goal (eg `./pants fmt conf/st2.conf.sample`), the sample will +be regenerated if any of the files used to generate it have changed. +Also, running the `lint` goal will fail if the sample needs to be +regenerated. + ### `schemas` plugin This plugin wires up pants to make sure `contrib/schemas/*.json` gets From a83e568985a054d11146ec6ce81ea541f0fe35a5 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 02:34:16 -0600 Subject: [PATCH 13/16] reformat with black --- BUILD | 6 +++--- pants-plugins/sample_conf/rules_test.py | 6 +++++- tools/config_gen.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/BUILD b/BUILD index f7b31b622a..5328f1b40e 100644 --- a/BUILD +++ b/BUILD @@ -31,12 +31,12 @@ python_requirements( "//:reqs#zake", ] }, - # make sure anything that uses st2-auth-ldap gets the st2auth constant - "st2-auth-ldap": { + # make sure anything that uses st2-auth-ldap gets the st2auth constant + "st2-auth-ldap": { "dependencies": [ "st2auth/st2auth/backends/constants.py", ] - } + }, }, ) diff --git a/pants-plugins/sample_conf/rules_test.py b/pants-plugins/sample_conf/rules_test.py index 4f949975dc..500423b0a6 100644 --- a/pants-plugins/sample_conf/rules_test.py +++ b/pants-plugins/sample_conf/rules_test.py @@ -104,7 +104,11 @@ def main(): def write_files( - sample_conf_dir: str, sample_conf_file: str, before: str, after: str, rule_runner: RuleRunner + sample_conf_dir: str, + sample_conf_file: str, + before: str, + after: str, + rule_runner: RuleRunner, ) -> None: files = { f"{sample_conf_dir}/{sample_conf_file}": before, diff --git a/tools/config_gen.py b/tools/config_gen.py index c86360173b..92d1093eab 100755 --- a/tools/config_gen.py +++ b/tools/config_gen.py @@ -25,7 +25,7 @@ CONFIGS = [ - # this is duplicated in conf/BUILD + # this is duplicated in tools/BUILD # TODO: replace this with a heuristic that searches for config.py # maybe with an exclude list (eg st2tests.config st2client) # grep -rl 'def register_opts(ignore_errors=False):' st2* From fbff9a3e32805f737f1cdf506e19c7c290f10c01 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 02:04:43 -0600 Subject: [PATCH 14/16] update changelog entry --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b5771cf182..b19d3c0dce 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,7 +14,7 @@ Added working on StackStorm, improve our security posture, and improve CI reliability thanks in part to pants' use of PEX lockfiles. This is not a user-facing addition. #5778 #5789 #5817 #5795 #5830 #5833 #5834 #5841 #5840 #5838 #5842 #5837 #5849 #5850 - #5846 #5853 #5848 #5847 #5858 #5857 + #5846 #5853 #5848 #5847 #5858 #5857 #5860 Contributed by @cognifloyd * Added a joint index to solve the problem of slow mongo queries for scheduled executions. #5805 From 0b69247b701fe5507702bac18f01772ab3869d93 Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 11:08:10 -0600 Subject: [PATCH 15/16] update pants-plugins/sample_conf copyright to 2023 --- pants-plugins/sample_conf/register.py | 2 +- pants-plugins/sample_conf/rules.py | 2 +- pants-plugins/sample_conf/rules_test.py | 2 +- pants-plugins/sample_conf/target_types.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pants-plugins/sample_conf/register.py b/pants-plugins/sample_conf/register.py index 284c375c37..522f745fb4 100644 --- a/pants-plugins/sample_conf/register.py +++ b/pants-plugins/sample_conf/register.py @@ -1,4 +1,4 @@ -# Copyright 2022 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pants-plugins/sample_conf/rules.py b/pants-plugins/sample_conf/rules.py index 09d99ac64c..ccc51aac81 100644 --- a/pants-plugins/sample_conf/rules.py +++ b/pants-plugins/sample_conf/rules.py @@ -1,4 +1,4 @@ -# Copyright 2021 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pants-plugins/sample_conf/rules_test.py b/pants-plugins/sample_conf/rules_test.py index 500423b0a6..ace7ef4f06 100644 --- a/pants-plugins/sample_conf/rules_test.py +++ b/pants-plugins/sample_conf/rules_test.py @@ -1,4 +1,4 @@ -# Copyright 2022 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/pants-plugins/sample_conf/target_types.py b/pants-plugins/sample_conf/target_types.py index 420e6d0a6e..b7f13e12b6 100644 --- a/pants-plugins/sample_conf/target_types.py +++ b/pants-plugins/sample_conf/target_types.py @@ -1,4 +1,4 @@ -# Copyright 2022 The StackStorm Authors. +# Copyright 2023 The StackStorm Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From a54e76e62236d1d3ad82301b3f9e0c143b34ff2f Mon Sep 17 00:00:00 2001 From: Jacob Floyd Date: Thu, 5 Jan 2023 11:10:33 -0600 Subject: [PATCH 16/16] satisfy flake8 --- pants-plugins/sample_conf/rules_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pants-plugins/sample_conf/rules_test.py b/pants-plugins/sample_conf/rules_test.py index ace7ef4f06..4986e7b905 100644 --- a/pants-plugins/sample_conf/rules_test.py +++ b/pants-plugins/sample_conf/rules_test.py @@ -13,8 +13,6 @@ # limitations under the License. from __future__ import annotations -import os - import pytest from pants.backend.python import target_types_rules