Skip to content

Commit 30ea39e

Browse files
author
Metin Yazici
committed
Show remote details while fetching
Closes #10
1 parent 21959dd commit 30ea39e

19 files changed

+248
-65
lines changed

.github/scripts/check_text.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import argparse
2+
import difflib
3+
import subprocess
4+
5+
6+
def get_file_lines(filename):
7+
with open(filename) as file:
8+
lines = file.readlines()
9+
return lines
10+
11+
12+
def get_readme_text(lines, start_pattern, end_pattern):
13+
start_index = lines.index(start_pattern) + 1
14+
end_index = lines.index(end_pattern)
15+
sublines = lines[start_index:end_index]
16+
# remove backticks
17+
sublines = [line for line in sublines if line != "```\n"]
18+
text = "".join(sublines)
19+
return text
20+
21+
22+
def get_command_output(command):
23+
cmd = command.split(" ")
24+
out = None
25+
run = subprocess.run(cmd, stdout=subprocess.PIPE)
26+
if run.returncode == 0:
27+
out = run.stdout.decode("utf-8")
28+
return out
29+
raise ValueError("could not get the help text")
30+
31+
32+
def check_if_equal(command_output, readme_text):
33+
equal = command_output == readme_text
34+
if not equal:
35+
d1 = command_output.splitlines(keepends=True)
36+
d2 = readme_text.splitlines(keepends=True)
37+
print("".join(difflib.ndiff(d1, d2)))
38+
print("* * *")
39+
raise ValueError("readme text is not up-to-date")
40+
print("help output in readme is up-to-date")
41+
42+
43+
def get_cli_args():
44+
parser = argparse.ArgumentParser(
45+
description="Check a block of text in a file against an output returned by a command or any other piped output."
46+
)
47+
parser.add_argument("--file", required=True, help="file to check the block")
48+
parser.add_argument("--pattern_start", required=True, help="start pattern")
49+
parser.add_argument("--pattern_end", required=True, help="end pattern")
50+
parser.add_argument("--command", required=True, help="command to check the output")
51+
parsed_args = parser.parse_args()
52+
# this solution isn't great as it also removes the other unicode characters
53+
# but so far, I don't have any use case so fine.
54+
parsed_args.pattern_start = bytes(parsed_args.pattern_start, "utf-8").decode(
55+
"unicode_escape"
56+
)
57+
parsed_args.pattern_end = bytes(parsed_args.pattern_end, "utf-8").decode(
58+
"unicode_escape"
59+
)
60+
return parsed_args
61+
62+
63+
def main():
64+
args = get_cli_args()
65+
lines = get_file_lines(args.file)
66+
readme_text = get_readme_text(lines, args.pattern_start, args.pattern_end)
67+
command_output = get_command_output(args.command)
68+
check_if_equal(command_output, readme_text)
69+
return 0
70+
71+
72+
if __name__ == "__main__":
73+
main()

.github/workflows/CI.yml

+48-13
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,69 @@ on: [push, pull_request]
44

55
jobs:
66

7-
pre-commit:
7+
prepare:
8+
name: "Prepare for CI"
9+
runs-on: ubuntu-latest
10+
timeout-minutes: 1
11+
12+
outputs:
13+
py_version: ${{ steps.versions.outputs.py_version }}
14+
15+
steps:
16+
- uses: actions/checkout@v2
17+
18+
- name: Get versions
19+
id: versions
20+
run: |
21+
py_version="$(./get_version.sh __py_version__)"
22+
echo "::set-output name=py_version::$py_version"
23+
24+
pre_commit:
825
name: pre-commit checks
926
runs-on: ubuntu-latest
1027
timeout-minutes: 2
28+
1129
steps:
12-
- uses: actions/checkout@v2
13-
- uses: actions/setup-python@v2
14-
- uses: pre-commit/[email protected]
30+
- uses: actions/checkout@v2
31+
- uses: actions/setup-python@v2
32+
- uses: pre-commit/[email protected]
1533

16-
tests:
17-
name: Run tests with python ${{ matrix.python-version }}
34+
check_readme:
35+
name: "Check README help text"
36+
needs: [ prepare ]
1837
runs-on: ubuntu-latest
1938
timeout-minutes: 2
2039

