Skip to content

Commit 2b3af12

Browse files
authored
Merge branch 'main' into dependabot/pip/fastcore-1.8.2
Signed-off-by: Roger Barker <roger.barker@swirldslabs.com>
2 parents f9958bd + 0211125 commit 2b3af12

7 files changed

Lines changed: 92 additions & 17 deletions

File tree

README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,53 @@
11
![App icon](images/FullSizeIcon.png)
22

33
# VersionTwo
4+
45
Hackathon project: CLI tool to generate static planning view of issues for better team planning
56

6-
# 🚀 Getting Started
7-
## 📦 Requirements
7+
## 🚀 Getting Started
8+
9+
### 📦 Requirements
10+
811
The following dependencies are required to run the program:
12+
913
- Python 3.x
1014
- `brew install python3`
1115
- Python `pyenv` and `pyenv-virtualenv`
12-
- `brew install pyenv pyenv-virtualenv`
16+
- `brew install pyenv pyenv-virtualenv`
1317
- GitHub Command Line Interface (CLI) `gh`
1418
- `brew install gh`
1519

16-
## 💻 Setup
20+
### 💻 Setup
21+
1722
- Authentication through `gh auth login`
18-
- Set the appropriate token permissions: `gh auth refresh --scopes read:project`
19-
- Note: The team must have `read` permissions on the Project Board in order to view the issues on the board.
20-
- Set the `GITHUB_TOKEN` environment variable: `export GITHUB_TOKEN=$(gh auth token)`
21-
- Set the `GITHUB_UNAME` environment variable: `export GITHUB_UNAME=$(gh auth status | grep "(GITHUB_TOKEN)" | cut -d " " -f9)`
23+
- Source the [setup-gh.sh](src/setup-gh.sh) script to configure your gh environment variables
24+
25+
```bash
26+
source src/setup-gh.sh # follow the prompts
27+
```
28+
29+
- Sets the appropriate token scopes: `read: project`.
30+
- Note: The team must have `read` permissions on the Project Board in order to view the issues on the board.
31+
- Sets the `GITHUB_TOKEN` environment variable.
32+
- Sets the `GITHUB_UNAME` environment variable.
2233
- Run `make install` inside the repo directory to configure the appropriate versions of dependencies.
2334

24-
## 🛠 Usage
35+
### 🛠 Usage
36+
2537
To run the main script, change to the current directory of the script, then run:
2638

2739
`python version2.py --output-file "<filename.json>" --temp-dir "<temp.dir>" --include-project <project name>`
2840

2941
See the `--help` menu for full list of filter functionality.
3042

31-
# Background
43+
## Background
44+
3245
GitHub users have issues assigned to themselves or a team they are a member of. These issues can be viewed on a
3346
Project board, which captures the issues in swim lanes. The Project board can only automate with a single organization,
3447
meaning users who work in more than one org do not have a single location to view all issues. This leads to fragmented
3548
planning and execution.
3649

37-
# How does it work?
50+
## How does it work?
3851

3952
```mermaid
4053
%% A · System-Architecture Diagram (≤25 nodes)
@@ -52,7 +65,8 @@ classDef api fill:#e3f2fd,stroke:#2196f3;
5265
classDef cli fill:#f1f8e9,stroke:#7cb342;
5366
```
5467

55-
# Our Solution
68+
## Our Solution
69+
5670
Our python script will query the GitHub API for all issues associated with the appropriate filters provided to the CLI
5771
tool. The output will be a static HTML page showing all issues in swim lanes. This provides a comprehensive overview
58-
of all issues the team or user has assigned.
72+
of all issues the team or user has assigned.

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
certifi==2025.4.26
2-
charset-normalizer==3.4.1
2+
charset-normalizer==3.4.2
33
fastcore==1.8.2
44
ghapi==1.0.6
55
idna==3.10

src/setup-gh.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#! /usr/bin/env bash
2+
3+
# This script sets up the authentication parameters for the GitHub CLI
4+
# that are needed to run the version2.py script.
5+
# It is assumed that the GitHub CLI is already installed and configured
6+
# on the system.
7+
# The script will update the user's github token to have the needed scopes,
8+
# set the token in the environment and pull in the gh users username as
9+
# another environment variable.
10+
11+
# Exit if not sourced
12+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
13+
echo "ERROR: This script must be sourced, not executed."
14+
echo "Usage: source ${BASH_SOURCE[0]}"
15+
exit 1
16+
fi
17+
18+
# Check if the GITHUB_TOKEN environment variable is already set
19+
set_gh_token() {
20+
# If not set, set the scopes for the token and set the environment variable
21+
gh auth refresh --scopes read:project
22+
23+
# Set the GITHUB_TOKEN environment variable
24+
export GITHUB_TOKEN=$(gh auth token)
25+
}
26+
27+
configure_gh_env() {
28+
if [ -n "${GITHUB_TOKEN}" ]; then
29+
echo "GITHUB_TOKEN needs to be unset to update the token scopes."
30+
unset GITHUB_TOKEN
31+
fi
32+
33+
# set the gh_token
34+
set_gh_token
35+
36+
# set the gh_user
37+
export GITHUB_UNAME=$(gh api user --jq .login)
38+
39+
echo "GITHUB_TOKEN is set. Length is: ${#GITHUB_TOKEN}"
40+
echo "GITHUB_UNAME is set to: ${GITHUB_UNAME}"
41+
}
42+
43+
configure_gh_env

