{"id":19107351,"url":"https://github.com/smartcontractkit/push-gha-metrics-action","last_synced_at":"2026-02-21T09:02:49.310Z","repository":{"id":73918267,"uuid":"521014884","full_name":"smartcontractkit/push-gha-metrics-action","owner":"smartcontractkit","description":null,"archived":false,"fork":false,"pushed_at":"2024-05-28T19:40:58.000Z","size":1105,"stargazers_count":7,"open_issues_count":3,"forks_count":2,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-06-30T21:41:43.705Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smartcontractkit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-03T19:57:21.000Z","updated_at":"2024-08-09T17:23:46.000Z","dependencies_parsed_at":"2024-03-04T23:26:12.718Z","dependency_job_id":"651de14c-2eba-407e-ae1b-80c7305f3535","html_url":"https://github.com/smartcontractkit/push-gha-metrics-action","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/smartcontractkit/push-gha-metrics-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fpush-gha-metrics-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fpush-gha-metrics-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fpush-gha-metrics-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fpush-gha-metrics-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smartcontractkit","download_url":"https://codeload.github.com/smartcontractkit/push-gha-metrics-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smartcontractkit%2Fpush-gha-metrics-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29677881,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T06:23:40.028Z","status":"ssl_error","status_checked_at":"2026-02-21T06:23:39.222Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T04:12:15.401Z","updated_at":"2026-02-21T09:02:44.222Z","avatar_url":"https://github.com/smartcontractkit.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# push-gha-metrics-action\n\n**Note:** This is is the public release repository containing mostly build artifacts.\nDevelopment for this action takes place at the [push-gha-metrics-action-source](https://github.com/smartcontractkit/push-gha-metrics-action-source)\nrepository.\n\n# Table of Contents\n\n- [About](#about)\n- [Permissions](#permissions)\n- [Usage](#usage)\n- [Release](#release)\n  - [Update Version](#update-version)\n  - [Update Snapshot](#update-snapshot)\n- [Considerations](#considerations)\n  - [Common Pitfalls](#common-pitfalls)\n    - [Security Concerns](#security-concerns)\n    - [Metrics Storage/Processing/Querying Scaling](#metrics-storageprocessingquerying-scaling)\n    - [Metrics Collection Scaling](#metrics-collection-scaling)\n    - [Metrics Collection Maintenance](#metrics-collection-maintenance)\n    - [Incompatible Technologies Used](#incompatible-technologies-used)\n  - [Projects Considered](#projects-considered)\n- [Notes](#notes)\n  - [Lifecycle of a Workflow Run](#lifecycle-of-a-workflow-run)\n  - [Job Name vs Job Id](#job-name-vs-job-id)\n    - [Matrices](#matrices)\n\n# About\n\nGithub actions' insights for github actions is lacking in easily actionable\ninformation that someone would want if they are looking to optimize their CI/CD\npipelines. The following questions are difficult to answer at a glance using the\ntools that Github UI provides you, whereas the goal of this action is to\ngradually answer them, starting with the highest impact ones first.\n\n| Question                                               | Over Time               | Over Commits, Pull Requests, Releases, Etc | Within A Repository     | Across All Repositories in an Organization |\n| ------------------------------------------------------ | ----------------------- | ------------------------------------------ | ----------------------- | ------------------------------------------ |\n| Which workflows are taking up the most time?           | :heavy_check_mark:      | :heavy_check_mark:                         | :heavy_check_mark:      | :heavy_check_mark:                         |\n| Which job takes up the most time?                      | :heavy_check_mark:      | :heavy_check_mark:                         | :heavy_check_mark:      | :heavy_check_mark:                         |\n| Which repository is responsible for the most run time? | :heavy_check_mark:      | :heavy_check_mark:                         | :heavy_check_mark:      | :heavy_check_mark:                         |\n| How often does a workflow fail?                        | :x:                     | :x:                                        | :x:                     | :x:                                        |\n| How often does a job fail?                             | :x:                     | :x:                                        | :x:                     | :x:                                        |\n| How long is a job queued for?                          | :building_construction: | :building_construction:                    | :building_construction: | :building_construction:                    |\n| How long is a workflow queued for?                     | :building_construction: | :building_construction:                    | :building_construction: | :building_construction:                    |\n| How many jobs are currently being executed?            | :building_construction: | :building_construction:                    | :building_construction: | :building_construction:                    |\n\nWe want to answer these questions so we are able to identify bottlenecks in\ndeveloper productivity and prioritize them using quantitative analysis.\n\nThis action creates logs that contain information about the currently executing\ngithub action's job, then pushes them to a Loki endpoint for metrics processing.\nFrom there, we visualize these metrics within Grafana. This enables us to answer\nthe aforementioned questions.\n\n```mermaid\nsequenceDiagram\n  participant L as Loki\n  participant F as Grafana\n  participant A as Metrics Action\n  participant J1 as Workflow Run A: Job Run B\n  participant G as Github\n  G--\u003e+J1: Start Job Run B\n    J1-\u003e\u003eJ1: Executes Step 1\n    Note Over J1,A: Let step \"K\" be the step that contains this action\n    J1-\u003e\u003eA:  Executes Step K\n    A-\u003e\u003eA: Detects that it's not in \"post-step\" phase, no-ops\n    J1-\u003e\u003eJ1: Executes Step N\n    J1-\u003e\u003eJ1: Executes Post-Step N\n    J1-\u003e\u003eA: Executes Post-Step K\n    A-\u003e\u003eA: Detects that it is in \"post-step phase\"\n    rect rgb(200, 150, 255)\n      Note Over G, A: Local and remote metadata collection\n      A-\u003e\u003eA: Take current timestamp\n      A-\u003e\u003eJ1: Get local job metadata on runner\n      A-\u003e\u003eG: Request remote job metadata\n      G-\u003e\u003eA: Return remote job metadata\n      A-\u003e\u003eA: Merge metadata\n      A-\u003e\u003eA: Calculate execution duration\n    end\n    A-\u003e\u003eL: Push calculated metrics as Loki Logs\n    J1-\u003e\u003eJ1: Executes Post-Step 1\n  J1--\u003e-G: Finish Job Run B\n\n    L-\u003e\u003eL: Store calculated metrics\n    F-\u003e\u003eL: Send metrics query\n    L-\u003e\u003eL: Perform metrics query\n    L-\u003e\u003eF: Send query results\n    F-\u003e\u003eF: Visualize metrics\n\n```\n\n# Permissions\n\nThis action requires the following permissions:\n\n```yaml\npermissions:\n  actions: read\n```\n\nMake sure you explicitly grant this permission to the github action's token if\nyou have the `permissions` key set in your workflow.\n\n# Usage\n\nYou should have this action being used as the **first** step in every single job\nof each workflow you'd like to collect metrics about. **Every job name in your\nworkflow must be unique**, see `Job Name vs Id` and `Matrices` in the `Notes`\nsection.\n\nSee the [action.yml](action.yml) file for information on inputs that this action\naccepts.\n\n# Release\n\nThis project is synced through a release pipeline in the form of Github workflows.\nThere exist two release jobs, one for official releases, and one for snapshot\nreleases. Both jobs execute a shell script as seen in [sync-from-source](sync-from-source).\n\n## Update Version\n\nThis job runs once every hour. If a new release exists, it will pull the changes from\nthe source repository. Released changes will be pushed to the `chore/update-push-gha-metrics-action`\nbranch with a PR titled \"Update push-gha-metrics-action\" targeting the `main` branch.\n\n## Update Snapshot\n\nThis job runs once every hour. This workflow will synchronize the source repository\nand the `snapshot` branch. This branch should be used for testing purposes.\n\n# Considerations\n\n## Common Pitfalls\n\nMost of the following considerations were not used due to certain architectural\ndecisions made which would cause friction either due to:\n\n### Security Concerns\n\n- Requiring PAT tokens for monitoring a repository\n- For multi-repository monitoring over an org, requiring a PAT token with org\n  level access\n\n### Metrics Storage/Processing/Querying Scaling\n\n- Creating labels with unbounded cardinality, which can cause severe degradation\n  of the metrics processing service\n\n### Metrics Collection Scaling\n\n- Collecting metrics by mass querying the Github HTTP API, or large queries\n  against Github's GraphQL api, both resulting in rate limiting\n- Collecting workflow + job metrics by having metrics collection done by a\n  workflow that triggered from other workflows being completed. This results in\n  doubling the amount of jobs being executed in a repository, making it very\n  expensive.\n\n### Metrics Collection Maintenance\n\n- Collecting workflow + job metrics by having a standalone service, this results\n  in having to maintain a long-lived service along with protecting any secrets\n  it needs to access Github's API.\n\n### Incompatible Technologies Used\n\n- Metrics collection / querying / vis revolving around a tech stack that isn't\n  Prometheus / Loki / Grafana. Adopting another tech would make the user\n  experience loaded with friction rather than using what we already have.\n\n## Projects Considered\n\n- https://github.com/tchelovilar/github-org-runner-exporter\n- https://github.com/transferwise/github-actions-api-exporter\n- https://github.com/Spendesk/github-actions-exporter\n- https://github.com/marketplace/actions/datadog-actions-metrics\n- https://github.com/kaidotdev/github-actions-exporter\n- https://github.com/cpanato/github_actions_exporter\n- https://docs.datadoghq.com/continuous_integration/setup_pipelines/github/\n\n# Notes\n\n## Lifecycle of a Workflow Run\n\nGiven the following workflow, a simplified sequence diagram is provided that\nshows us at what points github is updated with various timestamps. With these\ntimestamps we can calculate useful durations as metrics.\n\n```yaml\nname: A\non:\n  workflow_dispatch:\n  push:\n  pull_request:\n\njobs:\n  B:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: ./.github/actions/push-metrics/\n        with:\n          basic-auth: ${{ secrets.GRAFANA_INTERNAL_BASIC_AUTH }}\n          hostname: ${{ secrets.GRAFANA_INTERNAL_HOST }}\n          org-id: ${{ secrets.GRAFANA_INTERNAL_TENANT_ID}}\n          this-job-name: B\n\n      - run: echo \"test test test\"\n\n  C:\n    needs: [B]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n\n      - uses: ./.github/actions/push-metrics/\n        with:\n          basic-auth: ${{ secrets.GRAFANA_INTERNAL_BASIC_AUTH }}\n          hostname: ${{ secrets.GRAFANA_INTERNAL_HOST }}\n          org-id: ${{ secrets.GRAFANA_INTERNAL_TENANT_ID}}\n          this-job-name: C\n\n      - run: echo \"test test test\"\n```\n\n```mermaid\nsequenceDiagram\n  participant W as Workflow Run A\n  participant G as Github API\n  participant J1 as Workflow Run A: Job Run B\n  participant J2 as Workflow Run A: Job Run C\n\n\n  rect rgb(160,638,134)\n    Note Over G,W: Workflow A Initialization Phase\n    G-\u003e\u003eG: Trigger for Workflow A Occurs\n    G-\u003e\u003eW: Create Workflow Run A\n    W-\u003e\u003eG: Update Workflow Run A's \"created_at\" property\n    par Queueing of Workflow Run A and its Job Constituents\n    G--\u003e+W: Queue Workflow Run A\n    G--\u003e+J1: Queue Job Run B\n    G--\u003e+J2: Queue Job Run C\n    end\n    G-\u003e\u003eG: Waits for Available Runner\n    G-\u003e\u003eG: Gets Available Runner\n    W--\u003e-G: De-queue Workflow Run A\n  end\n  G--\u003e+W: Start Workflow Run A\n\n  W-\u003e\u003eG: Update Workflow Run A's \"updated_at\" + \"run_started_at\" property\n\n  Note over J2,G: Job B has no Job dependencies, Job C Depends on Job B\n  G-\u003e\u003eG: Selects jobs with no dependencies for workflow run A\n\n  J1--\u003e-G: De-queue Job Run B\n  G--\u003e+J1: Start Job Run B\n  rect rgb(191, 223, 255)\n    Note over J1: Job Run B Execution Phase\n    W-\u003e\u003eG: Update Workflow Run A's \"updated_at\" property\n    J1-\u003e\u003eG: Update Job Run B's \"started_at\" property\n    J1--\u003eJ1: Executes Step 1\n    J1--\u003eJ1: Executes Step N\n    J1--\u003eJ1: Executes Post-Step N\n    J1--\u003eJ1: Executes Post-Step 1\n    J1-\u003e\u003eG: Update Job Run B's \"completed_at\" property\n    W-\u003e\u003eG: Update Workflow Run A's \"updated_at\" property\n  end\n  J1--\u003e-G: Finish Job Run B\n\n  J2--\u003e-G: De-queue Job Run C\n    G--\u003e+J2: Start Job Run C\n  rect rgb(200, 150, 255)\n    Note over J2: Job run C Execution Phase\n    W-\u003e\u003eG: Update Workflow Run A's \"updated_at\" property\n    J2-\u003e\u003eG: Update Job Run C's \"started_at\" property\n    J2--\u003eJ2: Executes Step 1\n    J2--\u003eJ2: Executes Step N\n    J2--\u003eJ2: Executes Post-Step N\n    J2--\u003eJ2: Executes Post-Step 1\n    J2-\u003e\u003eG: Update Job Run C's \"completed_at\" property\n    W-\u003e\u003eG: Update Workflow Run A's \"updated_at\" property\n  end\n  J2--\u003e-G: Finish Job Run C\n\n  W-\u003e\u003eG: Update Workflow Run A's \"updated_at\" property\n  W--\u003e-G: Finish Workflow Run A\n```\n\n## Job Name vs Job Id\n\nWe gather additional context on the currently running job by querying the\n[List Jobs for a Workflow Run Attempt endpoint](https://docs.github.com/en/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run-attempt),\nthen filtering the returned jobs by their job name to find the currently\nexecuting one.\n\nUnfortunately, the github runner only reports the\n[job id](https://github.com/actions/runner/issues/852), not the `job name` that\nwe need to find our job by. If you have a `job[job_id].name` specified, you\n_must_ specify the `this-job-name` parameter to this action, see the below\n**Matrices** section for more info. **Also**, you must **not** specify the same\njob name for two different job id's, otherwise the same situation will occur:\nthe metrics collection action will fail to properly resolve the correct job.\n\n### Matrices\n\nThe name of the current job being executed given by the current runner context\ndoes not align with the name of the job reported by the API. Even when names of\nthe job are unique from dynamic job names, there is a discrepancy.\n\nThis is due to the Github runner reporting the currently executing job by its\n`key` within the workflow file, while the Github API reports the currently\nexecuting job by its `name` value, with the `key` value being the fallback if\nthe `name` isn't defined.\n\nWhat this means is that this action cannot discover its own job state from the\ngiven context, if the job being executed is part of a matrix. An input value\nthat gives the proper job name to lookup by is needed during matrix execution.\n\n```yaml\njobs:\n  my-parallel-job:\n    # This generates a unique name within the matrix itself. Now we need to pass this value to this action so it can do a successful lookup\n    name: my-unique-name-${{ matrix.key1 }}\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        key1: [\"unique\", \"values\"]\n    steps:\n      - uses: push-metrics\n        with:\n          # Now we use the same name here, so our metrics collection works properly\n          this-job-name: my-unique-name-${{ matrix.key1 }}\n```\n\nFor more information, see: https://github.com/actions/runner/issues/852\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmartcontractkit%2Fpush-gha-metrics-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmartcontractkit%2Fpush-gha-metrics-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmartcontractkit%2Fpush-gha-metrics-action/lists"}