21-
strategy:
22-
matrix:
23-
python-version:
24-
- "3.8"
25-
- "3.9"
26-
- "3.10"
40+
steps:
41+
- uses: actions/checkout@v2
42+
- uses: actions/setup-python@v2
43+
with:
44+
python-version: ${{ needs.prepare.outputs.version }}
45+
46+
- name: Install
47+
run: make install
48+
49+
- name: Check if help text in README is up-to-date
50+
run: |
51+
python .github/scripts/check_text.py \
52+
--file "README.md" \
53+
--pattern_start '<!-- help-output: start -->\n' \
54+
--pattern_end '<!-- help-output: end -->\n' \
55+
--command "git-substatus --help"
56+
57+
tests:
58+
name: "Run tests"
59+
needs: [ prepare ]
60+
runs-on: ubuntu-latest
61+
timeout-minutes: 2
2762

2863
steps:
2964
- uses: actions/checkout@v2
3065

3166
- name: Setup python
3267
uses: actions/setup-python@v2
3368
with:
34-
python-version: ${{ matrix.python-version }}
69+
python-version: ${{ needs.prepare.outputs.version }}
3570

3671
- name: Install dev dependencies
3772
run: |

.github/workflows/integration.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Integration
2+
3+
on:
4+
push:
5+
branches:
6+
- "master"
7+
8+
jobs:
9+
10+
test_docker:
11+
name: Build and test Docker image
12+
runs-on: ubuntu-latest
13+
timeout-minutes: 3
14+
15+
steps:
16+
- uses: actions/checkout@v2
17+
18+
- name: Build the Docker image
19+
run: make VERSION=test docker-build
20+
21+
- name: Docker container can be run
22+
run: |
23+
./tests/gen_test_repos.sh && \
24+
docker run --rm -t \
25+
-v "$(pwd)":/"$(pwd)" \
26+
-w "$(pwd)" strboul/git-substatus:test \
27+
tests/generated-test-proj-dir

.github/workflows/release.yml

+4-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Release
33
on:
44
push:
55
tags:
6-
- "v*"
6+
- "v*.*.*"
77

88
jobs:
99

@@ -47,21 +47,18 @@ jobs:
4747
uses: softprops/action-gh-release@v1
4848
with:
4949
body: |
50-
# v${{ needs.prepare.outputs.version }}
50+
Install this specific release with:
5151
52-
## PyPI
52+
- PyPI
5353
5454
```bash
5555
pip install git-substatus==${{ needs.prepare.outputs.version }}
5656
```
57-
58-
## DockerHub
57+
- Dockerhub
5958
6059
```bash
6160
docker run --rm -t -v "$(pwd)":/"$(pwd)" -w "$(pwd)" strboul/git-substatus:${{ needs.prepare.outputs.version }}
6261
```
63-
# TODO which files actually?
64-
files: dist/
6562
6663
publish_pypi:
6764
name: "Publish to PyPI (https://pypi.org/project/git-substatus/)"

.pre-commit-config.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# See https://pre-commit.com for more information
22
# See https://pre-commit.com/hooks.html for more hooks
3-
fail_fast: true
43
repos:
54
- repo: https://github.com/pre-commit/pre-commit-hooks
65
rev: v4.0.1

Dockerfile

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ LABEL HOMEPAGE="https://github.com/strboul/git-substatus"
66

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

9-
ARG CONTAINER_PATH="/opt/git-substatus/"
10-
RUN mkdir -p "$CONTAINER_PATH"
11-
COPY setup.py README.md "$CONTAINER_PATH"
12-
COPY git_substatus/ "$CONTAINER_PATH"/git_substatus
13-
RUN cd "$CONTAINER_PATH" && \
14-
python -m pip install --upgrade pip
9+
ARG INSTALL_PATH="/opt/git-substatus/"
10+
RUN mkdir -p "$INSTALL_PATH"
11+
COPY setup.py README.md get_version.sh "$INSTALL_PATH"
12+
COPY git_substatus/ "$INSTALL_PATH"/git_substatus
13+
RUN cd "$INSTALL_PATH" && \
14+
python -m pip install --upgrade pip .
15+
RUN rm -rf "$INSTALL_PATH"
1516

1617
ENTRYPOINT ["git-substatus"]

Makefile

+10-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ VERSION := $(shell ./get_version.sh __version__)
1717
PY_VERSION := $(shell ./get_version.sh __py_version__)
1818

1919

20-
all: typecheck black test install clean
20+
all: typecheck black test install docker-build clean
2121

2222

