Skip to content

Commit 9b5a77c

Browse files
authored
Merge pull request #10 from fergusmacd/feature/datetime
Adding date time to output
2 parents 3ad4c98 + 2858aa0 commit 9b5a77c

5 files changed

Lines changed: 113 additions & 37 deletions

File tree

Dockerfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,4 @@ FROM gcr.io/distroless/python3-debian11
1212
COPY --from=builder /app /app
1313
WORKDIR /app
1414
ENV PYTHONPATH /app
15-
# strictly speaking not necessary but makes it easier if working out what is happening
16-
# Useful when running locally
17-
ENV INPUT_ORGANISATION=
18-
ENV INPUT_GITHUBAPIKEY=
1915
CMD ["/app/python/main.py"]

docs/README.md

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,34 +71,100 @@ in the prettyprint formatted ASCII tables like this
7171

7272
## Usage
7373

74+
### As a GitHub Action
75+
76+
Create a file called `gha-audit.yml` in your `workflows` directory, paste the following as the contents and you are good
77+
to go
78+
79+
```
80+
name: GHA Billable Audit
81+
on: push
82+
jobs:
83+
gha-billable-minutes-report:
84+
runs-on: ubuntu-latest
85+
steps:
86+
- name: GitHub Actions Billable Usage Audit
87+
uses: fergusmacd/github-action-usage@v0.4.0
88+
# pass user input as arguments
89+
with:
90+
organisation: ${{secrets.ORGANISATION}}
91+
gitHubAPIKey: ${{secrets.GITHUBAPIKEY}} # default token in GitHub Workflow
92+
loglevel: error # not required, change to debug if misbehaving
93+
```
94+
7495
### Running Locally
7596

7697
The docker file and python script can both be run locally in the following ways.
7798

99+
### Running with Python
100+
78101
For python, from the python directory
79102

80-
```
103+
```shell
81104
pip install -r requirements.txt
82105
# default is warning, see the action.yaml for further details
83-
export LOGLEVEL=debug|info|warning|error
106+
# GHA environment variables prepend INPUT_ to values passed in
107+
export INPUT_LOGLEVEL=debug|info|warning|error
84108
export INPUT_ORGANISATION="myorg"
85109
export INPUT_GITHUBAPIKEY="***"
86110

87111
# from python directory you can run
88112
python main.py
89113
```
90114

115+
### Running with Docker
116+
91117
For Docker, run from the root directory
92118

93-
```
119+
```shell
94120
# from root directory
95121
docker build -t gha-billable-usage .
96-
97-
export LOGLEVEL=debug|info|warning|error
122+
# GHA environment variables prepend INPUT_ to values passed in
123+
export INPUT_LOGLEVEL=debug|info|warning|error
98124
export INPUT_ORGANISATION="myorg"
99125
export INPUT_GITHUBAPIKEY="***"
100-
docker run -v $PWD:/app/results -e LOGLEVEL=${LOGLEVEL} -e INPUT_ORGANISATION=${INPUT_ORGANISATION} -e INPUT_GITHUBAPIKEY=${INPUT_GITHUBAPIKEY} -it gha-billable-usage
126+
docker run -v $PWD:/app/results -e INPUT_LOGLEVEL=${INPUT_LOGLEVEL} -e INPUT_ORGANISATION=${INPUT_ORGANISATION} -e INPUT_GITHUBAPIKEY=${INPUT_GITHUBAPIKEY} -it gha-billable-usage
127+
```
128+
129+
## Common Errors
130+
131+
When problems happen, the best thing to do is set the log level to `debug` like this locally
132+
133+
```shell
134+
export LOGLEVEL="debug"
135+
```
136+
137+
Or change the loglevel input in the GHA
138+
139+
The following one happens when running locally and the `INPUT_GITHUBAPIKEY` environment variable has not been exported
140+
141+
```shell
142+
python3 main.py
143+
Traceback (most recent call last):
144+
File "/github-action-usage/python/main.py", line 5, in <module>
145+
from ghaworkflows import *
146+
File "/github-action-usage/python/ghaworkflows.py", line 9, in <module>
147+
github_api_key = getgithubapikey()
148+
File "/github-action-usage/python/common.py", line 24, in getgithubapikey
149+
return os.environ['INPUT_GITHUBAPIKEY']
150+
File "/usr/local/Cellar/python@3.9/3.9.12/Frameworks/Python.framework/Versions/3.9/lib/python3.9/os.py", line 679, in __getitem__
151+
raise KeyError(key) from None
152+
KeyError: 'INPUT_GITHUBAPIKEY'
153+
154+
```
101155

