Skip to content

Commit

Permalink
Show remote details while fetching
Browse files Browse the repository at this point in the history
Closes #10
  • Loading branch information
Metin Yazici committed Mar 21, 2022
1 parent 21959dd commit 30ea39e
Show file tree
Hide file tree
Showing 19 changed files with 248 additions and 65 deletions.
73 changes: 73 additions & 0 deletions .github/scripts/check_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import argparse
import difflib
import subprocess


def get_file_lines(filename):
with open(filename) as file:
lines = file.readlines()
return lines


def get_readme_text(lines, start_pattern, end_pattern):
start_index = lines.index(start_pattern) + 1
end_index = lines.index(end_pattern)
sublines = lines[start_index:end_index]
# remove backticks
sublines = [line for line in sublines if line != "```\n"]
text = "".join(sublines)
return text


def get_command_output(command):
cmd = command.split(" ")
out = None
run = subprocess.run(cmd, stdout=subprocess.PIPE)
if run.returncode == 0:
out = run.stdout.decode("utf-8")
return out
raise ValueError("could not get the help text")


def check_if_equal(command_output, readme_text):
equal = command_output == readme_text
if not equal:
d1 = command_output.splitlines(keepends=True)
d2 = readme_text.splitlines(keepends=True)
print("".join(difflib.ndiff(d1, d2)))
print("* * *")
raise ValueError("readme text is not up-to-date")
print("help output in readme is up-to-date")


def get_cli_args():
parser = argparse.ArgumentParser(
description="Check a block of text in a file against an output returned by a command or any other piped output."
)
parser.add_argument("--file", required=True, help="file to check the block")
parser.add_argument("--pattern_start", required=True, help="start pattern")
parser.add_argument("--pattern_end", required=True, help="end pattern")
parser.add_argument("--command", required=True, help="command to check the output")
parsed_args = parser.parse_args()
# this solution isn't great as it also removes the other unicode characters
# but so far, I don't have any use case so fine.
parsed_args.pattern_start = bytes(parsed_args.pattern_start, "utf-8").decode(
"unicode_escape"
)
parsed_args.pattern_end = bytes(parsed_args.pattern_end, "utf-8").decode(
"unicode_escape"
)
return parsed_args


def main():
args = get_cli_args()
lines = get_file_lines(args.file)
readme_text = get_readme_text(lines, args.pattern_start, args.pattern_end)
command_output = get_command_output(args.command)
check_if_equal(command_output, readme_text)
return 0


if __name__ == "__main__":
main()
61 changes: 48 additions & 13 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,69 @@ on: [push, pull_request]

jobs:

pre-commit:
prepare:
name: "Prepare for CI"
runs-on: ubuntu-latest
timeout-minutes: 1

outputs:
py_version: ${{ steps.versions.outputs.py_version }}

steps:
- uses: actions/checkout@v2

- name: Get versions
id: versions
run: |
py_version="$(./get_version.sh __py_version__)"
echo "::set-output name=py_version::$py_version"
pre_commit:
name: pre-commit checks
runs-on: ubuntu-latest
timeout-minutes: 2

steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/[email protected]
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/[email protected]

tests:
name: Run tests with python ${{ matrix.python-version }}
check_readme:
name: "Check README help text"
needs: [ prepare ]
runs-on: ubuntu-latest
timeout-minutes: 2

strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: ${{ needs.prepare.outputs.version }}

- name: Install
run: make install

- name: Check if help text in README is up-to-date
run: |
python .github/scripts/check_text.py \
--file "README.md" \
--pattern_start '<!-- help-output: start -->\n' \
--pattern_end '<!-- help-output: end -->\n' \
--command "git-substatus --help"
tests:
name: "Run tests"
needs: [ prepare ]
runs-on: ubuntu-latest
timeout-minutes: 2

steps:
- uses: actions/checkout@v2

- name: Setup python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ needs.prepare.outputs.version }}

- name: Install dev dependencies
run: |
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Integration

on:
push:
branches:
- "master"

jobs:

test_docker:
name: Build and test Docker image
runs-on: ubuntu-latest
timeout-minutes: 3

steps:
- uses: actions/checkout@v2

- name: Build the Docker image
run: make VERSION=test docker-build

- name: Docker container can be run
run: |
./tests/gen_test_repos.sh && \
docker run --rm -t \
-v "$(pwd)":/"$(pwd)" \
-w "$(pwd)" strboul/git-substatus:test \
tests/generated-test-proj-dir
11 changes: 4 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Release
on:
push:
tags:
- "v*"
- "v*.*.*"

jobs:

Expand Down Expand Up @@ -47,21 +47,18 @@ jobs:
uses: softprops/action-gh-release@v1
with:
body: |
# v${{ needs.prepare.outputs.version }}
Install this specific release with:
## PyPI
- PyPI
```bash
pip install git-substatus==${{ needs.prepare.outputs.version }}
```
## DockerHub
- Dockerhub
```bash
docker run --rm -t -v "$(pwd)":/"$(pwd)" -w "$(pwd)" strboul/git-substatus:${{ needs.prepare.outputs.version }}
```
# TODO which files actually?
files: dist/
publish_pypi:
name: "Publish to PyPI (https://pypi.org/project/git-substatus/)"
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
fail_fast: true
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
Expand Down
13 changes: 7 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ LABEL HOMEPAGE="https://github.com/strboul/git-substatus"

RUN apk add --update --no-cache --no-progress git