2323
install:
@@ -48,15 +48,22 @@ coverage:
4848
black:
4949
$(call check_pip_module,"black")
5050
$(call echo_section,"checking code formatting with black")
51-
python -m black --check git_substatus
51+
python -m black --check .
52+
53+
54+
black-apply:
55+
$(call check_pip_module,"black")
56+
$(call echo_section,"applying code formatting with black")
57+
python -m black .
5258

5359

5460
clean:
5561
$(call echo_section,"cleaning")
5662
find . -depth -name __pycache__ -exec rm -rf {} \; && \
5763
find . -depth -name *.pyc -exec rm -rf {} \; && \
5864
find . -depth -name *.mypy_cache -exec rm -rf {} \; && \
59-
find . -depth -name .coverage -exec rm {} \;
65+
find . -depth -name .coverage -exec rm {} \; && \
66+
rm -rf tests/generated-test-proj-dir
6067

6168

6269
tag-create:

README.md

+29-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

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

2930
See more at `git-substatus --help`
3031

32+
<!-- help-output: start -->
33+
```
34+
usage: git-substatus [-h] [-v] [--include-hidden] [--fetch] [path]
35+
36+
See subfolders' git status
37+
===========================
38+
39+
The output consists of four columns:
40+
41+
repo name | branch head | status | git stashes (if any)
42+
43+
The string (*WT) seen next to the repo names shows that the
44+
repo has some git worktrees. See more:
45+
<https://git-scm.com/docs/git-worktree>
46+
47+
positional arguments:
48+
path a path to where you want to see git substatuses. If empty, the
49+
current working directory is selected.
50+
51+
optional arguments:
52+
-h, --help show this help message and exit
53+
-v, --version show program's version number and exit
54+
--include-hidden repositories starting with a dot (.) are included.
55+
--fetch perform git fetch from remote on all sub repositories.
56+
```
57+
<!-- help-output: end -->
58+
3159
## Installation
3260

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

6896
## Development
6997

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

73101
<details>

get_version.sh

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env bash
22

3+
# Get the package versions as listed in the init file
4+
35
variable="$1"
46
sed -n "s/$variable = *\"\([^ ]*\)\"/\1/p" git_substatus/__init__.py

git_substatus/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# package version:
2-
__version__ = "0.2.9"
2+
__version__ = "0.2.10"
33

44
# min default supported python version:
55
__py_version__ = "3.8"

git_substatus/branch.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ def __branch_heads(self) -> Iterator[str]:
2020

2121
def __current_branch_name(self, path: str) -> str:
2222
cmd = run_git_command(path, ["symbolic-ref", "--short", "HEAD"])
23-
out = cmd.replace("\n", "")
23+
out = cmd["output"].replace("\n", "")
2424
return out

git_substatus/fetch.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,23 @@ def do_fetch(self) -> bool:
1212
"""
1313
for repo in self.repos:
1414
self.__do_git_fetch(repo)
15-
print("All fetched.")
1615
return True
1716

1817
def __do_git_fetch(self, path) -> bool:
19-
repo_name = os.path.basename(path)
20-
print(f'Fetching from remote "{repo_name}"')
21-
run_git_command(path, ["fetch"])
18+
remote_url = self.__get_remote_url(path)
19+
if not remote_url["status"]:
20+
repo_name = os.path.basename(path)
21+
print(f'can\'t fetch "{repo_name}" remote not exist')
22+
return False
23+
print(f'fetching from remote "{remote_url["output"]}"', end="")
24+
res = run_git_command(path, ["fetch"])
25+
if not res["status"]:
26+
print(" ❌")
27+
return False
28+
print(" ✅")
2229
return True
30+
31+
def __get_remote_url(self, path) -> Dict:
32+
cmd = run_git_command(path, ["config", "--get", "remote.origin.url"])
33+
cmd["output"] = cmd["output"].strip("\n")
34+
return cmd

git_substatus/numstatus.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from git_substatus.base import *
22

3-
TextType = TypedDict("TextType", {"singular": str, "plural": str})
3+
TypeText = TypedDict("TypeText", {"singular": str, "plural": str})
44

55

66
class NumStatus:
@@ -9,7 +9,7 @@ class NumStatus:
99
e.g. stash, worktree etc.
1010
"""
1111

12-
def __init__(self, repos: Tuple[str, ...], txt: TextType, fun_get_num: Callable):
12+
def __init__(self, repos: Tuple[str, ...], txt: TypeText, fun_get_num: Callable):
1313
self.repos = repos
1414
self.txt = txt
1515
self.fun_get_num = fun_get_num

0 commit comments

Comments
 (0)