diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index bc1c6cd..352f31c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -10,10 +10,11 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + os: [ubuntu-latest, windows-latest] python-version: [3.11, 3.12, 3.13, 3.14] steps: diff --git a/src/makeapp/appmaker.py b/src/makeapp/appmaker.py index b908abd..90deaed 100644 --- a/src/makeapp/appmaker.py +++ b/src/makeapp/appmaker.py @@ -113,7 +113,7 @@ def __init__( # Support for user-supplied template directories. for template in templates_to_use or []: - if '/' in template: + if os.sep in template: parent = str(Path(template).parent) if parent not in search_paths: search_paths.append(parent) @@ -302,6 +302,7 @@ def rollout( :param remote_push: Whether to push to remote. """ + dest = os.path.abspath(dest) self.logger.info(f'Application target path: {dest}') # Make remote available for hooks. diff --git a/src/makeapp/apptemplate.py b/src/makeapp/apptemplate.py index ec2390b..c190662 100644 --- a/src/makeapp/apptemplate.py +++ b/src/makeapp/apptemplate.py @@ -120,12 +120,12 @@ def get_files(self) -> dict[str, 'TemplateFile']: full_path = os.path.join(path, fname) - rel_path = full_path.replace(templates_path, '').lstrip('/') + rel_path = os.path.relpath(full_path, templates_path) template_file = TemplateFile( template=self, path_full=full_path, - path_rel=rel_path, + path_rel=rel_path.replace(os.sep, '/'), ) rel_path = rel_path.replace(maker.package_dir_marker, maker.settings['package_name']) @@ -174,9 +174,9 @@ def _find(cls, name_or_path: str, search_paths: tuple[str, ...]) -> tuple[str, s """ for supposed_path in search_paths: - if '/' in supposed_path and os.path.exists(supposed_path): + if os.sep in supposed_path and os.path.exists(supposed_path): path = str(os.path.abspath(supposed_path)) - return path.split('/')[-1], path + return path.split(os.sep)[-1], path raise AppMakerException( f"Unable to find application template {name_or_path}. " @@ -221,7 +221,7 @@ def parent_paths(self): if os.path.exists(path_full): # Check parent file exists in template. - paths.append(os.path.join(parent.name, path_rel)) + paths.append(f'{parent.name}/{path_rel}') if parent.is_default: break diff --git a/src/makeapp/helpers/vcs.py b/src/makeapp/helpers/vcs.py index e9c8542..5511a78 100644 --- a/src/makeapp/helpers/vcs.py +++ b/src/makeapp/helpers/vcs.py @@ -110,7 +110,11 @@ def commit(self, message: str): :param message: Commit description. """ - self.run_command("commit -m '%s'" % message.replace("'", "''")) + with NamedTemporaryFile() as f: + f.write(message.encode()) + f.flush() + + self.run_command(f'commit -F "{f.name}"') def get_remotes(self): """Returns a list of remotes.""" diff --git a/src/makeapp/utils.py b/src/makeapp/utils.py index a526097..ebf11f4 100644 --- a/src/makeapp/utils.py +++ b/src/makeapp/utils.py @@ -87,13 +87,10 @@ def check_command(command: str, *, hint: str): :param hint: """ - try: - run_command(f'type {command}') - - except CommandError as e: + if shutil.which(command) is None: raise CommandError( f"Failed to execute '{command}' command. " - f"Check {hint} is installed and available.") from e + f"Check {hint} is installed and available.") def run_command(command: str, *, err_msg: str = '', env: dict | None = None, capture: bool = True) -> list[str]: @@ -187,4 +184,8 @@ def sync(cls) -> list[str]: @classmethod def install(cls): - return run_command('curl -LsSf https://astral.sh/uv/install.sh | sh', capture=False) + if sys.platform == 'win32': + cmd = 'powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"' + else: + cmd = 'curl -LsSf https://astral.sh/uv/install.sh | sh' + return run_command(cmd, capture=False)