Github-workflow-dashboard - WEB and CLI dashboard for github action workflows

CLI capable of retrieving github action workflows stats

dashboard-image

Example usage

  • Dashboard mod
github-workflow-dashboard -server-mod -owner Azure -repo k8s-deploy  "Create release PR" "Tag and create release draft"
  • CLI mod
github-workflow-dashboard -owner Azure -repo k8s-deploy  "Create release PR" "Tag and create release draft"

Binaries

Prebuild binaries can be found here.
In order to rebuild the project run make go-build

Manual

Usage: github-workflow-dashboard [global flags] '<workflow>'

global flags:
  -format string
    	The format in which to print the workflow stats (ascii, json) (default "ascii")
  -latest-only
    	Fetch only the latest run of the github workflow
  -owner string
    	Github repository owner
  -repo string
    	Github repository
  -server-mod
    	Start a web server that periodically pulls github workflow stats
  -server-poll-interval int
    	Interval in minutes used to poll github workflows (default 5)
  -server-port int
    	The port on which to start the web server if running in server-mod (default 8080)
  -token string
    	Github API token, see: https://docs.github.coim/en/articles/creating-an-access-token-for-command-line-use
  -version
    	Print version and exit

example:
	github-workflow-dashboard -owner Azure -repo k8s-deploy  "Create release PR" "Tag and create release draft"

Environment variables

Command line args take precedence over env variables. If a cmd arg is not passed and an env variable is present then the env variable will be used.

WORKFLOW_TOKEN
WORKFLOW_OWNER
WORKFLOW_REPO
WORKFLOW_LATEST_ONLY
WORKFLOW_FORMAT
WORKFLOW_SERVER_MOD
WORKFLOW_SERVER_PORT 
WORKFLOW_SERVER_POLL_INTERVAL
WORKFLOW_CSV

Running with docker

  • Using Make
make docker-build
make docker-run
  • Using Docker
docker build -t github-workflow-dashboard .