src/static_site_generator.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self):
1515
'tasks': []
1616
}
1717

18-
def generate_site(self, data:list=None, projects:list[str]=None, output_file='./_site/index.html') -> None:
18+
def generate_site(self, data:list=None, filter_done:bool=False, projects:list[str]=None, output_file='./_site/index.html') -> None:
1919
logging.info("Generating Static Site")
2020
env = Environment(loader=FileSystemLoader('templates'))
2121
template = env.get_template('kaban_board.html')
@@ -34,9 +34,14 @@ def generate_site(self, data:list=None, projects:list[str]=None, output_file='./
3434
for task in self.GENERATOR_DATA["tasks"]:
3535
if "status" not in task:
3636
task["status"] = "NONE"
37+
elif task["status"].upper() == "DONE" and filter_done == True:
38+
# remove the task from the list
39+
self.GENERATOR_DATA["tasks"].remove(task)
40+
continue
3741
else:
3842
task["status"] = task["status"].upper()
3943
statuses.add(task["status"])
44+
print(f"[bold cyan]Adding {len(self.GENERATOR_DATA["tasks"])} tasks to the site[/bold cyan]")
4045

4146
# Extract unique statuses from the tasks list
4247
unique_statuses:list[str] = sorted(statuses)

src/version2config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ def init_parser(self):
3737
help="The temporary directory to store the json data files"
3838
)
3939

40+
parser.add_argument(
41+
"--filter-done",
42+
dest="filter_done",
43+
action="store_true",
44+
help="Filter out all issues and PRs that are marked as done"
45+
)
46+
4047
parser.add_argument(
4148
"--include-project",
4249
dest="include_project",
@@ -176,6 +183,7 @@ def init_parser(self):
176183
self.exclude_label = parsed_args.exclude_label
177184
self.publish_board = parsed_args.publish_board
178185
self.exclude_team = parsed_args.exclude_team
186+
self.filter_done = parsed_args.filter_done if parsed_args.filter_done else False
179187

180188
def init_logger(self):
181189
logging.basicConfig(level=self.LOG_LEVEL, format=self.LOG_FORMAT)
@@ -187,6 +195,7 @@ def display_config(self):
187195
logging.info("Configuration:")
188196
logging.info(f"Output File: {self.output_file}")
189197
logging.info(f"Temporary Directory: {self.temp_dir}")
198+
logging.info(f"Filter Done: {self.filter_done}")
190199
logging.info(f"Include Project: {self.include_project}")
191200
logging.info(f"Include User: {self.include_user}")
192201
logging.info(f"Include Repository: {self.include_repository}")

src/version2query.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pathlib import Path
88
from ghapi.all import GhApi
99
from rich import print
10+
from time import sleep
1011
from .queryFilter import QueryFilter
1112

1213
class Version2Query:
@@ -130,7 +131,9 @@ def fetch_project_items(self, projects:list[dict]) -> bool:
130131
# use os.system instead of subprocess.run or subprocess.popen to avoid creating a new
131132
# process space where we would need to copy environment variables into it (possibly
132133
# exposing things like GH_TOKEN or other environment variables)
133-
os.system( f'gh project item-list --owner "{org}" {number} --format json > "{out_path}"')
134+
# The default limit on the gh project item-list command is 30 items. We need to update
135+
# the limit to 1000 to get all items in the project.
136+
os.system( f'gh project item-list --owner "{org}" {number} --limit 1000 --format json > "{out_path}"')
134137

135138
if not out_path.exists():
136139
print(f"[red]Failed to fetch items for project: {title} (Org: {org}, Project: {number})[/red]")
@@ -148,6 +151,7 @@ def consolidate_items(self) -> bool:
148151
with open(file) as f:
149152
data = json.load(f)
150153
items = data.get("items", [])
154+
print(f"[green]Found {len(items)} items in {file}[/green]")
151155
all_items.extend(items)
152156
except Exception as e:
153157
print(f"[red]Error consolidating items: {e}[/red]")

version2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def main():
4646
with open(output_file, 'r') as f:
4747
data = json.load(f)
4848

49-
ss_gen.generate_site(data=data, projects=filters["include_projects"])
49+
ss_gen.generate_site(data=data, filter_done=config.filter_done, projects=filters["include_projects"])
5050

5151
if __name__ == "__main__":
5252
main()

0 commit comments

Comments
 (0)