ARG CONTAINER_PATH="/opt/git-substatus/"
RUN mkdir -p "$CONTAINER_PATH"
COPY setup.py README.md "$CONTAINER_PATH"
COPY git_substatus/ "$CONTAINER_PATH"/git_substatus
RUN cd "$CONTAINER_PATH" && \
python -m pip install --upgrade pip
ARG INSTALL_PATH="/opt/git-substatus/"
RUN mkdir -p "$INSTALL_PATH"
COPY setup.py README.md get_version.sh "$INSTALL_PATH"
COPY git_substatus/ "$INSTALL_PATH"/git_substatus
RUN cd "$INSTALL_PATH" && \
python -m pip install --upgrade pip .
RUN rm -rf "$INSTALL_PATH"

ENTRYPOINT ["git-substatus"]
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ VERSION := $(shell ./get_version.sh __version__)
PY_VERSION := $(shell ./get_version.sh __py_version__)


all: typecheck black test install clean
all: typecheck black test install docker-build clean


install:
Expand Down Expand Up @@ -48,15 +48,22 @@ coverage:
black:
$(call check_pip_module,"black")
$(call echo_section,"checking code formatting with black")
python -m black --check git_substatus
python -m black --check .


black-apply:
$(call check_pip_module,"black")
$(call echo_section,"applying code formatting with black")
python -m black .


clean:
$(call echo_section,"cleaning")
find . -depth -name __pycache__ -exec rm -rf {} \; && \
find . -depth -name *.pyc -exec rm -rf {} \; && \
find . -depth -name *.mypy_cache -exec rm -rf {} \; && \
find . -depth -name .coverage -exec rm {} \;
find . -depth -name .coverage -exec rm {} \; && \
rm -rf tests/generated-test-proj-dir


tag-create:
Expand Down
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<!-- badges: start -->
[![CI status](https://github.com/strboul/git-substatus/workflows/CI/badge.svg)](https://github.com/strboul/git-substatus/actions)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-success)](https://github.com/strboul/git-substatus/blob/master/.pre-commit-config.yaml)
[![PyPI version](https://img.shields.io/pypi/v/git-substatus?color=%230073b7&label=pypi)](https://pypi.org/project/git-substatus/)
[![hub.docker.com](https://img.shields.io/docker/v/strboul/git-substatus?color=%230db7ed&label=docker)](https://hub.docker.com/r/strboul/git-substatus)
Expand All @@ -28,6 +29,33 @@ like a projects folder keeping the git projects. You can therefore view:

See more at `git-substatus --help`

<!-- help-output: start -->
```
usage: git-substatus [-h] [-v] [--include-hidden] [--fetch] [path]
See subfolders' git status
===========================
The output consists of four columns:
repo name | branch head | status | git stashes (if any)
The string (*WT) seen next to the repo names shows that the
repo has some git worktrees. See more:
<https://git-scm.com/docs/git-worktree>
positional arguments:
path a path to where you want to see git substatuses. If empty, the
current working directory is selected.
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
--include-hidden repositories starting with a dot (.) are included.
--fetch perform git fetch from remote on all sub repositories.
```
<!-- help-output: end -->

## Installation

Install from the [PyPI](https://pypi.org/project/git-substatus/):
Expand Down Expand Up @@ -67,7 +95,7 @@ useful for portability matters.

## Development

This module has no module dependency outside
This tool has **no module dependency** outside
[The Python Standard Library](https://docs.python.org/3/library/index.html).

<details>
Expand Down
2 changes: 2 additions & 0 deletions get_version.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env bash

# Get the package versions as listed in the init file

variable="$1"
sed -n "s/$variable = *\"\([^ ]*\)\"/\1/p" git_substatus/__init__.py
2 changes: 1 addition & 1 deletion git_substatus/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# package version:
__version__ = "0.2.9"
__version__ = "0.2.10"

# min default supported python version:
__py_version__ = "3.8"
2 changes: 1 addition & 1 deletion git_substatus/branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ def __branch_heads(self) -> Iterator[str]:

def __current_branch_name(self, path: str) -> str:
cmd = run_git_command(path, ["symbolic-ref", "--short", "HEAD"])
out = cmd.replace("\n", "")
out = cmd["output"].replace("\n", "")
return out
20 changes: 16 additions & 4 deletions git_substatus/fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,23 @@ def do_fetch(self) -> bool:
"""
for repo in self.repos:
self.__do_git_fetch(repo)
print("All fetched.")
return True

def __do_git_fetch(self, path) -> bool:
repo_name = os.path.basename(path)
print(f'Fetching from remote "{repo_name}"')
run_git_command(path, ["fetch"])
remote_url = self.__get_remote_url(path)
if not remote_url["status"]:
repo_name = os.path.basename(path)
print(f'can\'t fetch "{repo_name}" remote not exist')
return False
print(f'fetching from remote "{remote_url["output"]}"', end="")
res = run_git_command(path, ["fetch"])
if not res["status"]:
print(" ❌")
return False
print(" ✅")
return True

def __get_remote_url(self, path) -> Dict:
cmd = run_git_command(path, ["config", "--get", "remote.origin.url"])
cmd["output"] = cmd["output"].strip("\n")
return cmd
4 changes: 2 additions & 2 deletions git_substatus/numstatus.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from git_substatus.base import *

TextType = TypedDict("TextType", {"singular": str, "plural": str})
TypeText = TypedDict("TypeText", {"singular": str, "plural": str})


class NumStatus:
Expand All @@ -9,7 +9,7 @@ class NumStatus:
e.g. stash, worktree etc.
"""

def __init__(self, repos: Tuple[str, ...], txt: TextType, fun_get_num: Callable):
def __init__(self, repos: Tuple[str, ...], txt: TypeText, fun_get_num: Callable):
self.repos = repos
self.txt = txt
self.fun_get_num = fun_get_num
Expand Down
Loading

0 comments on commit 30ea39e

Please sign in to comment.