docker run -it -rm -e WORKFLOW_SERVER_MOD=true -e WORKFLOW_OWNER="Azure" -e WORKFLOW_REPO="k8s-deploy" -p 8080:8080 github-workflow-dashboard
Comments
  • Exe displayed only the last run

    Exe displayed only the last run

    I I tried the exe-file on windows. It worked very well! The output displayed only the last run. Did I forgot to add a parameter?

    Screenshot of the workflows at github image

    Screnshot of exe output image

  • 同学,您这个项目引入了80个开源组件,存在2个漏洞,辛苦升级一下

    同学,您这个项目引入了80个开源组件,存在2个漏洞,辛苦升级一下

    检测到 NewestUser/github-workflow-dashboard 一共引入了80个开源组件,存在2个漏洞

    漏洞标题:go-yaml < 2.2.8拒绝服务漏洞
    缺陷组件:gopkg.in/[email protected]
    漏洞编号:CVE-2019-11254
    漏洞描述:gopkg.in/yaml.v2是go语言中用于处理yaml格式的包。
    在2.2.8之前的版本中,处理恶意的yaml数据时,会导致CPU资源耗尽。
    漏洞由Kubernetes开发者在fuzz测试中发现并提交修复补丁。
    国家漏洞库信息:https://www.cnvd.org.cn/flaw/show/CNVD-2020-35519
    影响范围:(∞, 2.2.8)
    最小修复版本:2.2.8
    缺陷组件引入路径:main@->gopkg.in/[email protected]
    

    另外还有2个漏洞,详细报告:https://mofeisec.com/jr?p=a6272e

  • Fix for 404-error exit on missing-param workflow run

    Fix for 404-error exit on missing-param workflow run

    Hi. It's a nice CLI utility!

    I'm experimenting around the CLI feature and faced an error when -parse-params is used for non-server mode. This PR is a trial to avoid an error exit.

    Summary

    • When -parse-params is used, unexpected status code: 404 Not Found can occur due to missing workflow params.
      • This can occur when github workflow failed before starting (ex. invalid workflow yaml definition).

    Changes

    • Proceed the processing after emitting the warning logs in a similar way as server mode (referenced the following logic).
      • https://github.com/NewestUser/github-workflow-dashboard/blob/master/backend/server.go#L189

    Behavior

    Before

    $ make go-build && ./bin/github-workflow-dashboard-darwin-amd64 -parse-params -owner parroty -repo workflow-dashboard-test -token $GITHUB_TOKEN
    env GOOS=linux GOARCH=386 go build -o ./bin/github-workflow-dashboard-linux-386 ./cmd/github-workflow-dashboard
    env GOOS=windows GOARCH=386 go build -o ./bin/github-workflow-dashboard-windows-386.exe ./cmd/github-workflow-dashboard
    env GOOS=darwin GOARCH=amd64 go build -o ./bin/github-workflow-dashboard-darwin-amd64 ./cmd/github-workflow-dashboard
    2022/02/25 23:05:14 unexpected status code: 404 Not Found
    

    After

    $ make go-build && ./bin/github-workflow-dashboard-darwin-amd64 -parse-params -owner parroty -repo workflow-dashboard-test -token $GITHUB_TOKEN
    env GOOS=linux GOARCH=386 go build -o ./bin/github-workflow-dashboard-linux-386 ./cmd/github-workflow-dashboard
    env GOOS=windows GOARCH=386 go build -o ./bin/github-workflow-dashboard-windows-386.exe ./cmd/github-workflow-dashboard
    env GOOS=darwin GOARCH=amd64 go build -o ./bin/github-workflow-dashboard-darwin-amd64 ./cmd/github-workflow-dashboard
    WARN[0004] Failed fetching workflow params for workflow: Run Tests runId: 1898674888, it will be ommitedd, err: unexpected status code: 404 Not Found
    |-----------|---|-----------|--------|----------|-----------------|------------|-------------------------------|-------------------------------|------------|
    | WORKFLOW  | # |  STATUS   | BRANCH | COMMITER |   COMMIT MSG    |   COMMIT   |          COMMIT TIME          |           RUN TIME            |   PARAMS   |
    |-----------|---|-----------|--------|----------|-----------------|------------|-------------------------------|-------------------------------|------------|
    | Run Tests | 3 | completed | main   | parroty  | Update test.yml | aad4c036fc | 2022-02-25 13:05:15 +0000 UTC | 2022-02-25 13:05:16 +0000 UTC | key2: val2 |
    |           |    |           |        |          |                 |            |                               |                               | key1: val1 |
    |           |    |           |        |          |                 |            |                               |                               |            |
    | Run Tests | 2 | completed | main   | parroty  | Update test.yml | 7588024cc6 | 2022-02-25 12:50:58 +0000 UTC | 2022-02-25 12:51:00 +0000 UTC |            |
    | Run Tests | 1 | completed | main   | parroty  | Add dummy yml   | f0fd2458d6 | 2022-02-25 12:41:27 +0000 UTC | 2022-02-25 12:41:36 +0000 UTC |            |
    
  • Handle the lost run problem

    Handle the lost run problem

    1. Add a workflow with a name 'CI1'
    2. Run some tests
    3. It should be shown like that image
    4. Rename the workflow file image
    5. Your old runs are only listed at "all workflows".

    If you have a lot's of runs you wouln't find them anymore. They get be lost. AND (the worst) they need a lots of space. They should be deleted! In the python script at issue https://github.com/NewestUser/github-workflow-dashboard/issues/9 you can see how to filter the lost-runs.

    Cheers

  • Add option to delete runs

    Add option to delete runs

    @NewestUser another problem of github is to delete workflow runs. Nobody wants to klick thousands of buttons. I added a python script into my tooling.

    If you want to, you can implement this into your dashboard. (Did you know the "Lost-runs" problem, by changing the name of a worklfow name in the yml file?)

    Greetings and best wishes. I use PyGithub.

        def cleanup_workflow_runs(self, repo, workflow_selection=None, run_selection=None, interactive:bool=True):
            ''' Delete workflow runs.
            Actual you have to do this manual, because a good-usable functionality to clean up the workflow runs on the github page is not implemented... This is lame.
            This function will help you and saves your time.
            see: https://support.github.com/ticket/personal/0/1260794
    
            In case of interactive-mode
            1. List workflows
                -> Select workflow: Name, >LOST-RUNS< or all
            2. List selectable runs
                -> Select runs to delete (black list): Run Number(s), all, all-except-last, cancelled, failure or success
                -> Select runs to not delete (white list): Run Number(s)
            3. List selected runs (to delete)
                -> Confirm
    
            Parameter:
                repo: Repository with type 'Github.get_repo()' or as str.
                workflow_selection: default = None, e.g. 'Self Hosted CI'
                run_selection: default = None, e.g. 'all', 'all-except-last', 'cancelled', 'failure' or 'success'
                interactive: default = True. If True the function will expact yout input/selection. If False all workflow-runs will be deleted.
            '''
            # input management
            if isinstance(repo, str):
                repo = self.get_repo(repo)
            if repo == None:
                raise Exception("Selected Repository is not in your organization!")
    
            # get runs
            totalRunList = repo.get_workflow_runs()
    
            # get a list of all workflows in the repo
            workflowList = repo.get_workflows()
            if workflowList.totalCount == 0:
                print("There are no workflows in your selected repository: '{}'".format(repo.name))
                return
    
            # print list
            print("List of all workflows of your selected repository: '{}'".format(repo.name))
            run_count = 0
            for workflow in workflowList:
                runs = workflow.get_runs()
                run_count = run_count + runs.totalCount
                print(" ID: '{}', Name: '{}', State: '{}': Run(s): '{}'".format(workflow.id, workflow.name, workflow.state, runs.totalCount))
            
            # calculate lost runs
            lost_run_count = totalRunList.totalCount-run_count
            if lost_run_count > 0:
                print(" LOST-RUNS: There are '{}' workflow run(s) without an active yaml-file!".format(lost_run_count))
    
            # if no selection of a workflow is initiated by function-call
            if workflow_selection == None and interactive:
                workflow_selection = input("Please select a workflow by Name (e.g. >CI< or >LOST-RUNS< or press [Return] for all):")
            # if no interactive and no selection is done: choose the first workflow of the list
            elif workflow_selection == None and not interactive:
                print("No workflow chosen.")
    
            # search workflow in list
            found = False
    
            # if no workflow chosen
            if workflow_selection == None or workflow_selection == '':
                found = True
                runList = totalRunList
                print("{} run(s) found.".format(totalRunList.totalCount))
            # in case of lost runs
            elif workflow_selection == 'LOST-RUNS':
                found = True
                runList = []
                for run in totalRunList:
                    run_found = False
                    # check if run has a workflow
                    for workflow in workflowList:
                        if run.workflow_id == workflow.id:
                            run_found = True
                    # if not, add it to the run list
                    if run_found == False:
                        runList.append(run)
                # raise exception if something went wrong
                if len(runList) != lost_run_count:
                    raise Exception("Something went wrong with get lost runs!")
                print("{} run(s) found.".format(lost_run_count))
            # if workflow chosen
            else:
                for workflow in workflowList:
                    if workflow.name == workflow_selection:
                        print("Workflow found in list above: {}".format(workflow_selection))
                        found = True
                        # get runs
                        runList = workflow.get_runs()
                        print("{} run(s) found.".format(runList.totalCount))
                        break
            
            # if workflow found
            if found:
                print("Run List: ")
                for run in runList:
                    #print(" Run Number: '{}', Satus: '{}', Conclusion: '{}', ID: '{}'".format(run.run_number, run.status, run.conclusion, run.id))
                    print(" Run Number: '{}', Satus: '{}', Conclusion: '{}', ID: '{}', Branch: '{}', SHA: '{}'".format(run.run_number, run.status, run.conclusion, run.id, run.head_branch, run.head_sha[0:7]))   
                # get input
                if run_selection == None and interactive:
                    ans = input("Select a special run number (e.g. 123), a list of workflow run numbers seperated by comma (e.g. 123, 111, 112) or type >all<, >all-except-last<, >cancelled<, >failure< or >success<:")
                # choose all if no selection is done an not interaction is used
                elif run_selection == None and not interactive:
                    ans = 'all'
                # choose selection if special run is selected
                else:
                    ans = run_selection
                # manage input: choose selection
                selectedRuns = []
                # special run number is selected
                if ans not in ['all', 'cancelled', 'failure', 'success', 'all-except-last']:
                    ansSplit = ans.split(',')
                    for id in ansSplit:
                        if id.isnumeric():
                            selectedRuns.append(int(id))
                        else:
                            raise Exception("Invalid input '{}'!".format(id))
                else:
                    for run in runList:
                        # delete all runs
                        if ans == 'all':
                            selectedRuns.append(run.run_number)
                        # check conclusion
                        elif ans in ['cancelled', 'failure', 'success'] and run.conclusion == ans:
                            selectedRuns.append(run.run_number)
                        # in case of "all-except-last"
                        elif ans == 'all-except-last' and not run == runList[0]:
                            selectedRuns.append(run.run_number)
    
                # whitelist
                if interactive:
                    ans = input("Do you want to keep runs? Select a special run number (e.g. 123) or a list of workflow run numbers seperated by comma (e.g. 123, 111, 112). For no run keeping press [Return]:")
                    if ans != '':
                        keepRuns = []
                        # convert run slection
                        ansSplit = ans.split(',')
                        for id in ansSplit:
                            keepRuns.append(int(id))
    
                        # step over keep runs
                        for keepRun in keepRuns:
                            # search in list and change List
                            for selectedRun in selectedRuns:
                                if selectedRun == keepRun:
                                    # delete keepRunId from run List (keeping)
                                    selectedRuns.remove(keepRun)
                                    break
    
                # list runs to delete
                if interactive:
                    print("Selected Run List: ")
                    for run in runList:
                        if run.run_number in selectedRuns:
                            #print(" Run Number: '{}', Satus: '{}', Conclusion: '{}', ID: '{}'".format(run.run_number, run.status, run.conclusion, run.id))
                            print(" Run Number: '{}', Satus: '{}', Conclusion: '{}', ID: '{}', Branch: '{}', SHA: '{}'".format(run.run_number, run.status, run.conclusion, run.id, run.head_branch, run.head_sha[0:7]))          
    
                    ans = input("Confirm selection! Yes [default] or No:")
                    if ans in ['y', 'Y', 'yes', 'YES', 'Yes', '']:
                        pass
                    elif ans in ['n', 'N', 'no', 'NO', 'No']:
                        return
                    else:
                        print("Unkown input! Skip.")
                        return
    
                # interate over list and 
                for run in runList:
                    if run.run_number in selectedRuns:
                        # delete completed workflow run
                        if run.status == 'completed':
                            print(" delete run with number: '{}'".format(run.run_number))
                            repo._requester.requestJson("DELETE", run.url)
                        # ask for cancel workflow run
                        else:
                            print("It is not possible to delete run: '{}'. This is not completed.".format(run.run_number))
                            if interactive:
                                ans = input("Do you want to cancel it? Yes [default] or No:")
                                if ans in ['y', 'Y', 'yes', 'YES', 'Yes', '']:
                                    run.cancel()
                                    print("Please rerun cleanup")
                                elif ans in ['n', 'N', 'no', 'NO', 'No']:
                                    print("No cancel selected!")
                                else:
                                    print("Unkown input! Skip.")
            else:
                print("Workflow '{}' not found in list above.".format(workflow_selection))```
    
    
Alert dashboard for Prometheus Alertmanager
Alert dashboard for Prometheus Alertmanager

karma Alert dashboard for Prometheus Alertmanager. Alertmanager >=0.19.0 is required as older versions might not show all receivers in karma, see issu

Dec 30, 2022
Go web monitor - A web monitor with golang

Step Download “go installer” and install on your machine. Open VPN. Go to “web-m

Jan 6, 2022
BRUS - Parses your web server (e.g. nginx) log files and checks with GreyNoise how much noise your website is exposed to.

BRUS bbbbbb rrrrrr u u sssss b b r r u u s bbbbbb rrrrrr u u sssss b b r r u u s bbbbbb r r

May 29, 2022
ProfileStatusSyncer - A tool to synchronize user profile status of Github and Netease CloudMusic

ProfileStatusSyncer A tool to synchronize user profile status of GitHub and Nete

Jul 20, 2022
A simple web service for storing text log files

logpaste A minimalist web service for uploading and sharing log files. Run locally go run main.go Run in local Docker container The Docker container a

Dec 30, 2022
Very powerful server agent for collecting & sending logs & metrics with an easy-to-use web console.
Very powerful server agent for collecting & sending logs & metrics with an easy-to-use web console.

logkit-community 中文版 Introduce Very powerful server agent for collecting & sending logs & metrics with an easy-to-use web console. logkit-community De

Dec 29, 2022
Fast, zero config web endpoint change monitor
Fast, zero config web endpoint change monitor

web monitor fast, zero config web endpoint change monitor. for comparing responses, a selected list of http headers and the full response body is stor

Nov 17, 2022
Port information web scraper written in Go.

Whatport is an open source tool that scrapes port information from SpeedGuide's Port Database Usage whatport [port(s)] (Seperate ports with a space)

Aug 18, 2022
Wlog: Logging System Desgned For Web

Logging System Desgned For Web Clean This is very clean logging system and easy

May 14, 2022
This is a toolKit/template project for web application/server with Gin, packed common services.

gin-toolKit This is a toolKit/template project for web application/server with Gin, packed common services. These services include fasthttp, xrate, lo

May 26, 2022
Test case to fix a bug in github.com/golang/glog

glog Library Pollutes Global Flags List This problem has been reported quite a few times, over the years; see it reported at golang-nuts mailing list,

Feb 10, 2022
ECS task event/log tracer CLI

tracer tracer is a tracing tool for Amazon ECS tasks. tracer shows events and logs of the tasks order by timestamp. example Run a task successfully an

Dec 28, 2022
Log-generator - A simple CLI tool that generates near real logs for testing

Log-generator - A simple CLI tool that generates near real logs for testing

Jan 22, 2022
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.

The open-source platform for monitoring and observability. Grafana allows you to query, visualize, alert on and understand your metrics no matter wher

Jan 3, 2023
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Nov 10, 2022
Simple and configurable Logging in Go, with level, formatters and writers

go-log Logging package similar to log4j for the Golang. Support dynamic log level Support customized formatter TextFormatter JSONFormatter Support mul

Sep 26, 2022
A Go (golang) package providing high-performance asynchronous logging, message filtering by severity and category, and multiple message targets.

ozzo-log Other languages 简体中文 Русский Description ozzo-log is a Go package providing enhanced logging support for Go programs. It has the following fe

Dec 17, 2022
Library and program to parse and forward HAProxy logs

haminer Library and program to parse and forward HAProxy logs. Supported forwarder, Influxdb Requirements Go for building from source code git for dow

Aug 17, 2022
Cloudinsight Agent is a system tool that monitors system processes and services, and sends information back to your Cloudinsight account.

Cloudinsight Agent 中文版 README Cloudinsight Agent is written in Go for collecting metrics from the system it's running on, or from other services, and

Nov 3, 2022