156+
This error happens when the PAT has expired or does not have sufficient permissions
157+
158+
```shell
159+
python3 main.py
160+
Traceback (most recent call last):
161+
File "/github-action-usage/python/main.py", line 92, in <module>
162+
main()
163+
File "/github-action-usage/python/main.py", line 30, in main
164+
repoNames = getreposfromorganisation(org)
165+
File "/github-action-usage/python/ghorg.py", line 27, in getreposfromorganisation
166+
totalPrivateRepos = json_data["total_private_repos"]
167+
KeyError: 'total_private_repos'
102168
```
103169

104170
## Relevant Links
@@ -124,6 +190,7 @@ There are plenty of tutorials on prettyprint, I used this one:
124190
- export to excel and upload to packages
125191
- sorting by different criteria, e.g. tags or ownership
126192
- test coverage
193+
- test scripts
127194
- colouring console
128195
- any requests?
129196

python/customlogger.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
DEBUG_FORMAT = (
77
"%(asctime)s [%(levelname)s]: %(message)s in %(pathname)s:%(lineno)d")
88

9-
AUDIT_FORMAT = (
10-
"%(message)s")
11-
LOG_LEVEL = os.environ.get('LOGLEVEL', 'WARNING').upper()
12-
9+
LOG_LEVEL = os.environ.get('INPUT_LOGLEVEL', 'WARNING').upper()
1310
# the debug logger is for figuring out what is going on
1411
debug_logger = logging.getLogger("debug")
1512
debug_logger.setLevel(LOG_LEVEL)
@@ -24,7 +21,7 @@ def getlogger():
2421

2522

2623
def main():
27-
getlogger.error('Who knows where they got the money?!')
24+
getlogger.error('The TV people are back!')
2825

2926

3027
if __name__ == "__main__":

python/ghaworkflows.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import requests
44

5-
from customlogger import getlogger
65
# get any header or key values we need
7-
from python.common import getgithubapikey
6+
from common import getgithubapikey
7+
from customlogger import getlogger
88

