From 868c1fae3bb2fa85df734905aa38b33dc37c9b47 Mon Sep 17 00:00:00 2001 From: George Vauter Date: Thu, 22 Aug 2024 08:36:25 -0400 Subject: [PATCH] feat: adding init command to entrypoints (#326) * feat: make git arguments optional to support entrypoints that do not interact with git * feat: adding init command entrypoint * feat: adds logic to create provider cicd files for github Signed-off-by: George Vauter * feat: adding unit tests for init command Signed-off-by: George Vauter * fix: add license header and module docstring Signed-off-by: George Vauter * feat: add templates directory Signed-off-by: George Vauter * feat: add init command to poetry scripts Signed-off-by: George Vauter * fix: updating workflow templates Signed-off-by: George Vauter * fix: revert entrypoint_base and do not subclass init entrypoint Signed-off-by: George Vauter * fix: lists should be sorted before assertion Signed-off-by: George Vauter * fix: use importlib_resources backport for compatability with older python versions Signed-off-by: George Vauter * fix: run workflows on push instead of pull_request Signed-off-by: George Vauter * fix: check return code from call to compliance-trestle Signed-off-by: George Vauter --------- Signed-off-by: George Vauter --- docs/contributing.md | 1 + docs/troubleshooting.md | 1 + poetry.lock | 12 +- pyproject.toml | 2 + tests/conftest.py | 7 + tests/testutils.py | 8 +- tests/trestlebot/entrypoints/test_init.py | 174 ++++++++++++++ trestlebot/const.py | 5 + trestlebot/entrypoints/init.py | 223 ++++++++++++++++++ .../github/trestlebot-autosync-catalog.yml | 30 +++ .../github/trestlebot-autosync-profile.yml | 30 +++ ...trestlebot-create-component-definition.yml | 49 ++++ .../github/trestlebot-rules-transform.yml | 45 ++++ 13 files changed, 580 insertions(+), 7 deletions(-) create mode 100644 docs/contributing.md create mode 100644 docs/troubleshooting.md create mode 100644 tests/trestlebot/entrypoints/test_init.py create mode 100644 trestlebot/entrypoints/init.py create mode 100644 trestlebot/entrypoints/templates/github/trestlebot-autosync-catalog.yml create mode 100644 trestlebot/entrypoints/templates/github/trestlebot-autosync-profile.yml create mode 100644 trestlebot/entrypoints/templates/github/trestlebot-create-component-definition.yml create mode 100644 trestlebot/entrypoints/templates/github/trestlebot-rules-transform.yml diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..66e8e2b4 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1 @@ +{!CONTRIBUTING.md!} diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..c72d7682 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1 @@ +{!TROUBLESHOOTING.md!} diff --git a/poetry.lock b/poetry.lock index dd9fbe91..c11c0445 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1029,21 +1029,21 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p [[package]] name = "importlib-resources" -version = "6.4.0" +version = "6.4.3" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, - {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, + {file = "importlib_resources-6.4.3-py3-none-any.whl", hash = "sha256:2d6dfe3b9e055f72495c2085890837fc8c758984e209115c8792bddcb762cd93"}, + {file = "importlib_resources-6.4.3.tar.gz", hash = "sha256:4a202b9b9d38563b46da59221d77bb73862ab5d79d461307bcb826d725448b98"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "inflect" @@ -2585,4 +2585,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "d5854f47625b746127786a796976bd9ff4b0648108074e1b9ee8e9918b1ae86b" +content-hash = "4a8eea1af39dae299c2000af88cfd50760a2a762498816238c5a0b8287fb7e78" diff --git a/pyproject.toml b/pyproject.toml index 96e86db1..ee5ab3f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ repository = 'https://github.com/RedHatProductSecurity/trestle-bot' [tool.poetry.scripts] +trestlebot-init = "trestlebot.entrypoints.init:main" trestlebot-autosync = "trestlebot.entrypoints.autosync:main" trestlebot-rules-transform = "trestlebot.entrypoints.rule_transform:main" trestlebot-create-cd = "trestlebot.entrypoints.create_cd:main" @@ -33,6 +34,7 @@ github3-py = "^4.0.1" python-gitlab = "^4.2.0" ruamel-yaml = "^0.18.5" pydantic = "^2.0.0" +importlib-resources = "^6.4.3" [tool.poetry.group.dev.dependencies] flake8 = "^7.0.0" diff --git a/tests/conftest.py b/tests/conftest.py index b170dcf2..d935a5b9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -44,6 +44,13 @@ def tmp_repo() -> YieldFixture[Tuple[str, Repo]]: clean(tmpdir, repo) +@pytest.fixture(scope="function") +def tmp_init_dir() -> YieldFixture[str]: + tmpdir = tempfile.mkdtemp(prefix=_TEST_PREFIX) + yield tmpdir + clean(tmpdir) + + @pytest.fixture(scope="function") def tmp_trestle_dir() -> YieldFixture[str]: """Create an initialized temporary trestle directory""" diff --git a/tests/testutils.py b/tests/testutils.py index b25dd48e..5fdc5811 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -45,7 +45,7 @@ def configure_test_logger(level: int = logging.INFO) -> None: configure_logger(level=level, propagate=True) -def clean(repo_path: str, repo: Optional[Repo]) -> None: +def clean(repo_path: str, repo: Optional[Repo] = None) -> None: """Clean up the temporary Git repository.""" if repo is not None: repo.close() @@ -270,6 +270,12 @@ def setup_rules_view( load_from_yaml(comp_dir, "test_complete_rule_multiple_controls") +def setup_for_init(tmp_init_dir: pathlib.Path) -> None: + """Creates an empty git repo""" + git_dir: pathlib.Path = tmp_init_dir.joinpath(pathlib.Path(".git")) + git_dir.mkdir() + + def replace_string_in_file(file_path: str, old_string: str, new_string: str) -> None: """Replace a string in a file.""" # Read the content of the file diff --git a/tests/trestlebot/entrypoints/test_init.py b/tests/trestlebot/entrypoints/test_init.py new file mode 100644 index 00000000..e23406dc --- /dev/null +++ b/tests/trestlebot/entrypoints/test_init.py @@ -0,0 +1,174 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Red Hat, Inc. + +"""Test for Init CLI entrypoint""" +import argparse +import logging +import pathlib +from typing import Dict +from unittest.mock import Mock, patch + +import pytest +from trestle.common.const import TRESTLE_CONFIG_DIR, TRESTLE_KEEP_FILE +from trestle.common.file_utils import is_hidden + +from tests.testutils import args_dict_to_list, configure_test_logger, setup_for_init +from trestlebot.const import GITHUB, TRESTLEBOT_CONFIG_DIR, TRESTLEBOT_KEEP_FILE +from trestlebot.entrypoints.init import InitEntrypoint +from trestlebot.entrypoints.init import main as cli_main +from trestlebot.tasks.authored import types as model_types + + +OSCAL_MODEL_SSP = model_types.AuthoredType.SSP.value +OSCAL_MODEL_COMPDEF = model_types.AuthoredType.COMPDEF.value + + +@pytest.fixture +def args_dict() -> Dict[str, str]: + return { + "working-dir": ".", + "provider": GITHUB, + "oscal-model": OSCAL_MODEL_COMPDEF, + } + + +@patch( + "trestlebot.entrypoints.log.configure_logger", + Mock(side_effect=configure_test_logger), +) +def test_init_fails_if_trestlebot_dir_exists( + tmp_init_dir: str, args_dict: Dict[str, str], caplog: pytest.LogCaptureFixture +) -> None: + """Trestlebot init should fail if .trestlebot directory already exists""" + setup_for_init(pathlib.Path(tmp_init_dir)) + + args_dict["working-dir"] = tmp_init_dir + + # Manulaly create .trestlebot dir so it already exists + trestlebot_dir = pathlib.Path(tmp_init_dir) / pathlib.Path(TRESTLEBOT_CONFIG_DIR) + trestlebot_dir.mkdir() + + with patch("sys.argv", ["trestlebot", *args_dict_to_list(args_dict)]): + with pytest.raises(SystemExit, match="1"): + cli_main() + + assert any( + record.levelno == logging.ERROR + and f"Initialization failed. Found existing {TRESTLEBOT_CONFIG_DIR} directory in" + in record.message + for record in caplog.records + ) + + +@patch( + "trestlebot.entrypoints.log.configure_logger", + Mock(side_effect=configure_test_logger), +) +def test_init_if_not_git_repo( + tmp_init_dir: str, args_dict: Dict[str, str], caplog: pytest.LogCaptureFixture +) -> None: + """Test init fails if not in git repo directory""" + args_dict["working-dir"] = tmp_init_dir + with patch("sys.argv", ["trestlebot", *args_dict_to_list(args_dict)]): + with pytest.raises(SystemExit, match="1"): + cli_main() + + assert any( + record.levelno == logging.ERROR + and f"Initialization failed. Given directory {tmp_init_dir} is not a Git repository." + in record.message + for record in caplog.records + ) + + +@patch( + "trestlebot.entrypoints.log.configure_logger", + Mock(side_effect=configure_test_logger), +) +def test_init_ssp_github( + tmp_init_dir: str, args_dict: Dict[str, str], caplog: pytest.LogCaptureFixture +) -> None: + """Tests for expected init command directories and files""" + args_dict["working-dir"] = tmp_init_dir + args_dict["oscal-model"] = OSCAL_MODEL_SSP + args_dict["provider"] = GITHUB + setup_for_init(pathlib.Path(tmp_init_dir)) + with patch("sys.argv", ["trestlebot", *args_dict_to_list(args_dict)]): + with pytest.raises(SystemExit, match="0"): + cli_main() + + # .keep file should exist in .trestlebot repo + tmp_dir = pathlib.Path(tmp_init_dir) + trestlebot_dir = tmp_dir / pathlib.Path(TRESTLEBOT_CONFIG_DIR) + keep_file = trestlebot_dir / pathlib.Path(TRESTLEBOT_KEEP_FILE) + assert keep_file.exists() is True + + # directories for ssp model should exist + model_dirs = [d.name for d in tmp_dir.iterdir() if not is_hidden(d)] + expected = InitEntrypoint.MODEL_DIRS[args_dict["oscal-model"]] + assert sorted(model_dirs) == sorted(expected) + + # directories for github workflows should exist + workflow_dir = tmp_dir.joinpath(".github/workflows") + workflow_files = [f.name for f in workflow_dir.iterdir()] + expected = InitEntrypoint.PROVIDER_TEMPLATES[args_dict["provider"]][ + args_dict["oscal-model"] + ] + assert sorted(workflow_files) == sorted(expected) + + assert any( + record.levelno == logging.INFO + and f"Initialized trestlebot project successfully in {tmp_init_dir}" + in record.message + for record in caplog.records + ) + + +@patch( + "trestlebot.entrypoints.log.configure_logger", + Mock(side_effect=configure_test_logger), +) +def test_init_compdef_github( + tmp_init_dir: str, args_dict: Dict[str, str], caplog: pytest.LogCaptureFixture +) -> None: + """Tests for expected init command directories and files""" + args_dict["working-dir"] = tmp_init_dir + args_dict["oscal-model"] = model_types.AuthoredType.COMPDEF.value + args_dict["provider"] = GITHUB + setup_for_init(pathlib.Path(tmp_init_dir)) + + with patch("sys.argv", ["trestlebot", *args_dict_to_list(args_dict)]): + with pytest.raises(SystemExit, match="0"): + cli_main() + + # directories for compdef model should exist + tmp_dir = pathlib.Path(tmp_init_dir) + model_dirs = [d.name for d in tmp_dir.iterdir() if not is_hidden(d)] + expected = InitEntrypoint.MODEL_DIRS[args_dict["oscal-model"]] + assert sorted(model_dirs) == sorted(expected) + + # directories for github workflows should exist + workflow_dir = tmp_dir.joinpath(".github/workflows") + workflow_files = [f.name for f in workflow_dir.iterdir()] + expected = InitEntrypoint.PROVIDER_TEMPLATES[args_dict["provider"]][ + args_dict["oscal-model"] + ] + assert sorted(workflow_files) == sorted(expected) + + assert any( + record.levelno == logging.INFO + and f"Initialized trestlebot project successfully in {tmp_init_dir}" + in record.message + for record in caplog.records + ) + + +def test_call_trestle_init(tmp_init_dir: str) -> None: + """Tests for expected results of calling trestle init""" + parser = argparse.ArgumentParser() + args = argparse.Namespace(verbose=0, working_dir=tmp_init_dir) + InitEntrypoint(parser=parser)._call_trestle_init(args) + tmp_dir = pathlib.Path(tmp_init_dir) + trestle_dir = tmp_dir / pathlib.Path(TRESTLE_CONFIG_DIR) + keep_file = trestle_dir / pathlib.Path(TRESTLE_KEEP_FILE) + assert keep_file.exists() is True diff --git a/trestlebot/const.py b/trestlebot/const.py index 89ccf1f3..a70b9c73 100644 --- a/trestlebot/const.py +++ b/trestlebot/const.py @@ -52,3 +52,8 @@ GITHUB = "github" GITLAB = "gitlab" GITHUB_SERVER_URL = "https://github.com" +GITHUB_WORKFLOWS_DIR = ".github/workflows" + +# Trestlebot init constants +TRESTLEBOT_CONFIG_DIR = ".trestlebot" +TRESTLEBOT_KEEP_FILE = ".keep" diff --git a/trestlebot/entrypoints/init.py b/trestlebot/entrypoints/init.py new file mode 100644 index 00000000..de6b3107 --- /dev/null +++ b/trestlebot/entrypoints/init.py @@ -0,0 +1,223 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Red Hat, Inc. + + +""" +This module provides the `init` entrypoint for trestlebot. + +The init command is used for creating a new trestlebot workspace. The command +will do the following: + - create the .trestlebot directory for trestlebot related files + - create files for use with the specified CICD provider + - create the appropriate directories for the specified OSCAL model + - run the trestle init command to generate the needed compliance-trestle directories +""" + +import argparse +import logging +import pathlib +import shutil +import sys +import traceback + +import importlib_resources +from trestle.common import file_utils +from trestle.core.commands.common.return_codes import CmdReturnCodes +from trestle.core.commands.init import InitCmd + +from trestlebot.const import ( + ERROR_EXIT_CODE, + GITHUB, + GITHUB_WORKFLOWS_DIR, + GITLAB, + SUCCESS_EXIT_CODE, + TRESTLEBOT_CONFIG_DIR, + TRESTLEBOT_KEEP_FILE, +) +from trestlebot.entrypoints.entrypoint_base import handle_exception +from trestlebot.entrypoints.log import set_log_level_from_args +from trestlebot.tasks.authored import types as model_types + + +logger = logging.getLogger(__name__) +logging.getLogger("trestle.core.commands.init").setLevel("CRITICAL") + +OSCAL_MODEL_SSP = model_types.AuthoredType.SSP.value +OSCAL_MODEL_COMPDEF = model_types.AuthoredType.COMPDEF.value + + +class InitEntrypoint: + """Entrypoint for the init command.""" + + TEMPLATES_MODULE = "trestlebot.entrypoints.templates" + SUPPORTED_PROVIDERS = [GITHUB, GITLAB] + SUPPORTED_MODELS = [ + OSCAL_MODEL_SSP, + OSCAL_MODEL_COMPDEF, + ] + PROVIDER_TEMPLATES = { + GITHUB: { + OSCAL_MODEL_SSP: [ + "trestlebot-autosync-catalog.yml", + "trestlebot-autosync-profile.yml", + ], + OSCAL_MODEL_COMPDEF: [ + "trestlebot-create-component-definition.yml", + "trestlebot-autosync-catalog.yml", + "trestlebot-autosync-profile.yml", + "trestlebot-rules-transform.yml", + ], + } + } + MODEL_DIRS = { + OSCAL_MODEL_SSP: [ + "system-security-plans", + "component-definitions", + "catalogs", + "profiles", + ], + OSCAL_MODEL_COMPDEF: [ + "component-definitions", + "catalogs", + "profiles", + "rules", + ], + } + + def __init__(self, parser: argparse.ArgumentParser) -> None: + self.parser: argparse.ArgumentParser = parser + self.setup_init_arguments() + + def setup_init_arguments(self) -> None: + """Setup arguments for the init entrypoint.""" + self.parser.add_argument( + "-v", + "--verbose", + help="Display verbose output", + action="count", + default=0, + ) + self.parser.add_argument( + "--working-dir", + type=str, + required=False, + default=".", + help="Working directory wit git repository", + ) + self.parser.add_argument( + "--provider", + required=False, + type=str, + choices=self.SUPPORTED_PROVIDERS, + default="github", + help="Name of CI/CD provider", + ) + self.parser.add_argument( + "--oscal-model", + required=True, + type=str, + choices=self.SUPPORTED_MODELS, + help="OSCAL model type to run tasks on.", + ) + + def _call_trestle_init(self, args: argparse.Namespace) -> None: + """Call compliance-trestle to initialize workspace""" + trestle_args = argparse.Namespace( + verbose=args.verbose, + trestle_root=pathlib.Path(args.working_dir), + full=False, + govdocs=True, + local=False, + ) + return_code = InitCmd()._run(trestle_args) + if return_code == CmdReturnCodes.SUCCESS.value: + logger.debug("Initialized trestle project successfully") + else: + logger.error( + f"Initialization failed. Unexpted trestle error: {CmdReturnCodes(return_code).name}" + ) + sys.exit(ERROR_EXIT_CODE) + + def _create_directories(self, args: argparse.Namespace) -> None: + """Initialize trestlebot directories""" + + root = pathlib.Path(args.working_dir) + model_dirs = self.MODEL_DIRS[args.oscal_model] + + for model_dir in model_dirs: + directory = root.joinpath(pathlib.Path(model_dir)) + directory.mkdir(exist_ok=True) + keep_file = directory.joinpath(pathlib.Path(TRESTLEBOT_KEEP_FILE)) + file_utils.make_hidden_file(keep_file) + + def _copy_provider_files(self, args: argparse.Namespace) -> None: + """Copy the CICD provider files to the new trestlebot workspace""" + if args.provider == GITHUB: + provider_dir = pathlib.Path(args.working_dir).joinpath( + pathlib.Path(GITHUB_WORKFLOWS_DIR) + ) + provider_dir.mkdir(parents=True, exist_ok=True) + + templates_dir = importlib_resources.files( + f"{self.TEMPLATES_MODULE}.{args.provider}" + ) + for template_file in self.PROVIDER_TEMPLATES[args.provider][args.oscal_model]: + template_path = templates_dir.joinpath(template_file) + dest_path = provider_dir.joinpath(pathlib.Path(template_file)) + shutil.copyfile(str(template_path), dest_path) + + def run(self, args: argparse.Namespace) -> None: + """Run the init entrypoint""" + exit_code: int = SUCCESS_EXIT_CODE + try: + set_log_level_from_args(args) + root: pathlib.Path = pathlib.Path(args.working_dir) + if not root.exists() or not root.is_dir(): + logger.error( + f"Initialization failed. Given directory {root} does not exist." + ) + sys.exit(ERROR_EXIT_CODE) + + git_dir: pathlib.Path = root.joinpath(pathlib.Path(".git")) + if not git_dir.exists(): # TODO: add --force flag to bypass + logger.error( + f"Initialization failed. Given directory {root} is not a Git repository." + ) + sys.exit(ERROR_EXIT_CODE) + + trestlebot_dir = root.joinpath(pathlib.Path(TRESTLEBOT_CONFIG_DIR)) + if trestlebot_dir.exists(): + logger.error( + f"Initialization failed. Found existing {TRESTLEBOT_CONFIG_DIR} directory in {root}" + ) + sys.exit(ERROR_EXIT_CODE) + else: + trestlebot_dir.mkdir(parents=True, exist_ok=False) + keep_file = trestlebot_dir / TRESTLEBOT_KEEP_FILE + file_utils.make_hidden_file(keep_file) + + self._create_directories(args) + self._call_trestle_init(args) + self._copy_provider_files(args) + + except Exception as e: + traceback_str = traceback.format_exc() + exit_code = handle_exception(e, traceback_str) + + logger.info(f"Initialized trestlebot project successfully in {root}") + sys.exit(exit_code) + + +def main() -> None: + """Run the init entrypoint CLI.""" + parser = argparse.ArgumentParser( + description="Workflow automation bot for compliance-trestle" + ) + + init = InitEntrypoint(parser=parser) + args = parser.parse_args() + init.run(args) + + +if __name__ == "__main__": + main() diff --git a/trestlebot/entrypoints/templates/github/trestlebot-autosync-catalog.yml b/trestlebot/entrypoints/templates/github/trestlebot-autosync-catalog.yml new file mode 100644 index 00000000..595896b3 --- /dev/null +++ b/trestlebot/entrypoints/templates/github/trestlebot-autosync-catalog.yml @@ -0,0 +1,30 @@ +--- +name: Trestle-bot autosync catalog updates + +on: + push: + branches: + - main + paths: + - 'catalogs/**' + - 'markdown/catalogs/**' + +jobs: + autosync: + name: Autosync catalog content + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - name: Run autosync + id: autosync + uses: RedHatProductSecurity/trestle-bot/actions/autosync@main + with: + markdown_path: "markdown/catalogs" + oscal_model: "catalog" + file_pattern: "*.json,markdown/*" + branch: ${{ github.head_ref }} diff --git a/trestlebot/entrypoints/templates/github/trestlebot-autosync-profile.yml b/trestlebot/entrypoints/templates/github/trestlebot-autosync-profile.yml new file mode 100644 index 00000000..9854b011 --- /dev/null +++ b/trestlebot/entrypoints/templates/github/trestlebot-autosync-profile.yml @@ -0,0 +1,30 @@ +--- +name: Trestle-bot autosync profile updates + +on: + push: + branches: + - main + paths: + - 'profiles/**' + - 'markdown/profiles/**' + +jobs: + autosync: + name: Autosync profile content + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - name: Run autosync + id: autosync + uses: RedHatProductSecurity/trestle-bot/actions/autosync@main + with: + markdown_path: "markdown/profiles" + oscal_model: "profile" + file_pattern: "*.json,markdown/*" + branch: ${{ github.head_ref }} diff --git a/trestlebot/entrypoints/templates/github/trestlebot-create-component-definition.yml b/trestlebot/entrypoints/templates/github/trestlebot-create-component-definition.yml new file mode 100644 index 00000000..e6090c17 --- /dev/null +++ b/trestlebot/entrypoints/templates/github/trestlebot-create-component-definition.yml @@ -0,0 +1,49 @@ +--- +name: Trestle-bot create component-definition + +on: + workflow_dispatch: + inputs: + profile_name: + description: Name of the Trestle profile to use for the component definition + required: true + component_definition_name: + description: Name of the component definition to create + required: true + component_title: + description: Name of the component to create in the generated component definition + required: true + component_type: + description: Type of the component (e.g. service, policy, physical, validation, etc.) + required: false + default: "service" + component_description: + description: Description of the component to create + required: true + +jobs: + create-component-definition: + name: Create component definition + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Create component definition and open pull request + id: generate-cd + uses: RedHatProductSecurity/trestle-bot/actions/create-cd@main + with: + profile_name: ${{ github.event.inputs.profile_name }} + component_definition_name: ${{ github.event.inputs.component_definition_name}} + component_title: ${{ github.event.inputs.component_title }} + component_type: ${{ github.event.inputs.component_type }} + component_description: ${{ github.event.inputs.component_description }} + markdown_path: "markdown/components" + branch: "create-component-definition-${{ github.run_id }}" + target_branch: "main" + file_pattern: "*.json,markdown/*,rules/*" + commit_message: "adds component ${{ github.event.inputs.component_title }} in ${{ github.event.inputs.component_definition_name }}" + pull_request_title: "Add component ${{ github.event.inputs.component_title }} to ${{ github.event.inputs.component_definition_name }}" + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/trestlebot/entrypoints/templates/github/trestlebot-rules-transform.yml b/trestlebot/entrypoints/templates/github/trestlebot-rules-transform.yml new file mode 100644 index 00000000..b4d8a6cd --- /dev/null +++ b/trestlebot/entrypoints/templates/github/trestlebot-rules-transform.yml @@ -0,0 +1,45 @@ +--- +name: Trestle-bot rules-transform and autosync + +on: + push: + branches: + - main + paths: + - 'profiles/**' + - 'catalogs/**' + - 'component-definitions/**' + - 'markdown/**' + - 'rules/**' + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + rules-transform-and-autosync: + name: Rules Transform and AutoSync + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: AutoSync + id: autosync + uses: RedHatProductSecurity/trestle-bot/actions/autosync@main + with: + markdown_path: "markdown/components" + oscal_model: "compdef" + file_pattern: "*.json,markdown/*" + - name: Check if rules changed + id: changes + uses: dorny/paths-filter@v3 + with: + filters: | + rules: + - 'rules/**' + - name: Rules Tranform + if: steps.changes.outputs.rules == 'true' + uses: RedHatProductSecurity/trestle-bot/actions/rules-transform@main + with: + markdown_path: "markdown" + commit_message: "Auto-transform rules [skip ci]" \ No newline at end of file