This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/main by this push:
new 1bbddbf Add license check exclude fields to the UI and related tests
1bbddbf is described below
commit 1bbddbf438a46c0d8cc387827c033a5deb523174
Author: Sean B. Palmer <[email protected]>
AuthorDate: Fri Jan 9 17:10:01 2026 +0000
Add license check exclude fields to the UI and related tests
---
atr/get/projects.py | 2 +
atr/shared/projects.py | 11 ++++++
atr/storage/writers/policy.py | 7 ++++
tests/e2e/policy/__init__.py | 16 ++++++++
tests/e2e/{sbom => policy}/conftest.py | 34 +++++++---------
tests/e2e/policy/helpers.py | 35 +++++++++++++++++
tests/e2e/policy/test_get.py | 49 +++++++++++++++++++++++
tests/e2e/policy/test_post.py | 72 ++++++++++++++++++++++++++++++++++
tests/e2e/sbom/conftest.py | 25 ++++++------
tests/e2e/sbom/helpers.py | 24 ++++++++++++
tests/e2e/sbom/test_post.py | 19 +++++----
11 files changed, 252 insertions(+), 42 deletions(-)
diff --git a/atr/get/projects.py b/atr/get/projects.py
index 085f4ff..1b44c94 100644
--- a/atr/get/projects.py
+++ b/atr/get/projects.py
@@ -330,6 +330,8 @@ def _render_compose_form(project: sql.Project) ->
htm.Element:
"project_name": project.name,
"source_artifact_paths":
"\n".join(project.policy_source_artifact_paths),
"license_check_mode": project.policy_license_check_mode,
+ "source_excludes_lightweight":
"\n".join(project.policy_source_excludes_lightweight),
+ "source_excludes_rat":
"\n".join(project.policy_source_excludes_rat),
"binary_artifact_paths":
"\n".join(project.policy_binary_artifact_paths),
"github_repository_name":
project.policy_github_repository_name or "",
"github_compose_workflow_path":
"\n".join(project.policy_github_compose_workflow_path),
diff --git a/atr/shared/projects.py b/atr/shared/projects.py
index db9951f..7eabf73 100644
--- a/atr/shared/projects.py
+++ b/atr/shared/projects.py
@@ -116,6 +116,17 @@ class ComposePolicyForm(form.Form):
"Only affects source artifacts. Lightweight checks ALWAYS RUN on
binary artifacts.",
widget=form.Widget.RADIO,
)
+ source_excludes_lightweight: str = form.label(
+ "Lightweight source excludes",
+ "Patterns using .gitignore syntax for files to exclude"
+ " from lightweight license header checks on source artifacts.",
+ widget=form.Widget.TEXTAREA,
+ )
+ source_excludes_rat: str = form.label(
+ "RAT source excludes",
+ "RAT exclude file contents for source artifacts. Used only when no
.rat-excludes file exists in the archive.",
+ widget=form.Widget.TEXTAREA,
+ )
binary_artifact_paths: str = form.label(
"Binary artifact paths",
"Paths to binary artifacts to be included in the release.",
diff --git a/atr/storage/writers/policy.py b/atr/storage/writers/policy.py
index d49b97a..b7f79c1 100644
--- a/atr/storage/writers/policy.py
+++ b/atr/storage/writers/policy.py
@@ -97,6 +97,8 @@ class CommitteeMember(CommitteeParticipant):
release_policy.source_artifact_paths =
_split_lines(form.source_artifact_paths)
release_policy.license_check_mode = form.license_check_mode #
pyright: ignore[reportAttributeAccessIssue]
+ release_policy.source_excludes_lightweight =
_split_lines_verbatim(form.source_excludes_lightweight)
+ release_policy.source_excludes_rat =
_split_lines_verbatim(form.source_excludes_rat)
release_policy.binary_artifact_paths =
_split_lines(form.binary_artifact_paths)
release_policy.github_repository_name =
form.github_repository_name.strip()
release_policy.github_compose_workflow_path =
_split_lines(form.github_compose_workflow_path)
@@ -235,3 +237,8 @@ class CommitteeMember(CommitteeParticipant):
def _split_lines(text: str) -> list[str]:
return [line.strip() for line in text.split("\n") if line.strip()]
+
+
+def _split_lines_verbatim(text: str) -> list[str]:
+ # This still excludes empty lines
+ return [line for line in text.split("\n") if line]
diff --git a/tests/e2e/policy/__init__.py b/tests/e2e/policy/__init__.py
new file mode 100644
index 0000000..13a8339
--- /dev/null
+++ b/tests/e2e/policy/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
diff --git a/tests/e2e/sbom/conftest.py b/tests/e2e/policy/conftest.py
similarity index 51%
copy from tests/e2e/sbom/conftest.py
copy to tests/e2e/policy/conftest.py
index 0801444..2918137 100644
--- a/tests/e2e/sbom/conftest.py
+++ b/tests/e2e/policy/conftest.py
@@ -17,10 +17,10 @@
from __future__ import annotations
-import pathlib
-from typing import TYPE_CHECKING, Final
+from typing import TYPE_CHECKING
import e2e.helpers as helpers
+import e2e.policy.helpers as policy_helpers
import pytest
if TYPE_CHECKING:
@@ -28,25 +28,19 @@ if TYPE_CHECKING:
from playwright.sync_api import Page
-PROJECT_NAME: Final[str] = "test"
-VERSION_NAME: Final[str] = "0.1+e2e-sbom"
-FILE_NAME: Final[str] = "apache-test-0.2.tar.gz"
-CURRENT_DIR: Final[pathlib.Path] = pathlib.Path(__file__).parent.resolve()
-
@pytest.fixture
-def page_release_with_file(page: Page) -> Generator[Page]:
+def page_project(page: Page) -> Generator[Page]:
helpers.log_in(page)
-
- helpers.delete_release_if_exists(page, PROJECT_NAME, VERSION_NAME)
-
- helpers.visit(page, f"/start/{PROJECT_NAME}")
- page.get_by_role("textbox").type(VERSION_NAME)
- page.get_by_role("button", name="Start new release").click()
- helpers.visit(page, f"/upload/{PROJECT_NAME}/{VERSION_NAME}")
-
page.locator('input[name="file_data"]').set_input_files(f"{CURRENT_DIR}/../test_files/{FILE_NAME}")
- page.get_by_role("button", name="Add files").click()
- page.wait_for_url(f"**/compose/{PROJECT_NAME}/{VERSION_NAME}")
- helpers.visit(page, f"/compose/{PROJECT_NAME}/{VERSION_NAME}")
- page.wait_for_selector("#ongoing-tasks-banner", state="hidden")
+ _clear_policy_excludes(page)
+ helpers.visit(page, policy_helpers.PROJECT_URL)
yield page
+ _clear_policy_excludes(page)
+
+
+def _clear_policy_excludes(page: Page) -> None:
+ helpers.visit(page, policy_helpers.PROJECT_URL)
+ policy_helpers.textarea_source_excludes_lightweight(page).fill("")
+ policy_helpers.textarea_source_excludes_rat(page).fill("")
+ policy_helpers.compose_form_save_button(page).click()
+ page.wait_for_load_state()
diff --git a/tests/e2e/policy/helpers.py b/tests/e2e/policy/helpers.py
new file mode 100644
index 0000000..c9bcc09
--- /dev/null
+++ b/tests/e2e/policy/helpers.py
@@ -0,0 +1,35 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 typing import Final
+
+from playwright.sync_api import Locator, Page
+
+PROJECT_NAME: Final[str] = "test"
+PROJECT_URL: Final[str] = f"/projects/{PROJECT_NAME}"
+
+
+def compose_form_save_button(page: Page) -> Locator:
+ return page.locator('form.atr-canary button[type="submit"]').first
+
+
+def textarea_source_excludes_lightweight(page: Page) -> Locator:
+ return page.locator('textarea[name="source_excludes_lightweight"]')
+
+
+def textarea_source_excludes_rat(page: Page) -> Locator:
+ return page.locator('textarea[name="source_excludes_rat"]')
diff --git a/tests/e2e/policy/test_get.py b/tests/e2e/policy/test_get.py
new file mode 100644
index 0000000..f447442
--- /dev/null
+++ b/tests/e2e/policy/test_get.py
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+import e2e.policy.helpers as helpers
+from playwright.sync_api import Page, expect
+
+
+def test_source_excludes_lightweight_initially_empty(page_project: Page) ->
None:
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ expect(textarea).to_have_value("")
+
+
+def test_source_excludes_lightweight_textarea_is_editable(page_project: Page)
-> None:
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ expect(textarea).to_be_editable()
+
+
+def test_source_excludes_lightweight_textarea_is_visible(page_project: Page)
-> None:
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ expect(textarea).to_be_visible()
+
+
+def test_source_excludes_rat_initially_empty(page_project: Page) -> None:
+ textarea = helpers.textarea_source_excludes_rat(page_project)
+ expect(textarea).to_have_value("")
+
+
+def test_source_excludes_rat_textarea_is_editable(page_project: Page) -> None:
+ textarea = helpers.textarea_source_excludes_rat(page_project)
+ expect(textarea).to_be_editable()
+
+
+def test_source_excludes_rat_textarea_is_visible(page_project: Page) -> None:
+ textarea = helpers.textarea_source_excludes_rat(page_project)
+ expect(textarea).to_be_visible()
diff --git a/tests/e2e/policy/test_post.py b/tests/e2e/policy/test_post.py
new file mode 100644
index 0000000..d3f6218
--- /dev/null
+++ b/tests/e2e/policy/test_post.py
@@ -0,0 +1,72 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+import e2e.helpers as root_helpers
+import e2e.policy.helpers as helpers
+from playwright.sync_api import Page, expect
+
+
+def test_source_excludes_lightweight_can_be_cleared(page_project: Page) ->
None:
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ textarea.fill("*.min.js")
+ helpers.compose_form_save_button(page_project).click()
+ page_project.wait_for_load_state()
+
+ root_helpers.visit(page_project, helpers.PROJECT_URL)
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ textarea.fill("")
+ helpers.compose_form_save_button(page_project).click()
+ page_project.wait_for_load_state()
+
+ root_helpers.visit(page_project, helpers.PROJECT_URL)
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ expect(textarea).to_have_value("")
+
+
+def
test_source_excludes_lightweight_preserves_internal_whitespace(page_project:
Page) -> None:
+ # TODO: There is a problem with leading and trailing whitespace in the form
+ # Anyway, this is an edge case, and perhaps normalisation would even be
better
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ textarea.fill("first\n middle with spaces \nlast")
+ helpers.compose_form_save_button(page_project).click()
+ page_project.wait_for_load_state()
+
+ root_helpers.visit(page_project, helpers.PROJECT_URL)
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ expect(textarea).to_have_value("first\n middle with spaces \nlast")
+
+
+def test_source_excludes_lightweight_value_persists(page_project: Page) ->
None:
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ textarea.fill("*.min.js\nvendor/**")
+ helpers.compose_form_save_button(page_project).click()
+ page_project.wait_for_load_state()
+
+ root_helpers.visit(page_project, helpers.PROJECT_URL)
+ textarea = helpers.textarea_source_excludes_lightweight(page_project)
+ expect(textarea).to_have_value("*.min.js\nvendor/**")
+
+
+def test_source_excludes_rat_value_persists(page_project: Page) -> None:
+ textarea = helpers.textarea_source_excludes_rat(page_project)
+ textarea.fill("third-party/**\n*.generated")
+ helpers.compose_form_save_button(page_project).click()
+ page_project.wait_for_load_state()
+
+ root_helpers.visit(page_project, helpers.PROJECT_URL)
+ textarea = helpers.textarea_source_excludes_rat(page_project)
+ expect(textarea).to_have_value("third-party/**\n*.generated")
diff --git a/tests/e2e/sbom/conftest.py b/tests/e2e/sbom/conftest.py
index 0801444..b118d95 100644
--- a/tests/e2e/sbom/conftest.py
+++ b/tests/e2e/sbom/conftest.py
@@ -17,10 +17,10 @@
from __future__ import annotations
-import pathlib
-from typing import TYPE_CHECKING, Final
+from typing import TYPE_CHECKING
import e2e.helpers as helpers
+import e2e.sbom.helpers as sbom_helpers
import pytest
if TYPE_CHECKING:
@@ -28,25 +28,22 @@ if TYPE_CHECKING:
from playwright.sync_api import Page
-PROJECT_NAME: Final[str] = "test"
-VERSION_NAME: Final[str] = "0.1+e2e-sbom"
-FILE_NAME: Final[str] = "apache-test-0.2.tar.gz"
-CURRENT_DIR: Final[pathlib.Path] = pathlib.Path(__file__).parent.resolve()
-
@pytest.fixture
def page_release_with_file(page: Page) -> Generator[Page]:
helpers.log_in(page)
- helpers.delete_release_if_exists(page, PROJECT_NAME, VERSION_NAME)
+ helpers.delete_release_if_exists(page, sbom_helpers.PROJECT_NAME,
sbom_helpers.VERSION_NAME)
- helpers.visit(page, f"/start/{PROJECT_NAME}")
- page.get_by_role("textbox").type(VERSION_NAME)
+ helpers.visit(page, f"/start/{sbom_helpers.PROJECT_NAME}")
+ page.get_by_role("textbox").type(sbom_helpers.VERSION_NAME)
page.get_by_role("button", name="Start new release").click()
- helpers.visit(page, f"/upload/{PROJECT_NAME}/{VERSION_NAME}")
-
page.locator('input[name="file_data"]').set_input_files(f"{CURRENT_DIR}/../test_files/{FILE_NAME}")
+ helpers.visit(page,
f"/upload/{sbom_helpers.PROJECT_NAME}/{sbom_helpers.VERSION_NAME}")
+ page.locator('input[name="file_data"]').set_input_files(
+ f"{sbom_helpers.CURRENT_DIR}/../test_files/{sbom_helpers.FILE_NAME}"
+ )
page.get_by_role("button", name="Add files").click()
- page.wait_for_url(f"**/compose/{PROJECT_NAME}/{VERSION_NAME}")
- helpers.visit(page, f"/compose/{PROJECT_NAME}/{VERSION_NAME}")
+
page.wait_for_url(f"**/compose/{sbom_helpers.PROJECT_NAME}/{sbom_helpers.VERSION_NAME}")
+ helpers.visit(page,
f"/compose/{sbom_helpers.PROJECT_NAME}/{sbom_helpers.VERSION_NAME}")
page.wait_for_selector("#ongoing-tasks-banner", state="hidden")
yield page
diff --git a/tests/e2e/sbom/helpers.py b/tests/e2e/sbom/helpers.py
new file mode 100644
index 0000000..d5dc363
--- /dev/null
+++ b/tests/e2e/sbom/helpers.py
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+import pathlib
+from typing import Final
+
+PROJECT_NAME: Final[str] = "test"
+VERSION_NAME: Final[str] = "0.1+e2e-sbom"
+FILE_NAME: Final[str] = "apache-test-0.2.tar.gz"
+CURRENT_DIR: Final[pathlib.Path] = pathlib.Path(__file__).parent.resolve()
diff --git a/tests/e2e/sbom/test_post.py b/tests/e2e/sbom/test_post.py
index cbc2109..207bef3 100644
--- a/tests/e2e/sbom/test_post.py
+++ b/tests/e2e/sbom/test_post.py
@@ -15,25 +15,28 @@
# specific language governing permissions and limitations
# under the License.
-import e2e.helpers as helpers # type: ignore[reportMissingImports]
-from e2e.sbom.conftest import FILE_NAME, PROJECT_NAME, VERSION_NAME # type:
ignore[reportMissingImports]
+import e2e.helpers as helpers
+import e2e.sbom.helpers as sbom_helpers
from playwright.sync_api import Page, expect
def test_sbom_generate(page_release_with_file: Page) -> None:
- # Make sure test file exists
- file_cell = page_release_with_file.get_by_role("cell", name=FILE_NAME)
+ # Make sure that the test file exists
+ file_cell = page_release_with_file.get_by_role("cell",
name=sbom_helpers.FILE_NAME)
expect(file_cell).to_be_visible()
# Generate an SBOM for the file
- helpers.visit(page_release_with_file,
f"/draft/tools/{PROJECT_NAME}/{VERSION_NAME}/{FILE_NAME}")
+ helpers.visit(
+ page_release_with_file,
+
f"/draft/tools/{sbom_helpers.PROJECT_NAME}/{sbom_helpers.VERSION_NAME}/{sbom_helpers.FILE_NAME}",
+ )
generate_button = page_release_with_file.get_by_role("button", name="SBOM")
generate_button.click()
- # Check the generated SBOM exists now
- helpers.visit(page_release_with_file,
f"/compose/{PROJECT_NAME}/{VERSION_NAME}")
+ # Check that the generated SBOM exists now
+ helpers.visit(page_release_with_file,
f"/compose/{sbom_helpers.PROJECT_NAME}/{sbom_helpers.VERSION_NAME}")
page_release_with_file.wait_for_selector("#ongoing-tasks-banner",
state="hidden")
page_release_with_file.reload()
- sbom_cell = page_release_with_file.get_by_role("cell",
name=f"{FILE_NAME}.cdx.json")
+ sbom_cell = page_release_with_file.get_by_role("cell",
name=f"{sbom_helpers.FILE_NAME}.cdx.json")
expect(sbom_cell).to_be_visible()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]