diff --git a/CMakeLists.txt b/CMakeLists.txt index add830d..7df0005 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ add_subdirectory(src/beman/task) #=============================================================================== # NOTE: this must be done before tests! CK -include(./cmake/beman-install-library.cmake) +include(infra/cmake/beman-install-library.cmake) beman_install_library(${PROJECT_NAME} TARGETS ${PROJECT_NAME} beman.task_headers DEPENDENCIES [===[beman.execution 0.2.0 EXACT]===] ) diff --git a/infra/.beman_submodule b/infra/.beman_submodule index bfed167..28a0600 100644 --- a/infra/.beman_submodule +++ b/infra/.beman_submodule @@ -1,3 +1,3 @@ [beman_submodule] remote=https://github.com/bemanproject/infra.git -commit_hash=bb58b2a1cc894d58a55bf745be78f5d27029e245 +commit_hash=63cb577f6484f13ce3349de49ad5ce27e20bf1da diff --git a/infra/.github/workflows/beman-submodule.yml b/infra/.github/workflows/beman-submodule.yml deleted file mode 100644 index 8435086..0000000 --- a/infra/.github/workflows/beman-submodule.yml +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -name: beman-submodule tests - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - -jobs: - beman-submodule-script-ci: - name: beman_module.py ci - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: 3.13 - - - name: Install pytest - run: | - python3 -m pip install pytest - - - name: Run pytest - run: | - cd tools/beman-submodule/ - pytest diff --git a/infra/.pre-commit-config.yaml b/infra/.pre-commit-config.yaml index e806e59..bc4dd84 100644 --- a/infra/.pre-commit-config.yaml +++ b/infra/.pre-commit-config.yaml @@ -19,14 +19,3 @@ repos: - id: gersemi name: CMake linting exclude: ^.*/tests/.*/data/ # Exclude test data directories - - # Python linting and formatting - # config file: ruff.toml (not currently present but add if needed) - # https://docs.astral.sh/ruff/configuration/ - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.13.2 - hooks: - - id: ruff-check - files: ^tools/beman-tidy/ - - id: ruff-format - files: ^tools/beman-tidy/ diff --git a/infra/.pre-commit-hooks.yaml b/infra/.pre-commit-hooks.yaml deleted file mode 100644 index d327587..0000000 --- a/infra/.pre-commit-hooks.yaml +++ /dev/null @@ -1,7 +0,0 @@ -- id: beman-tidy - name: "beman-tidy: bemanification your repo" - entry: ./tools/beman-tidy/beman-tidy - language: script - pass_filenames: false - always_run: true - args: [".", "--verbose"] diff --git a/infra/README.md b/infra/README.md index 16b2672..a869e46 100644 --- a/infra/README.md +++ b/infra/README.md @@ -9,12 +9,11 @@ so it does not respect the usual structure of a Beman library repository nor The * `cmake/`: CMake modules and toolchain files used by Beman libraries. * `containers/`: Containers used for CI builds and tests in the Beman org. -* `tools/`: Tools used to manage the infrastructure and the codebase (e.g., linting, formatting, etc.). ## Usage This repository is intended to be used as a beman-submodule in other Beman repositories. See -[the Beman Submodule documentation](./tools/beman-submodule/README.md) for details. +[the beman-submodule documentation](https://github.com/bemanproject/beman-submodule) for details. ### CMake Modules diff --git a/cmake/Config.cmake.in b/infra/cmake/Config.cmake.in similarity index 86% rename from cmake/Config.cmake.in rename to infra/cmake/Config.cmake.in index 4369fb0..81adf80 100644 --- a/cmake/Config.cmake.in +++ b/infra/cmake/Config.cmake.in @@ -1,4 +1,4 @@ -# cmake/Config.cmake.in -*-cmake-*- +# cmake/Config.cmake.in -*-makefile-*- # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception include(CMakeFindDependencyMacro) diff --git a/infra/cmake/beman-install-library-config.cmake b/infra/cmake/beman-install-library-config.cmake deleted file mode 100644 index e7fd0ad..0000000 --- a/infra/cmake/beman-install-library-config.cmake +++ /dev/null @@ -1,169 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -include_guard(GLOBAL) - -# This file defines the function `beman_install_library` which is used to -# install a library target and its headers, along with optional CMake -# configuration files. -# -# The function is designed to be reusable across different Beman libraries. - -function(beman_install_library name) - # Usage - # ----- - # - # beman_install_library(NAME) - # - # Brief - # ----- - # - # This function installs the specified library target and its headers. - # It also handles the installation of the CMake configuration files if needed. - # - # CMake variables - # --------------- - # - # Note that configuration of the installation is generally controlled by CMake - # cache variables so that they can be controlled by the user or tool running the - # `cmake` command. Neither `CMakeLists.txt` nor `*.cmake` files should set these - # variables directly. - # - # - BEMAN_INSTALL_CONFIG_FILE_PACKAGES: - # List of packages that require config file installation. - # If the package name is in this list, it will install the config file. - # - # - _INSTALL_CONFIG_FILE_PACKAGE: - # Boolean to control config file installation for the specific library. - # The prefix `` is the uppercased name of the library with dots - # replaced by underscores. - # - if(NOT TARGET "${name}") - message(FATAL_ERROR "Target '${name}' does not exist.") - endif() - - if(NOT ARGN STREQUAL "") - message( - FATAL_ERROR - "beman_install_library does not accept extra arguments: ${ARGN}" - ) - endif() - - # Given foo.bar, the component name is bar - string(REPLACE "." ";" name_parts "${name}") - # fail if the name doesn't look like foo.bar - list(LENGTH name_parts name_parts_length) - if(NOT name_parts_length EQUAL 2) - message( - FATAL_ERROR - "beman_install_library expects a name of the form 'beman.', got '${name}'" - ) - endif() - - set(target_name "${name}") - set(install_component_name "${name}") - set(export_name "${name}") - set(package_name "${name}") - list(GET name_parts -1 component_name) - - install( - TARGETS "${target_name}" - COMPONENT "${install_component_name}" - EXPORT "${export_name}" - FILE_SET HEADERS - ) - - set_target_properties( - "${target_name}" - PROPERTIES EXPORT_NAME "${component_name}" - ) - - include(GNUInstallDirs) - - # Determine the prefix for project-specific variables - string(TOUPPER "${name}" project_prefix) - string(REPLACE "." "_" project_prefix "${project_prefix}") - - option( - ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE - "Enable building examples. Default: ${PROJECT_IS_TOP_LEVEL}. Values: { ON, OFF }." - ${PROJECT_IS_TOP_LEVEL} - ) - - # By default, install the config package - set(install_config_package ON) - - # Turn OFF installation of config package by default if, - # in order of precedence: - # 1. The specific package variable is set to OFF - # 2. The package name is not in the list of packages to install config files - if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES) - if( - NOT "${install_component_name}" - IN_LIST - BEMAN_INSTALL_CONFIG_FILE_PACKAGES - ) - set(install_config_package OFF) - endif() - endif() - if(DEFINED ${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE) - set(install_config_package - ${${project_prefix}_INSTALL_CONFIG_FILE_PACKAGE} - ) - endif() - - if(install_config_package) - message( - DEBUG - "beman-install-library: Installing a config package for '${name}'" - ) - - include(CMakePackageConfigHelpers) - - find_file( - config_file_template - NAMES "${package_name}-config.cmake.in" - PATHS "${CMAKE_CURRENT_SOURCE_DIR}" - NO_DEFAULT_PATH - NO_CACHE - REQUIRED - ) - set(config_package_file - "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-config.cmake" - ) - set(package_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}") - configure_package_config_file( - "${config_file_template}" - "${config_package_file}" - INSTALL_DESTINATION "${package_install_dir}" - PATH_VARS PROJECT_NAME PROJECT_VERSION - ) - - set(config_version_file - "${CMAKE_CURRENT_BINARY_DIR}/${package_name}-config-version.cmake" - ) - write_basic_package_version_file( - "${config_version_file}" - VERSION "${PROJECT_VERSION}" - COMPATIBILITY ExactVersion - ) - - install( - FILES "${config_package_file}" "${config_version_file}" - DESTINATION "${package_install_dir}" - COMPONENT "${install_component_name}" - ) - - set(config_targets_file "${package_name}-targets.cmake") - install( - EXPORT "${export_name}" - DESTINATION "${package_install_dir}" - NAMESPACE beman:: - FILE "${config_targets_file}" - COMPONENT "${install_component_name}" - ) - else() - message( - DEBUG - "beman-install-library: Not installing a config package for '${name}'" - ) - endif() -endfunction() diff --git a/cmake/beman-install-library.cmake b/infra/cmake/beman-install-library.cmake similarity index 82% rename from cmake/beman-install-library.cmake rename to infra/cmake/beman-install-library.cmake index ccea5d3..8a6c5a1 100644 --- a/cmake/beman-install-library.cmake +++ b/infra/cmake/beman-install-library.cmake @@ -1,7 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required(VERSION 3.30) - include_guard(GLOBAL) include(CMakePackageConfigHelpers) @@ -21,7 +19,6 @@ include(GNUInstallDirs) # [NAMESPACE ] # [EXPORT_NAME ] # [DESTINATION ] -# [VERSION_SUFFIX] # ) # # Arguments: @@ -52,9 +49,6 @@ include(GNUInstallDirs) # The install destination for CXX_MODULES. # Defaults to ${CMAKE_INSTALL_LIBDIR}/cmake/${name}/modules. # -# VERSION_SUFFIX (optional) -# option to enable the versioning of install destinations -# # Brief # ----- # @@ -88,7 +82,6 @@ function(beman_install_library name) # ---------------------------- # Argument parsing # ---------------------------- - set(options VERSION_SUFFIX) set(oneValueArgs NAMESPACE EXPORT_NAME DESTINATION) set(multiValueArgs TARGETS DEPENDENCIES) @@ -115,20 +108,7 @@ function(beman_install_library name) return() endif() - # gersemi: off - set(_version_suffix) - set(_include_install_dir) - set(_lib_install_dir) - set(_bin_install_dir) - # NOTE: If one of this variables is not set, the default DESTINATION is used! CK - if(BEMAN_VERSION_SUFFIX) - set(_version_suffix "-${PROJECT_VERSION}") - set(_include_install_dir DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/beman${_version_suffix}) - # set(_lib_install_dir DESTINATION ${CMAKE_INSTALL_LIBDIR}/beman${_version_suffix}) - # set(_bin_install_dir DESTINATION ${CMAKE_INSTALL_BINDIR}/beman${_version_suffix}) - endif() - set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}${_version_suffix}") - # gersemi: on + set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}") # ---------------------------- # Defaults @@ -197,10 +177,10 @@ function(beman_install_library name) ) foreach(_install_header_set IN LISTS _available_header_sets) list( - APPEND _install_header_set_args + APPEND + _install_header_set_args FILE_SET "${_install_header_set}" - ${_include_install_dir} COMPONENT "${install_component_name}_Development" ) @@ -219,39 +199,30 @@ function(beman_install_library name) install( TARGETS "${_tgt}" EXPORT ${BEMAN_EXPORT_NAME} - ARCHIVE - ${_lib_install_dir} - COMPONENT "${install_component_name}_Development" + ARCHIVE COMPONENT "${install_component_name}_Development" LIBRARY - ${_lib_install_dir} COMPONENT "${install_component_name}_Runtime" NAMELINK_COMPONENT "${install_component_name}_Development" - RUNTIME - ${_bin_install_dir} - COMPONENT "${install_component_name}_Runtime" + RUNTIME COMPONENT "${install_component_name}_Runtime" ${_install_header_set_args} FILE_SET ${_module_sets} DESTINATION "${BEMAN_DESTINATION}" COMPONENT "${install_component_name}_Development" # NOTE: There's currently no convention for this location! CK CXX_MODULES_BMI - # TODO(CK): DESTINATION ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ - COMPONENT "${install_component_name}_Development" + DESTINATION + ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ + COMPONENT "${install_component_name}_Development" ) else() install( TARGETS "${_tgt}" EXPORT ${BEMAN_EXPORT_NAME} - ARCHIVE - ${_lib_install_dir} - COMPONENT "${install_component_name}_Development" + ARCHIVE COMPONENT "${install_component_name}_Development" LIBRARY - ${_lib_install_dir} COMPONENT "${install_component_name}_Runtime" NAMELINK_COMPONENT "${install_component_name}_Development" - RUNTIME - ${_bin_install_dir} - COMPONENT "${install_component_name}_Runtime" + RUNTIME COMPONENT "${install_component_name}_Runtime" ${_install_header_set_args} ) endif() @@ -272,6 +243,11 @@ function(beman_install_library name) # ---------------------------------------- # Config file installation logic + # + # Precedence (highest to lowest): + # 1. Per-package variable _INSTALL_CONFIG_FILE_PACKAGE + # 2. Allow-list BEMAN_INSTALL_CONFIG_FILE_PACKAGES (if defined) + # 3. Default: ON # ---------------------------------------- string(TOUPPER "${name}" _pkg_upper) string(REPLACE "." "_" _pkg_prefix "${_pkg_upper}") @@ -284,30 +260,26 @@ function(beman_install_library name) set(_pkg_var "${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE") - if(NOT DEFINED ${_pkg_var}) - set(${_pkg_var} - OFF - CACHE BOOL - "Install CMake package config files for ${name}" - ) - endif() + # Default: install config files + set(_install_config ON) - set(_install_config OFF) - - if(${_pkg_var}) - set(_install_config ON) - elseif(BEMAN_INSTALL_CONFIG_FILE_PACKAGES) - list(FIND BEMAN_INSTALL_CONFIG_FILE_PACKAGES "${name}" _idx) - if(NOT _idx EQUAL -1) - set(_install_config ON) + # If the allow-list is defined, only install for packages in the list + if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES) + if(NOT "${name}" IN_LIST BEMAN_INSTALL_CONFIG_FILE_PACKAGES) + set(_install_config OFF) endif() endif() + # Per-package override takes highest precedence + if(DEFINED ${_pkg_var}) + set(_install_config ${${_pkg_var}}) + endif() + # ---------------------------------------- # expand dependencies # ---------------------------------------- set(_beman_find_deps "") - foreach(dep IN ITEMS ${BEMAN_DEPENDENCIES}) + foreach(dep IN LISTS BEMAN_DEPENDENCIES) message( VERBOSE "beman-install-library(${name}): Add find_dependency(${dep})" diff --git a/infra/tools/beman-submodule/README.md b/infra/tools/beman-submodule/README.md deleted file mode 100644 index 36883ad..0000000 --- a/infra/tools/beman-submodule/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# beman-submodule - - - -## What is this script? - -`beman-submodule` provides some of the features of `git submodule`, adding child git -repositories to a parent git repository, but unlike with `git submodule`, the entire child -repo is directly checked in, so only maintainers, not users, need to run this script. The -command line interface mimics `git submodule`'s. - -## How do I add a beman submodule to my repository? - -The first beman submodule you should add is this repository, `infra/`, which you can -bootstrap by running: - - -```sh -curl -s https://raw.githubusercontent.com/bemanproject/infra/refs/heads/main/tools/beman-submodule/beman-submodule | python3 - add https://github.com/bemanproject/infra.git -``` - -Once that's added, you can run the script from `infra/tools/beman-submodule/beman-submodule`. - -## How do I update a beman submodule to the latest trunk? - -You can run `beman-submodule update --remote` to update all beman submodule to latest -trunk, or e.g. `beman-submodule update --remote infra` to update only a specific one. - -## How does it work under the hood? - -Along with the files from the child repository, it creates a dotfile called -`.beman_submodule`, which looks like this: - -```ini -[beman_submodule] -remote=https://github.com/bemanproject/infra.git -commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77 -``` - -## How do I update a beman submodule to a specific commit or change the remote URL? - -You can edit the corresponding lines in the `.beman_submodule` file and run -`beman-submodule update` to update the state of the beman submodule to the new -`.beman_submodule` settings. - -## How can I make CI ensure that my beman submodules are in a valid state? - -Add this job to your CI workflow: - -```yaml - beman-submodule-test: - runs-on: ubuntu-latest - name: "Check beman submodules for consistency" - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: beman submodule consistency check - run: | - (set -o pipefail; ./infra/tools/beman-submodule/beman-submodule status | grep -qvF '+') -``` - -This will fail if the contents of any beman submodule don't match what's specified in the -`.beman_submodule` file. diff --git a/infra/tools/beman-submodule/beman-submodule b/infra/tools/beman-submodule/beman-submodule deleted file mode 100755 index 66cb96e..0000000 --- a/infra/tools/beman-submodule/beman-submodule +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python3 - -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -import argparse -import configparser -import filecmp -import glob -import os -import shutil -import subprocess -import sys -import tempfile -from pathlib import Path - - -def directory_compare( - reference: str | Path, actual: str | Path, ignore, allow_untracked_files: bool): - reference, actual = Path(reference), Path(actual) - - compared = filecmp.dircmp(reference, actual, ignore=ignore) - if (compared.left_only - or (compared.right_only and not allow_untracked_files) - or compared.diff_files): - return False - for common_dir in compared.common_dirs: - path1 = reference / common_dir - path2 = actual / common_dir - if not directory_compare(path1, path2, ignore, allow_untracked_files): - return False - return True - -class BemanSubmodule: - def __init__( - self, dirpath: str | Path, remote: str, commit_hash: str, - allow_untracked_files: bool): - self.dirpath = Path(dirpath) - self.remote = remote - self.commit_hash = commit_hash - self.allow_untracked_files = allow_untracked_files - -def parse_beman_submodule_file(path): - config = configparser.ConfigParser() - read_result = config.read(path) - def fail(): - raise Exception(f'Failed to parse {path} as a .beman_submodule file') - if not read_result: - fail() - if not 'beman_submodule' in config: - fail() - if not 'remote' in config['beman_submodule']: - fail() - if not 'commit_hash' in config['beman_submodule']: - fail() - allow_untracked_files = config.getboolean( - 'beman_submodule', 'allow_untracked_files', fallback=False) - return BemanSubmodule( - Path(path).resolve().parent, - config['beman_submodule']['remote'], - config['beman_submodule']['commit_hash'], - allow_untracked_files) - -def get_beman_submodule(path: str | Path): - beman_submodule_filepath = Path(path) / '.beman_submodule' - - if beman_submodule_filepath.is_file(): - return parse_beman_submodule_file(beman_submodule_filepath) - else: - return None - -def find_beman_submodules_in(path): - path = Path(path) - assert path.is_dir() - - result = [] - for dirpath, _, filenames in path.walk(): - if '.beman_submodule' in filenames: - result.append(parse_beman_submodule_file(dirpath / '.beman_submodule')) - return sorted(result, key=lambda module: module.dirpath) - -def cwd_git_repository_path(): - process = subprocess.run( - ['git', 'rev-parse', '--show-toplevel'], capture_output=True, text=True, - check=False) - if process.returncode == 0: - return process.stdout.strip() - elif "fatal: not a git repository" in process.stderr: - return None - else: - raise Exception("git rev-parse --show-toplevel failed") - -def clone_beman_submodule_into_tmpdir(beman_submodule, remote): - tmpdir = tempfile.TemporaryDirectory() - subprocess.run( - ['git', 'clone', beman_submodule.remote, tmpdir.name], capture_output=True, - check=True) - if not remote: - subprocess.run( - ['git', '-C', tmpdir.name, 'reset', '--hard', beman_submodule.commit_hash], - capture_output=True, check=True) - return tmpdir - -def get_paths(beman_submodule): - tmpdir = clone_beman_submodule_into_tmpdir(beman_submodule, False) - paths = set(glob.glob('*', root_dir=Path(tmpdir.name), include_hidden=True)) - paths.remove('.git') - return paths - -def beman_submodule_status(beman_submodule): - tmpdir = clone_beman_submodule_into_tmpdir(beman_submodule, False) - if directory_compare( - tmpdir.name, beman_submodule.dirpath, ['.beman_submodule', '.git'], - beman_submodule.allow_untracked_files): - status_character=' ' - else: - status_character='+' - parent_repo_path = cwd_git_repository_path() - if not parent_repo_path: - raise Exception('this is not a git repository') - relpath = Path(beman_submodule.dirpath).relative_to(Path(parent_repo_path)) - return status_character + ' ' + beman_submodule.commit_hash + ' ' + str(relpath) - -def beman_submodule_update(beman_submodule, remote): - tmpdir = clone_beman_submodule_into_tmpdir(beman_submodule, remote) - tmp_path = Path(tmpdir.name) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmp_path) - - if beman_submodule.allow_untracked_files: - for path in get_paths(beman_submodule): - path2 = Path(beman_submodule.dirpath) / path - if Path(path2).is_dir(): - shutil.rmtree(path2) - elif Path(path2).is_file(): - os.remove(path2) - else: - shutil.rmtree(beman_submodule.dirpath) - - submodule_path = tmp_path / '.beman_submodule' - with open(submodule_path, 'w') as f: - f.write('[beman_submodule]\n') - f.write(f'remote={beman_submodule.remote}\n') - f.write(f'commit_hash={sha_process.stdout.strip()}\n') - if beman_submodule.allow_untracked_files: - f.write(f'allow_untracked_files=True\n') - shutil.rmtree(tmp_path / '.git') - shutil.copytree(tmp_path, beman_submodule.dirpath, dirs_exist_ok=True) - -def update_command(remote, path): - if not path: - parent_repo_path = cwd_git_repository_path() - if not parent_repo_path: - raise Exception('this is not a git repository') - beman_submodules = find_beman_submodules_in(parent_repo_path) - else: - beman_submodule = get_beman_submodule(path) - if not beman_submodule: - raise Exception(f'{path} is not a beman_submodule') - beman_submodules = [beman_submodule] - for beman_submodule in beman_submodules: - beman_submodule_update(beman_submodule, remote) - -def add_command(repository, path, allow_untracked_files): - tmpdir = tempfile.TemporaryDirectory() - subprocess.run( - ['git', 'clone', repository], capture_output=True, check=True, cwd=tmpdir.name) - repository_name = os.listdir(tmpdir.name)[0] - if not path: - path = Path(repository_name) - else: - path = Path(path) - if not allow_untracked_files and path.exists(): - raise Exception(f'{path} exists') - path.mkdir(exist_ok=allow_untracked_files) - tmpdir_repo = Path(tmpdir.name) / repository_name - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir_repo) - with open(tmpdir_repo / '.beman_submodule', 'w') as f: - f.write('[beman_submodule]\n') - f.write(f'remote={repository}\n') - f.write(f'commit_hash={sha_process.stdout.strip()}\n') - if allow_untracked_files: - f.write(f'allow_untracked_files=True\n') - shutil.rmtree(tmpdir_repo /'.git') - shutil.copytree(tmpdir_repo, path, dirs_exist_ok=True) - -def status_command(paths): - if not paths: - parent_repo_path = cwd_git_repository_path() - if not parent_repo_path: - raise Exception('this is not a git repository') - beman_submodules = find_beman_submodules_in(parent_repo_path) - else: - beman_submodules = [] - for path in paths: - beman_submodule = get_beman_submodule(path) - if not beman_submodule: - raise Exception(f'{path} is not a beman_submodule') - beman_submodules.append(beman_submodule) - for beman_submodule in beman_submodules: - print(beman_submodule_status(beman_submodule)) - -def get_parser(): - parser = argparse.ArgumentParser(description='Beman pseudo-submodule tool') - subparsers = parser.add_subparsers(dest='command', help='available commands') - parser_update = subparsers.add_parser('update', help='update beman_submodules') - parser_update.add_argument( - '--remote', action='store_true', - help='update a beman_submodule to its latest from upstream') - parser_update.add_argument( - 'beman_submodule_path', nargs='?', - help='relative path to the beman_submodule to update') - parser_add = subparsers.add_parser('add', help='add a new beman_submodule') - parser_add.add_argument('repository', help='git repository to add') - parser_add.add_argument( - 'path', nargs='?', help='path where the repository will be added') - parser_add.add_argument( - '--allow-untracked-files', action='store_true', - help='the beman_submodule will not occupy the subdirectory exclusively') - parser_status = subparsers.add_parser( - 'status', help='show the status of beman_submodules') - parser_status.add_argument('paths', nargs='*') - return parser - -def parse_args(args): - return get_parser().parse_args(args); - -def usage(): - return get_parser().format_help() - -def run_command(args): - if args.command == 'update': - update_command(args.remote, args.beman_submodule_path) - elif args.command == 'add': - add_command(args.repository, args.path, args.allow_untracked_files) - elif args.command == 'status': - status_command(args.paths) - else: - raise Exception(usage()) - -def check_for_git(path): - env = os.environ.copy() - if path is not None: - env["PATH"] = path - return shutil.which("git", path=env.get("PATH")) is not None - -def main(): - try: - if not check_for_git(None): - raise Exception('git not found in PATH') - args = parse_args(sys.argv[1:]) - run_command(args) - except Exception as e: - print("Error:", e, file=sys.stderr) - sys.exit(1) - -if __name__ == '__main__': - main() diff --git a/infra/tools/beman-submodule/test/test_beman_submodule.py b/infra/tools/beman-submodule/test/test_beman_submodule.py deleted file mode 100644 index 600fc07..0000000 --- a/infra/tools/beman-submodule/test/test_beman_submodule.py +++ /dev/null @@ -1,539 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -import glob -import os -import pytest -import shutil -import stat -import subprocess -import tempfile -from pathlib import Path - -# https://stackoverflow.com/a/19011259 -import types -import importlib.machinery -loader = importlib.machinery.SourceFileLoader( - 'beman_submodule', - str(Path(__file__).parent.resolve().parent / 'beman-submodule')) -beman_submodule = types.ModuleType(loader.name) -loader.exec_module(beman_submodule) - -def create_test_git_repository(): - tmpdir = tempfile.TemporaryDirectory() - tmp_path = Path(tmpdir.name) - - subprocess.run(['git', 'init'], check=True, cwd=tmpdir.name, capture_output=True) - def make_commit(a_txt_contents): - with open(tmp_path / 'a.txt', 'w') as f: - f.write(a_txt_contents) - subprocess.run( - ['git', 'add', 'a.txt'], check=True, cwd=tmpdir.name, capture_output=True) - subprocess.run( - ['git', '-c', 'user.name=test', '-c', 'user.email=test@example.com', 'commit', - '--author="test "', '-m', 'test'], - check=True, cwd=tmpdir.name, capture_output=True) - make_commit('A') - make_commit('a') - return tmpdir - -def create_test_git_repository2(): - tmpdir = tempfile.TemporaryDirectory() - tmp_path = Path(tmpdir.name) - - subprocess.run(['git', 'init'], check=True, cwd=tmpdir.name, capture_output=True) - with open(tmp_path / 'a.txt', 'w') as f: - f.write('a') - subprocess.run( - ['git', 'add', 'a.txt'], check=True, cwd=tmpdir.name, capture_output=True) - subprocess.run( - ['git', '-c', 'user.name=test', '-c', 'user.email=test@example.com', 'commit', - '--author="test "', '-m', 'test'], - check=True, cwd=tmpdir.name, capture_output=True) - os.remove(tmp_path / 'a.txt') - subprocess.run( - ['git', 'rm', 'a.txt'], check=True, cwd=tmpdir.name, capture_output=True) - with open(tmp_path / 'b.txt', 'w') as f: - f.write('b') - subprocess.run( - ['git', 'add', 'b.txt'], check=True, cwd=tmpdir.name, capture_output=True) - subprocess.run( - ['git', '-c', 'user.name=test', '-c', 'user.email=test@example.com', 'commit', - '--author="test "', '-m', 'test'], - check=True, cwd=tmpdir.name, capture_output=True) - return tmpdir - -def test_directory_compare(): - def create_dir_structure(dir_path: Path): - bar_path = dir_path / 'bar' - os.makedirs(bar_path) - - with open(dir_path / 'foo.txt', 'w') as f: - f.write('foo') - with open(bar_path / 'baz.txt', 'w') as f: - f.write('baz') - - with tempfile.TemporaryDirectory() as dir_a, \ - tempfile.TemporaryDirectory() as dir_b: - path_a = Path(dir_a) - path_b = Path(dir_b) - - create_dir_structure(path_a) - create_dir_structure(path_b) - - assert beman_submodule.directory_compare(dir_a, dir_b, [], False) - - with open(path_a / 'bar' / 'quux.txt', 'w') as f: - f.write('quux') - - assert not beman_submodule.directory_compare(path_a, path_b, [], False) - assert beman_submodule.directory_compare(path_a, path_b, ['quux.txt'], False) - -def test_directory_compare_untracked_files(): - def create_dir_structure(dir_path: Path): - bar_path = dir_path / 'bar' - os.makedirs(bar_path) - - with open(dir_path / 'foo.txt', 'w') as f: - f.write('foo') - with open(bar_path / 'baz.txt', 'w') as f: - f.write('baz') - - with tempfile.TemporaryDirectory() as reference, \ - tempfile.TemporaryDirectory() as actual: - path_a = Path(reference) - path_b = Path(actual) - - create_dir_structure(path_a) - create_dir_structure(path_b) - (path_b / 'c.txt').touch() - - assert beman_submodule.directory_compare(reference, actual, [], True) - - with open(path_a / 'bar' / 'quux.txt', 'w') as f: - f.write('quux') - - assert not beman_submodule.directory_compare(path_a, path_b, [], True) - assert beman_submodule.directory_compare(path_a, path_b, ['quux.txt'], True) - -def test_parse_beman_submodule_file(): - def valid_file(): - tmpfile = tempfile.NamedTemporaryFile() - tmpfile.write('[beman_submodule]\n'.encode('utf-8')) - tmpfile.write( - 'remote=git@github.com:bemanproject/infra.git\n'.encode('utf-8')) - tmpfile.write( - 'commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77\n'.encode('utf-8')) - tmpfile.flush() - module = beman_submodule.parse_beman_submodule_file(tmpfile.name) - assert module.dirpath == Path(tmpfile.name).resolve().parent - assert module.remote == 'git@github.com:bemanproject/infra.git' - assert module.commit_hash == '9b88395a86c4290794e503e94d8213b6c442ae77' - valid_file() - def invalid_file_missing_remote(): - threw = False - try: - tmpfile = tempfile.NamedTemporaryFile() - tmpfile.write('[beman_submodule]\n'.encode('utf-8')) - tmpfile.write( - 'commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77\n'.encode('utf-8')) - tmpfile.flush() - beman_submodule.parse_beman_submodule_file(tmpfile.name) - except: - threw = True - assert threw - invalid_file_missing_remote() - def invalid_file_missing_commit_hash(): - threw = False - try: - tmpfile = tempfile.NamedTemporaryFile() - tmpfile.write('[beman_submodule]\n'.encode('utf-8')) - tmpfile.write( - 'remote=git@github.com:bemanproject/infra.git\n'.encode('utf-8')) - tmpfile.flush() - beman_submodule.parse_beman_submodule_file(tmpfile.name) - except: - threw = True - assert threw - invalid_file_missing_commit_hash() - def invalid_file_wrong_section(): - threw = False - try: - tmpfile = tempfile.NamedTemporaryFile() - tmpfile.write('[invalid]\n'.encode('utf-8')) - tmpfile.write( - 'remote=git@github.com:bemanproject/infra.git\n'.encode('utf-8')) - tmpfile.write( - 'commit_hash=9b88395a86c4290794e503e94d8213b6c442ae77\n'.encode('utf-8')) - tmpfile.flush() - beman_submodule.parse_beman_submodule_file(tmpfile.name) - except: - threw = True - assert threw - invalid_file_wrong_section() - -def test_get_beman_submodule(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - assert beman_submodule.get_beman_submodule('foo') - os.remove('foo/.beman_submodule') - assert not beman_submodule.get_beman_submodule('foo') - os.chdir(original_cwd) - -def test_find_beman_submodules_in(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - beman_submodule.add_command(tmpdir.name, 'bar', False) - beman_submodules = beman_submodule.find_beman_submodules_in(tmpdir2.name) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - sha = sha_process.stdout.strip() - assert beman_submodules[0].dirpath == Path(tmpdir2.name) / 'bar' - assert beman_submodules[0].remote == tmpdir.name - assert beman_submodules[0].commit_hash == sha - assert beman_submodules[1].dirpath == Path(tmpdir2.name) / 'foo' - assert beman_submodules[1].remote == tmpdir.name - assert beman_submodules[1].commit_hash == sha - os.chdir(original_cwd) - -def test_cwd_git_repository_path(): - original_cwd = Path.cwd() - tmpdir = tempfile.TemporaryDirectory() - os.chdir(tmpdir.name) - assert not beman_submodule.cwd_git_repository_path() - subprocess.run(['git', 'init']) - assert beman_submodule.cwd_git_repository_path() == tmpdir.name - os.chdir(original_cwd) - -def test_clone_beman_submodule_into_tmpdir(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - sha = sha_process.stdout.strip() - beman_submodule.add_command(tmpdir.name, 'foo', False) - module = beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo') - module.commit_hash = sha - tmpdir3 = beman_submodule.clone_beman_submodule_into_tmpdir(module, False) - assert not beman_submodule.directory_compare( - tmpdir.name, tmpdir3.name, ['.git'], False) - tmpdir4 = beman_submodule.clone_beman_submodule_into_tmpdir(module, True) - assert beman_submodule.directory_compare(tmpdir.name, tmpdir4.name, ['.git'], False) - subprocess.run( - ['git', 'reset', '--hard', sha], capture_output=True, check=True, - cwd=tmpdir.name) - assert beman_submodule.directory_compare(tmpdir.name, tmpdir3.name, ['.git'], False) - os.chdir(original_cwd) - -def test_get_paths(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - module = beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo') - assert beman_submodule.get_paths(module) == set(['a.txt']) - os.chdir(original_cwd) - -def test_beman_submodule_status(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - sha = sha_process.stdout.strip() - assert ' ' + sha + ' foo' == beman_submodule.beman_submodule_status( - beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo')) - with open(Path(tmpdir2.name) / 'foo' / 'a.txt', 'w') as f: - f.write('b') - assert '+ ' + sha + ' foo' == beman_submodule.beman_submodule_status( - beman_submodule.get_beman_submodule(Path(tmpdir2.name) / 'foo')) - os.chdir(original_cwd) - -def test_update_command_no_paths(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - orig_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - orig_sha = orig_sha_process.stdout.strip() - parent_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - parent_sha = parent_sha_process.stdout.strip() - parent_parent_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - parent_parent_sha = parent_parent_sha_process.stdout.strip() - subprocess.run( - ['git', 'reset', '--hard', parent_parent_sha], capture_output=True, check=True, - cwd=tmpdir.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - beman_submodule.add_command(tmpdir.name, 'bar', False) - subprocess.run( - ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True, - cwd=tmpdir.name) - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'w') as f: - f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n') - with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'w') as f: - f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n') - beman_submodule.update_command(False, None) - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n' - with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n' - subprocess.run( - ['git', 'reset', '--hard', parent_sha], capture_output=True, check=True, - cwd=tmpdir.name) - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False) - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False) - subprocess.run( - ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True, - cwd=tmpdir.name) - beman_submodule.update_command(True, None) - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={orig_sha}\n' - with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={orig_sha}\n' - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False) - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False) - os.chdir(original_cwd) - -def test_update_command_with_path(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - orig_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - orig_sha = orig_sha_process.stdout.strip() - parent_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - parent_sha = parent_sha_process.stdout.strip() - parent_parent_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - parent_parent_sha = parent_parent_sha_process.stdout.strip() - subprocess.run( - ['git', 'reset', '--hard', parent_parent_sha], capture_output=True, check=True, - cwd=tmpdir.name) - tmpdir_parent_parent_copy = tempfile.TemporaryDirectory() - shutil.copytree(tmpdir.name, tmpdir_parent_parent_copy.name, dirs_exist_ok=True) - beman_submodule.add_command(tmpdir.name, 'foo', False) - beman_submodule.add_command(tmpdir.name, 'bar', False) - subprocess.run( - ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True, - cwd=tmpdir.name) - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'w') as f: - f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n') - with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'w') as f: - f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n') - beman_submodule.update_command(False, 'foo') - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n' - with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n' - subprocess.run( - ['git', 'reset', '--hard', parent_sha], capture_output=True, check=True, - cwd=tmpdir.name) - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False) - assert beman_submodule.directory_compare( - tmpdir_parent_parent_copy.name, - Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False) - subprocess.run( - ['git', 'reset', '--hard', orig_sha], capture_output=True, check=True, - cwd=tmpdir.name) - beman_submodule.update_command(True, 'foo') - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={orig_sha}\n' - with open(Path(tmpdir2.name) / 'bar' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\n' - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False) - assert beman_submodule.directory_compare( - tmpdir_parent_parent_copy.name, - Path(tmpdir2.name) / 'bar', ['.git', '.beman_submodule'], False) - os.chdir(original_cwd) - -def test_update_command_untracked_files(): - tmpdir = create_test_git_repository2() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd(); - os.chdir(tmpdir2.name) - orig_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - orig_sha = orig_sha_process.stdout.strip() - parent_sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD^'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - parent_sha = parent_sha_process.stdout.strip() - os.makedirs(Path(tmpdir2.name) / 'foo') - (Path(tmpdir2.name) / 'foo' / 'c.txt').touch() - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'w') as f: - f.write(f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={parent_sha}\nallow_untracked_files=True') - beman_submodule.update_command(False, 'foo') - assert set(['./foo/a.txt', './foo/c.txt']) == set(glob.glob('./foo/*.txt')) - beman_submodule.update_command(True, 'foo') - assert set(['./foo/b.txt', './foo/c.txt']) == set(glob.glob('./foo/*.txt')) - os.chdir(original_cwd) - -def test_add_command(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - sha = sha_process.stdout.strip() - assert beman_submodule.directory_compare( - tmpdir.name, Path(tmpdir2.name) / 'foo', ['.git', '.beman_submodule'], False) - with open(Path(tmpdir2.name) / 'foo' / '.beman_submodule', 'r') as f: - assert f.read() == f'[beman_submodule]\nremote={tmpdir.name}\ncommit_hash={sha}\n' - os.chdir(original_cwd) - -def test_add_command_untracked_files(): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - os.makedirs(Path(tmpdir2.name) / 'foo') - (Path(tmpdir2.name) / 'foo' / 'c.txt').touch() - beman_submodule.add_command(tmpdir.name, 'foo', True) - assert set(['./foo/a.txt', './foo/c.txt']) == set(glob.glob('./foo/*.txt')) - os.chdir(original_cwd) - -def test_status_command_no_paths(capsys): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - beman_submodule.add_command(tmpdir.name, 'bar', False) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - with open(Path(tmpdir2.name) / 'bar' / 'a.txt', 'w') as f: - f.write('b') - beman_submodule.status_command([]) - sha = sha_process.stdout.strip() - assert capsys.readouterr().out == '+ ' + sha + ' bar\n' + ' ' + sha + ' foo\n' - os.chdir(original_cwd) - -def test_status_command_with_path(capsys): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', False) - beman_submodule.add_command(tmpdir.name, 'bar', False) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - with open(Path(tmpdir2.name) / 'bar' / 'a.txt', 'w') as f: - f.write('b') - beman_submodule.status_command(['bar']) - sha = sha_process.stdout.strip() - assert capsys.readouterr().out == '+ ' + sha + ' bar\n' - os.chdir(original_cwd) - -def test_status_command_untracked_files(capsys): - tmpdir = create_test_git_repository() - tmpdir2 = create_test_git_repository() - original_cwd = Path.cwd() - os.chdir(tmpdir2.name) - beman_submodule.add_command(tmpdir.name, 'foo', True) - sha_process = subprocess.run( - ['git', 'rev-parse', 'HEAD'], capture_output=True, check=True, text=True, - cwd=tmpdir.name) - (Path(tmpdir2.name) / 'foo' / 'c.txt').touch() - beman_submodule.status_command(['foo']) - sha = sha_process.stdout.strip() - assert capsys.readouterr().out == ' ' + sha + ' foo\n' - os.chdir(original_cwd) - -def test_check_for_git(): - tmpdir = tempfile.TemporaryDirectory() - assert not beman_submodule.check_for_git(tmpdir.name) - fake_git_path = Path(tmpdir.name) / 'git' - with open(fake_git_path, 'w'): - pass - os.chmod(fake_git_path, stat.S_IRWXU) - assert beman_submodule.check_for_git(tmpdir.name) - -def test_parse_args(): - def plain_update(): - args = beman_submodule.parse_args(['update']) - assert args.command == 'update' - assert not args.remote - assert not args.beman_submodule_path - plain_update() - def update_remote(): - args = beman_submodule.parse_args(['update', '--remote']) - assert args.command == 'update' - assert args.remote - assert not args.beman_submodule_path - update_remote() - def update_path(): - args = beman_submodule.parse_args(['update', 'infra/']) - assert args.command == 'update' - assert not args.remote - assert args.beman_submodule_path == 'infra/' - update_path() - def update_path_remote(): - args = beman_submodule.parse_args(['update', '--remote', 'infra/']) - assert args.command == 'update' - assert args.remote - assert args.beman_submodule_path == 'infra/' - update_path_remote() - def plain_add(): - args = beman_submodule.parse_args(['add', 'git@github.com:bemanproject/infra.git']) - assert args.command == 'add' - assert args.repository == 'git@github.com:bemanproject/infra.git' - assert not args.path - plain_add() - def add_path(): - args = beman_submodule.parse_args( - ['add', 'git@github.com:bemanproject/infra.git', 'infra/']) - assert args.command == 'add' - assert args.repository == 'git@github.com:bemanproject/infra.git' - assert args.path == 'infra/' - add_path() - def plain_status(): - args = beman_submodule.parse_args(['status']) - assert args.command == 'status' - assert args.paths == [] - plain_status() - def status_one_module(): - args = beman_submodule.parse_args(['status', 'infra/']) - assert args.command == 'status' - assert args.paths == ['infra/'] - status_one_module() - def status_multiple_modules(): - args = beman_submodule.parse_args(['status', 'infra/', 'foobar/']) - assert args.command == 'status' - assert args.paths == ['infra/', 'foobar/'] - status_multiple_modules()