99
github_api_key = getgithubapikey()
1010
headers = {"Authorization": "token {}".format(github_api_key),

python/main.py

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# The starting point
22
import os
3+
from datetime import datetime
34

45
from prettytable import PrettyTable
56

6-
from python.customlogger import getlogger
7-
from python.ghaworkflows import getrepoworkflows
8-
from python.ghorg import getreposfromorganisation
7+
from customlogger import getlogger
8+
from ghaworkflows import getrepoworkflows
9+
from ghorg import getreposfromorganisation
910

1011

1112
class RepoData:
@@ -24,6 +25,7 @@ def __init__(self, name, usage, actions):
2425
logger = getlogger()
2526

2627
repo_name_column_header = "Repo Name"
28+
datetime_format = "%Y-%m-%d %H:%M"
2729

2830

2931
def main():
@@ -32,23 +34,23 @@ def main():
3234
logger.info(f'*************** Getting repos for {org} ***************')
3335
# Get all the repo names for the org, will page results too
3436
# repo names are returned sorted
35-
repoNames = getreposfromorganisation(org)
37+
repo_names = getreposfromorganisation(org)
3638

37-
reposUsage = []
38-
totalCosts = dict.fromkeys(['UBUNTU', 'MACOS', 'WINDOWS'], 0)
39+
repos_usage = []
40+
total_costs = dict.fromkeys(['UBUNTU', 'MACOS', 'WINDOWS'], 0)
3941
# Collect the data from each repo
40-
for repoName in repoNames:
42+
for repo_name in repo_names:
4143
actions = []
42-
repoData = RepoData(repoName, dict.fromkeys(['UBUNTU', 'MACOS', 'WINDOWS'], 0), actions)
43-
logger.info(f"*************** Repo Name {repoData.name} ***************")
44-
getrepoworkflows(org, repoData)
45-
reposUsage.append(repoData)
46-
logger.info(f"*************** Repo Usage Summary {repoData.usage} ***************")
47-
totalCosts["UBUNTU"] += repoData.usage["UBUNTU"]
48-
totalCosts["MACOS"] += repoData.usage["MACOS"]
49-
totalCosts["WINDOWS"] += repoData.usage["WINDOWS"]
50-
51-
logger.info(f"***************Total Costs: {totalCosts} *******************")
44+
repo_data = RepoData(repo_name, dict.fromkeys(['UBUNTU', 'MACOS', 'WINDOWS'], 0), actions)
45+
logger.info(f"*************** Repo Name {repo_data.name} ***************")
46+
getrepoworkflows(org, repo_data)
47+
repos_usage.append(repo_data)
48+
logger.info(f"*************** Repo Usage Summary {repo_data.usage} ***************")
49+
total_costs["UBUNTU"] += repo_data.usage["UBUNTU"]
50+
total_costs["MACOS"] += repo_data.usage["MACOS"]
51+
total_costs["WINDOWS"] += repo_data.usage["WINDOWS"]
52+
53+
logger.info(f"***************Total Costs: {total_costs} *******************")
5254
# table tp print out per repo/workflow
5355
# Repo names are already sorted and we don't want to sort on tables
5456
# as order would mess up with totals
@@ -59,8 +61,8 @@ def main():
5961
summary_table: PrettyTable = PrettyTable()
6062
summary_table.field_names = [repo_name_column_header, "Ubuntu", "MacOS", "Windows"]
6163
summary_table.align[repo_name_column_header] = "l"
62-
63-
for repo in reposUsage:
64+
validate_total_costs = dict.fromkeys(['UBUNTU', 'MACOS', 'WINDOWS'], 0)
65+
for repo in repos_usage:
6466
summary_table.add_row([repo.name, repo.usage["UBUNTU"], repo.usage["MACOS"], repo.usage["WINDOWS"]])
6567
first_row: bool = True
6668
if not repo.actions:
@@ -70,14 +72,28 @@ def main():
7072
workflow_table.add_row([repo.name, action.name, action.workflow['UBUNTU'], action.workflow['MACOS'],
7173
action.workflow['WINDOWS']])
7274
first_row = False
75+
validate_total_costs["UBUNTU"] += action.workflow["UBUNTU"]
76+
validate_total_costs["MACOS"] += action.workflow["MACOS"]
77+
validate_total_costs["WINDOWS"] += action.workflow["WINDOWS"]
7378
else:
7479
workflow_table.add_row(["", action.name, action.workflow['UBUNTU'], action.workflow['MACOS'],
7580
action.workflow['WINDOWS']])
81+
validate_total_costs["UBUNTU"] += action.workflow["UBUNTU"]
82+
validate_total_costs["MACOS"] += action.workflow["MACOS"]
83+
validate_total_costs["WINDOWS"] += action.workflow["WINDOWS"]
84+
7685
workflow_table.add_row(["--------", "--------", "-----", "-----", "-----"])
7786

7887
summary_table.add_row(["---------", "----", "----", "----"])
79-
summary_table.add_row(["Total Costs", totalCosts["UBUNTU"], totalCosts["MACOS"], totalCosts["WINDOWS"]])
88+
summary_table.add_row(
89+
["Billable Minutes " + datetime.now().strftime(datetime_format), total_costs["UBUNTU"],
90+
total_costs["MACOS"],
91+
total_costs["WINDOWS"]])
8092
summary_table.add_row(["---------", "----", "----", "----"])
93+
workflow_table.add_row(["Billable Minutes " + datetime.now().strftime(datetime_format), "",
94+
validate_total_costs["UBUNTU"], validate_total_costs["MACOS"],
95+
validate_total_costs["WINDOWS"]])
96+
8197
print(summary_table)
8298
print(workflow_table)
8399

0 commit comments

Comments
 (0)