diff --git a/.github/workflows/build-deploy-non-framework-docs.yml b/.github/workflows/build-deploy-non-framework-docs.yml index 16c645df36b5..5351d4fb7c8e 100644 --- a/.github/workflows/build-deploy-non-framework-docs.yml +++ b/.github/workflows/build-deploy-non-framework-docs.yml @@ -35,11 +35,11 @@ jobs: python -m poetry install python -m pip install -e ../datasets - name: Update HTML theme options - run: python dev/update-html-themes.py + run: python -m devtool.update_html_themes - name: Build baselines docs run: ./dev/build-baseline-docs.sh - name: Build examples docs - run: python dev/build-example-docs.py + run: python -m devtool.build_example_docs - name: Build datasets docs run: ./datasets/dev/build-flwr-datasets-docs.sh - name: Deploy docs diff --git a/.github/workflows/datasets-test.yml b/.github/workflows/datasets-test.yml index cbdb5cae2e04..c433e6d76fce 100644 --- a/.github/workflows/datasets-test.yml +++ b/.github/workflows/datasets-test.yml @@ -6,12 +6,14 @@ on: - main paths: - "datasets/**" + - "dev/devtool/**" - ".github/workflows/datasets-*.yml" pull_request: branches: - main paths: - "datasets/**" + - "dev/devtool/**" - ".github/workflows/datasets-*.yml" concurrency: diff --git a/.github/workflows/framework-test.yml b/.github/workflows/framework-test.yml index f23377d3ec4a..5d3691051788 100644 --- a/.github/workflows/framework-test.yml +++ b/.github/workflows/framework-test.yml @@ -33,6 +33,7 @@ jobs: filters: | framework: - 'framework/**/*' + - 'dev/devtool/**' - '.github/workflows/framework-test.yml' ex_bench: - 'examples/**/*' diff --git a/.github/workflows/repo-check-pr-title.yml b/.github/workflows/repo-check-pr-title.yml index 67a0878318b1..4d357bd977c9 100644 --- a/.github/workflows/repo-check-pr-title.yml +++ b/.github/workflows/repo-check-pr-title.yml @@ -24,4 +24,4 @@ jobs: - name: Check PR title format env: PR_TITLE: ${{ github.event.pull_request.title }} - run: python ./dev/check_pr_title.py "$PR_TITLE" + run: PYTHONPATH=dev python -m devtool.check_pr_title "$PR_TITLE" diff --git a/dev/changelog_config.toml b/dev/changelog_config.toml index 7a4401f3ecef..74756b3ff873 100644 --- a/dev/changelog_config.toml +++ b/dev/changelog_config.toml @@ -1,4 +1,4 @@ -# Defines the PR title format to be used in the `check_pr_title.py` script +# Defines the PR title format to be used in the `devtool.check_pr_title` script # and in the `update_changelog.py` script type = ["ci", "docs", "feat", "fix", "refactor", "break"] diff --git a/dev/build-example-docs.py b/dev/devtool/build_example_docs.py similarity index 95% rename from dev/build-example-docs.py rename to dev/devtool/build_example_docs.py index ad40db7d1465..dcfb9660bc35 100644 --- a/dev/build-example-docs.py +++ b/dev/devtool/build_example_docs.py @@ -20,8 +20,8 @@ import subprocess from pathlib import Path -ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) -INDEX = os.path.join(ROOT, "examples", "docs", "source", "index.rst") +ROOT = Path(__file__).resolve().parents[2] +INDEX = ROOT / "examples" / "docs" / "source" / "index.rst" initial_text = """ Flower Examples Documentation @@ -179,14 +179,14 @@ def _copy_markdown_files(example): if file.endswith(".md"): src = os.path.join(example, file) dest = os.path.join( - ROOT, "examples", "docs", "source", os.path.basename(example) + ".md" + str(ROOT), "examples", "docs", "source", os.path.basename(example) + ".md" ) shutil.copyfile(src, dest) def _add_gh_button(example): gh_text = f'[View on GitHub](https://github.com/adap/flower/blob/main/examples/{example})' - readme_file = os.path.join(ROOT, "examples", "docs", "source", example + ".md") + readme_file = os.path.join(str(ROOT), "examples", "docs", "source", example + ".md") with open(readme_file, "r+") as f: content = f.read() if gh_text not in content: @@ -200,7 +200,7 @@ def _add_gh_button(example): def _copy_images(example): static_dir = os.path.join(example, "_static") - dest_dir = os.path.join(ROOT, "examples", "docs", "source", "_static") + dest_dir = os.path.join(str(ROOT), "examples", "docs", "source", "_static") if os.path.isdir(static_dir): for file in os.listdir(static_dir): if file.endswith((".jpg", ".png", ".jpeg")): @@ -220,10 +220,10 @@ def _add_all_entries(): def _main(): - if os.path.exists(INDEX): - os.remove(INDEX) + if INDEX.exists(): + INDEX.unlink() - with open(INDEX, "w") as index_file: + with INDEX.open("w", encoding="utf-8") as index_file: index_file.write(initial_text) examples_dir = os.path.join(ROOT, "examples") @@ -238,7 +238,7 @@ def _main(): if not _add_table_entry(example_path, "advanced", "advanced"): _add_table_entry(example_path, "", "other") - with open(INDEX, "a") as index_file: + with INDEX.open("a", encoding="utf-8") as index_file: index_file.write(categories["quickstart"]["table"]) index_file.write("\nAdvanced Examples\n-----------------\n") diff --git a/dev/check_pr_title.py b/dev/devtool/check_pr_title.py similarity index 80% rename from dev/check_pr_title.py rename to dev/devtool/check_pr_title.py index 7a9aad83379b..f4db98a72a6f 100644 --- a/dev/check_pr_title.py +++ b/dev/devtool/check_pr_title.py @@ -14,21 +14,20 @@ # ============================================================================== """Used to check a given PR title format.""" -import pathlib import re import sys import tomllib +from pathlib import Path +from typing import Any -if __name__ == "__main__": - pr_title = sys.argv[1] +def _load_config() -> dict[str, Any]: + config_path = Path(__file__).resolve().parent.parent / "changelog_config.toml" + with config_path.open("rb") as file: + return tomllib.load(file) - # Load the YAML configuration - with (pathlib.Path(__file__).parent.resolve() / "changelog_config.toml").open( - "rb" - ) as file: - config = tomllib.load(file) +def _validate_title(pr_title: str, config: dict[str, Any]) -> tuple[bool, str]: # Extract types, project, and scope from the config types = "|".join(config["type"]) projects = "|".join(config["project"]) + "|\\*" @@ -39,7 +38,7 @@ pattern_template = config["pattern_template"] pattern = pattern_template.format(types=types, projects=projects, scope=scope) - # Check for the pattern in the first argument given to the script + # Check title against pattern. match = re.search(pattern, pr_title) valid = True @@ -47,7 +46,7 @@ # This check is there to ignore dependabot PRs from title checks if pr_title.startswith("build"): - sys.exit(0) + return True, "" elif not match: valid = False else: @@ -57,8 +56,19 @@ elif match.group(2) == "*" and match.group(3) is None: valid = False error = "the cannot be '*' without using the ':skip' flag" + return valid, error + + +def main() -> None: + if len(sys.argv) < 2: + raise ValueError("Usage: python -m devtool.check_pr_title ''") + + pr_title = sys.argv[1] + config = _load_config() + valid, error = _validate_title(pr_title, config) if not valid: + types = "|".join(config["type"]) print( f"PR title `{pr_title}` is invalid, {error}.\n\nA PR title should " "be of the form:\n\n\t<PR_TYPE>(<PR_PROJECT>): <PR_SUBJECT>\n\n" @@ -73,3 +83,7 @@ "the changelog:\n\n\t`feat(framework:skip): Add new option to build CLI`\n" ) sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/dev/update-html-themes.py b/dev/devtool/update_html_themes.py similarity index 96% rename from dev/update-html-themes.py rename to dev/devtool/update_html_themes.py index 48dbed5c8a5f..d6e2658eefe4 100644 --- a/dev/update-html-themes.py +++ b/dev/devtool/update_html_themes.py @@ -19,10 +19,11 @@ import re from pathlib import Path from typing import Optional, Union + import yaml -# Change this if you want to search a different directory. -ROOT_DIR = Path(".") +REPO_ROOT = Path(__file__).resolve().parents[2] +ROOT_DIR = REPO_ROOT # Define new fields to be added to the `html_theme_options` dictionary in `conf.py`. # If no fields are needed, set to an empty dictionary. @@ -37,7 +38,7 @@ }, } -with (ROOT_DIR / "dev" / "docs-ui-config.yml").open() as f: +with (REPO_ROOT / "dev" / "docs-ui-config.yml").open(encoding="utf-8") as f: announcement = yaml.safe_load(f)["announcement"] if announcement["enabled"]: NEW_FIELDS["announcement"] = announcement["html"] diff --git a/dev/update_python.py b/dev/devtool/update_python.py similarity index 99% rename from dev/update_python.py rename to dev/devtool/update_python.py index f2acd8102b07..2cbadb5bac73 100644 --- a/dev/update_python.py +++ b/dev/devtool/update_python.py @@ -125,7 +125,7 @@ def _update_python_versions( r"\g<1>" + new_major_minor + r"\g<2>", ), ], - "dev/*.py": [ + "dev/devtool/*.py": [ # Update version assignments ( r'(["\'])' + re.escape(old_version) + r'(\.\d+)?(["\'],?)\s*\n?',